summaryrefslogtreecommitdiffstats
path: root/gfx/layers/ipc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/ipc
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/ipc')
-rw-r--r--gfx/layers/ipc/APZCTreeManagerChild.cpp181
-rw-r--r--gfx/layers/ipc/APZCTreeManagerChild.h99
-rw-r--r--gfx/layers/ipc/APZCTreeManagerParent.cpp185
-rw-r--r--gfx/layers/ipc/APZCTreeManagerParent.h78
-rw-r--r--gfx/layers/ipc/APZChild.cpp105
-rw-r--r--gfx/layers/ipc/APZChild.h69
-rw-r--r--gfx/layers/ipc/APZInputBridgeChild.cpp124
-rw-r--r--gfx/layers/ipc/APZInputBridgeChild.h44
-rw-r--r--gfx/layers/ipc/APZInputBridgeParent.cpp128
-rw-r--r--gfx/layers/ipc/APZInputBridgeParent.h71
-rw-r--r--gfx/layers/ipc/CanvasChild.cpp325
-rw-r--r--gfx/layers/ipc/CanvasChild.h151
-rw-r--r--gfx/layers/ipc/CanvasThread.cpp138
-rw-r--r--gfx/layers/ipc/CanvasThread.h97
-rw-r--r--gfx/layers/ipc/CanvasTranslator.cpp534
-rw-r--r--gfx/layers/ipc/CanvasTranslator.h284
-rw-r--r--gfx/layers/ipc/CompositableForwarder.cpp15
-rw-r--r--gfx/layers/ipc/CompositableForwarder.h125
-rw-r--r--gfx/layers/ipc/CompositableTransactionParent.cpp337
-rw-r--r--gfx/layers/ipc/CompositableTransactionParent.h65
-rw-r--r--gfx/layers/ipc/CompositorBench.cpp352
-rw-r--r--gfx/layers/ipc/CompositorBench.h30
-rw-r--r--gfx/layers/ipc/CompositorBridgeChild.cpp1310
-rw-r--r--gfx/layers/ipc/CompositorBridgeChild.h427
-rw-r--r--gfx/layers/ipc/CompositorBridgeParent.cpp2990
-rw-r--r--gfx/layers/ipc/CompositorBridgeParent.h904
-rw-r--r--gfx/layers/ipc/CompositorManagerChild.cpp256
-rw-r--r--gfx/layers/ipc/CompositorManagerChild.h109
-rw-r--r--gfx/layers/ipc/CompositorManagerParent.cpp335
-rw-r--r--gfx/layers/ipc/CompositorManagerParent.h88
-rw-r--r--gfx/layers/ipc/CompositorThread.cpp159
-rw-r--r--gfx/layers/ipc/CompositorThread.h62
-rw-r--r--gfx/layers/ipc/CompositorVsyncScheduler.cpp365
-rw-r--r--gfx/layers/ipc/CompositorVsyncScheduler.h173
-rw-r--r--gfx/layers/ipc/CompositorVsyncSchedulerOwner.h32
-rw-r--r--gfx/layers/ipc/ContentCompositorBridgeParent.cpp753
-rw-r--r--gfx/layers/ipc/ContentCompositorBridgeParent.h246
-rw-r--r--gfx/layers/ipc/ISurfaceAllocator.cpp239
-rw-r--r--gfx/layers/ipc/ISurfaceAllocator.h287
-rw-r--r--gfx/layers/ipc/ImageBridgeChild.cpp995
-rw-r--r--gfx/layers/ipc/ImageBridgeChild.h390
-rw-r--r--gfx/layers/ipc/ImageBridgeParent.cpp796
-rw-r--r--gfx/layers/ipc/ImageBridgeParent.h187
-rw-r--r--gfx/layers/ipc/KnowsCompositor.h237
-rw-r--r--gfx/layers/ipc/LayerAnimationUtils.cpp40
-rw-r--r--gfx/layers/ipc/LayerAnimationUtils.h29
-rw-r--r--gfx/layers/ipc/LayerTransactionChild.cpp37
-rw-r--r--gfx/layers/ipc/LayerTransactionChild.h74
-rw-r--r--gfx/layers/ipc/LayerTransactionParent.cpp1020
-rw-r--r--gfx/layers/ipc/LayerTransactionParent.h236
-rw-r--r--gfx/layers/ipc/LayerTreeOwnerTracker.cpp68
-rw-r--r--gfx/layers/ipc/LayerTreeOwnerTracker.h73
-rw-r--r--gfx/layers/ipc/LayersMessageUtils.h980
-rw-r--r--gfx/layers/ipc/LayersMessages.ipdlh573
-rw-r--r--gfx/layers/ipc/LayersSurfaces.ipdlh204
-rw-r--r--gfx/layers/ipc/PAPZ.ipdl78
-rw-r--r--gfx/layers/ipc/PAPZCTreeManager.ipdl86
-rw-r--r--gfx/layers/ipc/PAPZInputBridge.ipdl82
-rw-r--r--gfx/layers/ipc/PCanvas.ipdl50
-rw-r--r--gfx/layers/ipc/PCompositorBridge.ipdl310
-rw-r--r--gfx/layers/ipc/PCompositorBridgeTypes.ipdlh20
-rw-r--r--gfx/layers/ipc/PCompositorManager.ipdl92
-rw-r--r--gfx/layers/ipc/PImageBridge.ipdl71
-rw-r--r--gfx/layers/ipc/PLayerTransaction.ipdl130
-rw-r--r--gfx/layers/ipc/PTexture.ipdl40
-rw-r--r--gfx/layers/ipc/PUiCompositorController.ipdl46
-rw-r--r--gfx/layers/ipc/PVideoBridge.ipdl33
-rw-r--r--gfx/layers/ipc/PWebRenderBridge.ipdl106
-rw-r--r--gfx/layers/ipc/RefCountedShmem.cpp94
-rw-r--r--gfx/layers/ipc/RefCountedShmem.h48
-rw-r--r--gfx/layers/ipc/RemoteContentController.cpp425
-rw-r--r--gfx/layers/ipc/RemoteContentController.h115
-rw-r--r--gfx/layers/ipc/ShadowLayerUtils.h42
-rw-r--r--gfx/layers/ipc/ShadowLayerUtilsMac.cpp31
-rw-r--r--gfx/layers/ipc/ShadowLayerUtilsX11.cpp153
-rw-r--r--gfx/layers/ipc/ShadowLayerUtilsX11.h28
-rw-r--r--gfx/layers/ipc/ShadowLayers.cpp1074
-rw-r--r--gfx/layers/ipc/ShadowLayers.h481
-rw-r--r--gfx/layers/ipc/SharedPlanarYCbCrImage.cpp179
-rw-r--r--gfx/layers/ipc/SharedPlanarYCbCrImage.h63
-rw-r--r--gfx/layers/ipc/SharedRGBImage.cpp156
-rw-r--r--gfx/layers/ipc/SharedRGBImage.h61
-rw-r--r--gfx/layers/ipc/SharedSurfacesChild.cpp657
-rw-r--r--gfx/layers/ipc/SharedSurfacesChild.h267
-rw-r--r--gfx/layers/ipc/SharedSurfacesMemoryReport.h60
-rw-r--r--gfx/layers/ipc/SharedSurfacesParent.cpp257
-rw-r--r--gfx/layers/ipc/SharedSurfacesParent.h82
-rw-r--r--gfx/layers/ipc/SurfaceDescriptor.h71
-rw-r--r--gfx/layers/ipc/SynchronousTask.h55
-rw-r--r--gfx/layers/ipc/TextureForwarder.h91
-rw-r--r--gfx/layers/ipc/UiCompositorControllerChild.cpp322
-rw-r--r--gfx/layers/ipc/UiCompositorControllerChild.h87
-rw-r--r--gfx/layers/ipc/UiCompositorControllerMessageTypes.h32
-rw-r--r--gfx/layers/ipc/UiCompositorControllerParent.cpp305
-rw-r--r--gfx/layers/ipc/UiCompositorControllerParent.h84
-rw-r--r--gfx/layers/ipc/VideoBridgeChild.cpp184
-rw-r--r--gfx/layers/ipc/VideoBridgeChild.h95
-rw-r--r--gfx/layers/ipc/VideoBridgeParent.cpp155
-rw-r--r--gfx/layers/ipc/VideoBridgeParent.h82
-rw-r--r--gfx/layers/ipc/VideoBridgeUtils.h37
-rw-r--r--gfx/layers/ipc/WebRenderMessages.ipdlh213
-rw-r--r--gfx/layers/ipc/fuzztest/compositor_manager_parent_ipc_libfuzz.cpp37
-rw-r--r--gfx/layers/ipc/fuzztest/moz.build16
103 files changed, 25494 insertions, 0 deletions
diff --git a/gfx/layers/ipc/APZCTreeManagerChild.cpp b/gfx/layers/ipc/APZCTreeManagerChild.cpp
new file mode 100644
index 0000000000..b9fc37efb9
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZCTreeManagerChild.h"
+
+#include "InputData.h" // for InputData
+#include "mozilla/dom/BrowserParent.h" // for BrowserParent
+#include "mozilla/layers/APZCCallbackHelper.h" // for APZCCallbackHelper
+#include "mozilla/layers/APZInputBridgeChild.h" // for APZInputBridgeChild
+#include "mozilla/layers/GeckoContentController.h" // for GeckoContentController
+#include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession
+
+namespace mozilla {
+namespace layers {
+
+APZCTreeManagerChild::APZCTreeManagerChild()
+ : mCompositorSession(nullptr), mIPCOpen(false) {}
+
+APZCTreeManagerChild::~APZCTreeManagerChild() = default;
+
+void APZCTreeManagerChild::SetCompositorSession(
+ RemoteCompositorSession* aSession) {
+ // Exactly one of mCompositorSession and aSession must be null (i.e. either
+ // we're setting mCompositorSession or we're clearing it).
+ MOZ_ASSERT(!mCompositorSession ^ !aSession);
+ mCompositorSession = aSession;
+}
+
+void APZCTreeManagerChild::SetInputBridge(APZInputBridgeChild* aInputBridge) {
+ // The input bridge only exists from the UI process to the GPU process.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(!mInputBridge);
+
+ mInputBridge = aInputBridge;
+}
+
+void APZCTreeManagerChild::Destroy() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mInputBridge) {
+ mInputBridge->Destroy();
+ mInputBridge = nullptr;
+ }
+}
+
+void APZCTreeManagerChild::SetKeyboardMap(const KeyboardMap& aKeyboardMap) {
+ SendSetKeyboardMap(aKeyboardMap);
+}
+
+void APZCTreeManagerChild::ZoomToRect(const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t aFlags) {
+ SendZoomToRect(aGuid, aRect, aFlags);
+}
+
+void APZCTreeManagerChild::ContentReceivedInputBlock(uint64_t aInputBlockId,
+ bool aPreventDefault) {
+ SendContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+}
+
+void APZCTreeManagerChild::SetTargetAPZC(
+ uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) {
+ SendSetTargetAPZC(aInputBlockId, aTargets);
+}
+
+void APZCTreeManagerChild::UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) {
+ if (mIPCOpen) {
+ SendUpdateZoomConstraints(aGuid, aConstraints);
+ }
+}
+
+void APZCTreeManagerChild::SetDPI(float aDpiValue) { SendSetDPI(aDpiValue); }
+
+void APZCTreeManagerChild::SetAllowedTouchBehavior(
+ uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) {
+ SendSetAllowedTouchBehavior(aInputBlockId, aValues);
+}
+
+void APZCTreeManagerChild::StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) {
+ SendStartScrollbarDrag(aGuid, aDragMetrics);
+}
+
+bool APZCTreeManagerChild::StartAutoscroll(const ScrollableLayerGuid& aGuid,
+ const ScreenPoint& aAnchorLocation) {
+ return SendStartAutoscroll(aGuid, aAnchorLocation);
+}
+
+void APZCTreeManagerChild::StopAutoscroll(const ScrollableLayerGuid& aGuid) {
+ SendStopAutoscroll(aGuid);
+}
+
+void APZCTreeManagerChild::SetLongTapEnabled(bool aTapGestureEnabled) {
+ SendSetLongTapEnabled(aTapGestureEnabled);
+}
+
+APZInputBridge* APZCTreeManagerChild::InputBridge() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(mInputBridge);
+
+ return mInputBridge.get();
+}
+
+void APZCTreeManagerChild::AddInputBlockCallback(
+ uint64_t aInputBlockId, InputBlockCallback&& aCallback) {
+ MOZ_RELEASE_ASSERT(false,
+ "Remoting of input block callbacks is not implemented");
+}
+
+void APZCTreeManagerChild::AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+}
+
+void APZCTreeManagerChild::ReleaseIPDLReference() {
+ mIPCOpen = false;
+ Release();
+}
+
+void APZCTreeManagerChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mIPCOpen = false;
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerChild::RecvHandleTap(
+ const TapType& aType, const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (mCompositorSession &&
+ mCompositorSession->RootLayerTreeId() == aGuid.mLayersId &&
+ mCompositorSession->GetContentController()) {
+ RefPtr<GeckoContentController> controller =
+ mCompositorSession->GetContentController();
+ controller->HandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId);
+ return IPC_OK();
+ }
+ dom::BrowserParent* tab =
+ dom::BrowserParent::GetBrowserParentFromLayersId(aGuid.mLayersId);
+ if (tab) {
+ tab->SendHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerChild::RecvNotifyPinchGesture(
+ const PinchGestureType& aType, const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint, const LayoutDeviceCoord& aSpanChange,
+ const Modifiers& aModifiers) {
+ // This will only get sent from the GPU process to the parent process, so
+ // this function should never get called in the content process.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We want to handle it in this process regardless of what the target guid
+ // of the pinch is. This may change in the future.
+ if (mCompositorSession && mCompositorSession->GetWidget()) {
+ APZCCallbackHelper::NotifyPinchGesture(aType, aFocusPoint, aSpanChange,
+ aModifiers,
+ mCompositorSession->GetWidget());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerChild::RecvCancelAutoscroll(
+ const ScrollableLayerGuid::ViewID& aScrollId) {
+ // This will only get sent from the GPU process to the parent process, so
+ // this function should never get called in the content process.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ APZCCallbackHelper::CancelAutoscroll(aScrollId);
+ return IPC_OK();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZCTreeManagerChild.h b/gfx/layers/ipc/APZCTreeManagerChild.h
new file mode 100644
index 0000000000..dcba794bfa
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerChild.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerChild_h
+#define mozilla_layers_APZCTreeManagerChild_h
+
+#include "mozilla/layers/APZInputBridge.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/PAPZCTreeManagerChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class APZInputBridgeChild;
+class RemoteCompositorSession;
+
+class APZCTreeManagerChild : public IAPZCTreeManager,
+ public PAPZCTreeManagerChild {
+ friend class PAPZCTreeManagerChild;
+ using TapType = GeckoContentController_TapType;
+
+ public:
+ APZCTreeManagerChild();
+
+ void SetCompositorSession(RemoteCompositorSession* aSession);
+ void SetInputBridge(APZInputBridgeChild* aInputBridge);
+ void Destroy();
+
+ void SetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
+
+ void ZoomToRect(const ScrollableLayerGuid& aGuid, const CSSRect& aRect,
+ const uint32_t aFlags = DEFAULT_BEHAVIOR) override;
+
+ void ContentReceivedInputBlock(uint64_t aInputBlockId,
+ bool aPreventDefault) override;
+
+ void SetTargetAPZC(uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+
+ void UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) override;
+
+ void SetDPI(float aDpiValue) override;
+
+ void SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues) override;
+
+ void StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) override;
+
+ bool StartAutoscroll(const ScrollableLayerGuid& aGuid,
+ const ScreenPoint& aAnchorLocation) override;
+
+ void StopAutoscroll(const ScrollableLayerGuid& aGuid) override;
+
+ void SetLongTapEnabled(bool aTapGestureEnabled) override;
+
+ APZInputBridge* InputBridge() override;
+
+ void AddInputBlockCallback(uint64_t aInputBlockId,
+ InputBlockCallback&& aCallback) override;
+
+ void AddIPDLReference();
+ void ReleaseIPDLReference();
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ protected:
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvHandleTap(const TapType& aType,
+ const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ mozilla::ipc::IPCResult RecvNotifyPinchGesture(
+ const PinchGestureType& aType, const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint,
+ const LayoutDeviceCoord& aSpanChange, const Modifiers& aModifiers);
+
+ mozilla::ipc::IPCResult RecvCancelAutoscroll(
+ const ScrollableLayerGuid::ViewID& aScrollId);
+
+ virtual ~APZCTreeManagerChild();
+
+ private:
+ MOZ_NON_OWNING_REF RemoteCompositorSession* mCompositorSession;
+ RefPtr<APZInputBridgeChild> mInputBridge;
+ bool mIPCOpen;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZCTreeManagerChild_h
diff --git a/gfx/layers/ipc/APZCTreeManagerParent.cpp b/gfx/layers/ipc/APZCTreeManagerParent.cpp
new file mode 100644
index 0000000000..1dcd3fc275
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZCTreeManagerParent.h"
+
+#include "apz/src/APZCTreeManager.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layers/APZUpdater.h"
+
+namespace mozilla {
+namespace layers {
+
+APZCTreeManagerParent::APZCTreeManagerParent(
+ LayersId aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager,
+ RefPtr<APZUpdater> aAPZUpdater)
+ : mLayersId(aLayersId),
+ mTreeManager(std::move(aAPZCTreeManager)),
+ mUpdater(std::move(aAPZUpdater)) {
+ MOZ_ASSERT(mTreeManager != nullptr);
+ MOZ_ASSERT(mUpdater != nullptr);
+ MOZ_ASSERT(mUpdater->HasTreeManager(mTreeManager));
+}
+
+APZCTreeManagerParent::~APZCTreeManagerParent() = default;
+
+void APZCTreeManagerParent::ChildAdopted(
+ RefPtr<APZCTreeManager> aAPZCTreeManager, RefPtr<APZUpdater> aAPZUpdater) {
+ MOZ_ASSERT(aAPZCTreeManager != nullptr);
+ MOZ_ASSERT(aAPZUpdater != nullptr);
+ MOZ_ASSERT(aAPZUpdater->HasTreeManager(aAPZCTreeManager));
+ mTreeManager = std::move(aAPZCTreeManager);
+ mUpdater = std::move(aAPZUpdater);
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetKeyboardMap(
+ const KeyboardMap& aKeyboardMap) {
+ mUpdater->RunOnControllerThread(
+ mLayersId, NewRunnableMethod<KeyboardMap>(
+ "layers::IAPZCTreeManager::SetKeyboardMap", mTreeManager,
+ &IAPZCTreeManager::SetKeyboardMap, aKeyboardMap));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvZoomToRect(
+ const ScrollableLayerGuid& aGuid, const CSSRect& aRect,
+ const uint32_t& aFlags) {
+ if (!IsGuidValid(aGuid)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mUpdater->RunOnControllerThread(
+ aGuid.mLayersId,
+ NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
+ "layers::IAPZCTreeManager::ZoomToRect", mTreeManager,
+ &IAPZCTreeManager::ZoomToRect, aGuid, aRect, aFlags));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvContentReceivedInputBlock(
+ const uint64_t& aInputBlockId, const bool& aPreventDefault) {
+ mUpdater->RunOnControllerThread(
+ mLayersId, NewRunnableMethod<uint64_t, bool>(
+ "layers::IAPZCTreeManager::ContentReceivedInputBlock",
+ mTreeManager, &IAPZCTreeManager::ContentReceivedInputBlock,
+ aInputBlockId, aPreventDefault));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetTargetAPZC(
+ const uint64_t& aInputBlockId, nsTArray<ScrollableLayerGuid>&& aTargets) {
+ mUpdater->RunOnControllerThread(
+ mLayersId,
+ NewRunnableMethod<uint64_t,
+ StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
+ "layers::IAPZCTreeManager::SetTargetAPZC", mTreeManager,
+ &IAPZCTreeManager::SetTargetAPZC, aInputBlockId,
+ std::move(aTargets)));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvUpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const MaybeZoomConstraints& aConstraints) {
+ if (!IsGuidValid(aGuid)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mTreeManager->UpdateZoomConstraints(aGuid, aConstraints);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetDPI(
+ const float& aDpiValue) {
+ mUpdater->RunOnControllerThread(
+ mLayersId,
+ NewRunnableMethod<float>("layers::IAPZCTreeManager::SetDPI", mTreeManager,
+ &IAPZCTreeManager::SetDPI, aDpiValue));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetAllowedTouchBehavior(
+ const uint64_t& aInputBlockId, nsTArray<TouchBehaviorFlags>&& aValues) {
+ mUpdater->RunOnControllerThread(
+ mLayersId,
+ NewRunnableMethod<uint64_t,
+ StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>(
+ "layers::IAPZCTreeManager::SetAllowedTouchBehavior", mTreeManager,
+ &IAPZCTreeManager::SetAllowedTouchBehavior, aInputBlockId,
+ std::move(aValues)));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) {
+ if (!IsGuidValid(aGuid)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mUpdater->RunOnControllerThread(
+ aGuid.mLayersId,
+ NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
+ "layers::IAPZCTreeManager::StartScrollbarDrag", mTreeManager,
+ &IAPZCTreeManager::StartScrollbarDrag, aGuid, aDragMetrics));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartAutoscroll(
+ const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation) {
+ // Unlike RecvStartScrollbarDrag(), this message comes from the parent
+ // process (via nsBaseWidget::mAPZC) rather than from the child process
+ // (via BrowserChild::mApzcTreeManager), so there is no need to check the
+ // layers id against mLayersId (and in any case, it wouldn't match, because
+ // mLayersId stores the parent process's layers id, while nsBaseWidget is
+ // sending the child process's layers id).
+
+ mUpdater->RunOnControllerThread(
+ mLayersId,
+ NewRunnableMethod<ScrollableLayerGuid, ScreenPoint>(
+ "layers::IAPZCTreeManager::StartAutoscroll", mTreeManager,
+ &IAPZCTreeManager::StartAutoscroll, aGuid, aAnchorLocation));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStopAutoscroll(
+ const ScrollableLayerGuid& aGuid) {
+ // See RecvStartAutoscroll() for why we don't check the layers id.
+
+ mUpdater->RunOnControllerThread(
+ mLayersId, NewRunnableMethod<ScrollableLayerGuid>(
+ "layers::IAPZCTreeManager::StopAutoscroll", mTreeManager,
+ &IAPZCTreeManager::StopAutoscroll, aGuid));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetLongTapEnabled(
+ const bool& aLongTapEnabled) {
+ mUpdater->RunOnControllerThread(
+ mLayersId,
+ NewRunnableMethod<bool>(
+ "layers::IAPZCTreeManager::SetLongTapEnabled", mTreeManager,
+ &IAPZCTreeManager::SetLongTapEnabled, aLongTapEnabled));
+
+ return IPC_OK();
+}
+
+bool APZCTreeManagerParent::IsGuidValid(const ScrollableLayerGuid& aGuid) {
+ if (aGuid.mLayersId != mLayersId) {
+ NS_ERROR("Unexpected layers id");
+ return false;
+ }
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZCTreeManagerParent.h b/gfx/layers/ipc/APZCTreeManagerParent.h
new file mode 100644
index 0000000000..de54d4dbfd
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerParent.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerParent_h
+#define mozilla_layers_APZCTreeManagerParent_h
+
+#include "mozilla/layers/PAPZCTreeManagerParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class APZCTreeManager;
+class APZUpdater;
+
+class APZCTreeManagerParent : public PAPZCTreeManagerParent {
+ public:
+ APZCTreeManagerParent(LayersId aLayersId,
+ RefPtr<APZCTreeManager> aAPZCTreeManager,
+ RefPtr<APZUpdater> mAPZUpdater);
+ virtual ~APZCTreeManagerParent();
+
+ LayersId GetLayersId() const { return mLayersId; }
+
+ /**
+ * Called when the layer tree that this protocol is connected to
+ * is adopted by another compositor, and we need to switch APZCTreeManagers.
+ */
+ void ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager,
+ RefPtr<APZUpdater> aAPZUpdater);
+
+ mozilla::ipc::IPCResult RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap);
+
+ mozilla::ipc::IPCResult RecvZoomToRect(const ScrollableLayerGuid& aGuid,
+ const CSSRect& aRect,
+ const uint32_t& aFlags);
+
+ mozilla::ipc::IPCResult RecvContentReceivedInputBlock(
+ const uint64_t& aInputBlockId, const bool& aPreventDefault);
+
+ mozilla::ipc::IPCResult RecvSetTargetAPZC(
+ const uint64_t& aInputBlockId, nsTArray<ScrollableLayerGuid>&& aTargets);
+
+ mozilla::ipc::IPCResult RecvUpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const MaybeZoomConstraints& aConstraints);
+
+ mozilla::ipc::IPCResult RecvSetDPI(const float& aDpiValue);
+
+ mozilla::ipc::IPCResult RecvSetAllowedTouchBehavior(
+ const uint64_t& aInputBlockId, nsTArray<TouchBehaviorFlags>&& aValues);
+
+ mozilla::ipc::IPCResult RecvStartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics);
+
+ mozilla::ipc::IPCResult RecvStartAutoscroll(
+ const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation);
+
+ mozilla::ipc::IPCResult RecvStopAutoscroll(const ScrollableLayerGuid& aGuid);
+
+ mozilla::ipc::IPCResult RecvSetLongTapEnabled(const bool& aTapGestureEnabled);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override {}
+
+ private:
+ bool IsGuidValid(const ScrollableLayerGuid& aGuid);
+
+ LayersId mLayersId;
+ RefPtr<APZCTreeManager> mTreeManager;
+ RefPtr<APZUpdater> mUpdater;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZCTreeManagerParent_h
diff --git a/gfx/layers/ipc/APZChild.cpp b/gfx/layers/ipc/APZChild.cpp
new file mode 100644
index 0000000000..a1754e1613
--- /dev/null
+++ b/gfx/layers/ipc/APZChild.cpp
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/GeckoContentController.h"
+
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+
+#include "InputData.h" // for InputData
+
+namespace mozilla {
+namespace layers {
+
+APZChild::APZChild(RefPtr<GeckoContentController> aController)
+ : mController(aController) {
+ MOZ_ASSERT(mController);
+}
+
+APZChild::~APZChild() {
+ if (mController) {
+ mController->Destroy();
+ mController = nullptr;
+ }
+}
+
+mozilla::ipc::IPCResult APZChild::RecvLayerTransforms(
+ nsTArray<MatrixMessage>&& aTransforms) {
+ mController->NotifyLayerTransforms(std::move(aTransforms));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvRequestContentRepaint(
+ const RepaintRequest& aRequest) {
+ MOZ_ASSERT(mController->IsRepaintThread());
+
+ mController->RequestContentRepaint(aRequest);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvUpdateOverscrollVelocity(
+ const ScrollableLayerGuid& aGuid, const float& aX, const float& aY,
+ const bool& aIsRootContent) {
+ mController->UpdateOverscrollVelocity(aGuid, aX, aY, aIsRootContent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvUpdateOverscrollOffset(
+ const ScrollableLayerGuid& aGuid, const float& aX, const float& aY,
+ const bool& aIsRootContent) {
+ mController->UpdateOverscrollOffset(aGuid, aX, aY, aIsRootContent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyMozMouseScrollEvent(
+ const ViewID& aScrollId, const nsString& aEvent) {
+ mController->NotifyMozMouseScrollEvent(aScrollId, aEvent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyAPZStateChange(
+ const ScrollableLayerGuid& aGuid, const APZStateChange& aChange,
+ const int& aArg) {
+ mController->NotifyAPZStateChange(aGuid, aChange, aArg);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyFlushComplete() {
+ MOZ_ASSERT(mController->IsRepaintThread());
+
+ mController->NotifyFlushComplete();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyAsyncScrollbarDragInitiated(
+ const uint64_t& aDragBlockId, const ViewID& aScrollId,
+ const ScrollDirection& aDirection) {
+ mController->NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId,
+ aDirection);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyAsyncScrollbarDragRejected(
+ const ViewID& aScrollId) {
+ mController->NotifyAsyncScrollbarDragRejected(aScrollId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyAsyncAutoscrollRejected(
+ const ViewID& aScrollId) {
+ mController->NotifyAsyncAutoscrollRejected(aScrollId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvDestroy() {
+ // mController->Destroy will be called in the destructor
+ PAPZChild::Send__delete__(this);
+ return IPC_OK();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZChild.h b/gfx/layers/ipc/APZChild.h
new file mode 100644
index 0000000000..a87fd7b7aa
--- /dev/null
+++ b/gfx/layers/ipc/APZChild.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZChild_h
+#define mozilla_layers_APZChild_h
+
+#include "mozilla/layers/PAPZChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class GeckoContentController;
+
+/**
+ * APZChild implements PAPZChild and is used to remote a GeckoContentController
+ * that lives in a different process than where APZ lives.
+ */
+class APZChild final : public PAPZChild {
+ public:
+ using APZStateChange = GeckoContentController_APZStateChange;
+
+ explicit APZChild(RefPtr<GeckoContentController> aController);
+ virtual ~APZChild();
+
+ mozilla::ipc::IPCResult RecvLayerTransforms(
+ nsTArray<MatrixMessage>&& aTransforms);
+
+ mozilla::ipc::IPCResult RecvRequestContentRepaint(
+ const RepaintRequest& aRequest);
+
+ mozilla::ipc::IPCResult RecvUpdateOverscrollVelocity(
+ const ScrollableLayerGuid& aGuid, const float& aX, const float& aY,
+ const bool& aIsRootContent);
+
+ mozilla::ipc::IPCResult RecvUpdateOverscrollOffset(
+ const ScrollableLayerGuid& aGuid, const float& aX, const float& aY,
+ const bool& aIsRootContent);
+
+ mozilla::ipc::IPCResult RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId,
+ const nsString& aEvent);
+
+ mozilla::ipc::IPCResult RecvNotifyAPZStateChange(
+ const ScrollableLayerGuid& aGuid, const APZStateChange& aChange,
+ const int& aArg);
+
+ mozilla::ipc::IPCResult RecvNotifyFlushComplete();
+
+ mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragInitiated(
+ const uint64_t& aDragBlockId, const ViewID& aScrollId,
+ const ScrollDirection& aDirection);
+ mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragRejected(
+ const ViewID& aScrollId);
+
+ mozilla::ipc::IPCResult RecvNotifyAsyncAutoscrollRejected(
+ const ViewID& aScrollId);
+
+ mozilla::ipc::IPCResult RecvDestroy();
+
+ private:
+ RefPtr<GeckoContentController> mController;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZChild_h
diff --git a/gfx/layers/ipc/APZInputBridgeChild.cpp b/gfx/layers/ipc/APZInputBridgeChild.cpp
new file mode 100644
index 0000000000..d1bb57a52c
--- /dev/null
+++ b/gfx/layers/ipc/APZInputBridgeChild.cpp
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZInputBridgeChild.h"
+
+#include "InputData.h" // for InputData, etc
+
+namespace mozilla {
+namespace layers {
+
+APZInputBridgeChild::APZInputBridgeChild() : mDestroyed(false) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+APZInputBridgeChild::~APZInputBridgeChild() = default;
+
+void APZInputBridgeChild::Destroy() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mDestroyed) {
+ return;
+ }
+
+ Send__delete__(this);
+ mDestroyed = true;
+}
+
+void APZInputBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mDestroyed = true;
+}
+
+APZEventResult APZInputBridgeChild::ReceiveInputEvent(InputData& aEvent) {
+ APZEventResult res;
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ MultiTouchInput& event = aEvent.AsMultiTouchInput();
+ MultiTouchInput processedEvent;
+
+ SendReceiveMultiTouchInputEvent(event, &res, &processedEvent);
+
+ event = processedEvent;
+ return res;
+ }
+ case MOUSE_INPUT: {
+ MouseInput& event = aEvent.AsMouseInput();
+ MouseInput processedEvent;
+
+ SendReceiveMouseInputEvent(event, &res, &processedEvent);
+
+ event = processedEvent;
+ return res;
+ }
+ case PANGESTURE_INPUT: {
+ PanGestureInput& event = aEvent.AsPanGestureInput();
+ PanGestureInput processedEvent;
+
+ SendReceivePanGestureInputEvent(event, &res, &processedEvent);
+
+ event = processedEvent;
+ return res;
+ }
+ case PINCHGESTURE_INPUT: {
+ PinchGestureInput& event = aEvent.AsPinchGestureInput();
+ PinchGestureInput processedEvent;
+
+ SendReceivePinchGestureInputEvent(event, &res, &processedEvent);
+
+ event = processedEvent;
+ return res;
+ }
+ case TAPGESTURE_INPUT: {
+ TapGestureInput& event = aEvent.AsTapGestureInput();
+ TapGestureInput processedEvent;
+
+ SendReceiveTapGestureInputEvent(event, &res, &processedEvent);
+
+ event = processedEvent;
+ return res;
+ }
+ case SCROLLWHEEL_INPUT: {
+ ScrollWheelInput& event = aEvent.AsScrollWheelInput();
+ ScrollWheelInput processedEvent;
+
+ SendReceiveScrollWheelInputEvent(event, &res, &processedEvent);
+
+ event = processedEvent;
+ return res;
+ }
+ case KEYBOARD_INPUT: {
+ KeyboardInput& event = aEvent.AsKeyboardInput();
+ KeyboardInput processedEvent;
+
+ SendReceiveKeyboardInputEvent(event, &res, &processedEvent);
+
+ event = processedEvent;
+ return res;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("Invalid InputData type.");
+ res.mStatus = nsEventStatus_eConsumeNoDefault;
+ return res;
+ }
+ }
+}
+
+void APZInputBridgeChild::ProcessUnhandledEvent(
+ LayoutDeviceIntPoint* aRefPoint, ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutFocusSequenceNumber, LayersId* aOutLayersId) {
+ SendProcessUnhandledEvent(*aRefPoint, aRefPoint, aOutTargetGuid,
+ aOutFocusSequenceNumber, aOutLayersId);
+}
+
+void APZInputBridgeChild::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage) {
+ SendUpdateWheelTransaction(aRefPoint, aEventMessage);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZInputBridgeChild.h b/gfx/layers/ipc/APZInputBridgeChild.h
new file mode 100644
index 0000000000..88e1d28c0a
--- /dev/null
+++ b/gfx/layers/ipc/APZInputBridgeChild.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZInputBridgeChild_h
+#define mozilla_layers_APZInputBridgeChild_h
+
+#include "mozilla/layers/APZInputBridge.h"
+#include "mozilla/layers/PAPZInputBridgeChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class APZInputBridgeChild : public PAPZInputBridgeChild, public APZInputBridge {
+ NS_INLINE_DECL_REFCOUNTING(APZInputBridgeChild, final)
+
+ public:
+ APZInputBridgeChild();
+ void Destroy();
+
+ APZEventResult ReceiveInputEvent(InputData& aEvent) override;
+
+ protected:
+ void ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutFocusSequenceNumber,
+ LayersId* aOutLayersId) override;
+
+ void UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ virtual ~APZInputBridgeChild();
+
+ private:
+ bool mDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZInputBridgeChild_h
diff --git a/gfx/layers/ipc/APZInputBridgeParent.cpp b/gfx/layers/ipc/APZInputBridgeParent.cpp
new file mode 100644
index 0000000000..23fcb1cf88
--- /dev/null
+++ b/gfx/layers/ipc/APZInputBridgeParent.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZInputBridgeParent.h"
+
+#include "mozilla/layers/APZInputBridge.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "InputData.h"
+
+namespace mozilla {
+namespace layers {
+
+APZInputBridgeParent::APZInputBridgeParent(const LayersId& aLayersId) {
+ MOZ_ASSERT(XRE_IsGPUProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mTreeManager = CompositorBridgeParent::GetAPZCTreeManager(aLayersId);
+ MOZ_ASSERT(mTreeManager);
+}
+
+APZInputBridgeParent::~APZInputBridgeParent() = default;
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveMultiTouchInputEvent(
+ const MultiTouchInput& aEvent, APZEventResult* aOutResult,
+ MultiTouchInput* aOutEvent) {
+ MultiTouchInput event = aEvent;
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event);
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveMouseInputEvent(
+ const MouseInput& aEvent, APZEventResult* aOutResult,
+ MouseInput* aOutEvent) {
+ MouseInput event = aEvent;
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event);
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceivePanGestureInputEvent(
+ const PanGestureInput& aEvent, APZEventResult* aOutResult,
+ PanGestureInput* aOutEvent) {
+ PanGestureInput event = aEvent;
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event);
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceivePinchGestureInputEvent(
+ const PinchGestureInput& aEvent, APZEventResult* aOutResult,
+ PinchGestureInput* aOutEvent) {
+ PinchGestureInput event = aEvent;
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event);
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveTapGestureInputEvent(
+ const TapGestureInput& aEvent, APZEventResult* aOutResult,
+ TapGestureInput* aOutEvent) {
+ TapGestureInput event = aEvent;
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event);
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveScrollWheelInputEvent(
+ const ScrollWheelInput& aEvent, APZEventResult* aOutResult,
+ ScrollWheelInput* aOutEvent) {
+ ScrollWheelInput event = aEvent;
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event);
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveKeyboardInputEvent(
+ const KeyboardInput& aEvent, APZEventResult* aOutResult,
+ KeyboardInput* aOutEvent) {
+ KeyboardInput event = aEvent;
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(event);
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvUpdateWheelTransaction(
+ const LayoutDeviceIntPoint& aRefPoint, const EventMessage& aEventMessage) {
+ mTreeManager->InputBridge()->UpdateWheelTransaction(aRefPoint, aEventMessage);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvProcessUnhandledEvent(
+ const LayoutDeviceIntPoint& aRefPoint, LayoutDeviceIntPoint* aOutRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutFocusSequenceNumber,
+ LayersId* aOutLayersId) {
+ LayoutDeviceIntPoint refPoint = aRefPoint;
+ mTreeManager->InputBridge()->ProcessUnhandledEvent(
+ &refPoint, aOutTargetGuid, aOutFocusSequenceNumber, aOutLayersId);
+ *aOutRefPoint = refPoint;
+
+ return IPC_OK();
+}
+
+void APZInputBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+ // We shouldn't need it after this
+ mTreeManager = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZInputBridgeParent.h b/gfx/layers/ipc/APZInputBridgeParent.h
new file mode 100644
index 0000000000..94d917a80f
--- /dev/null
+++ b/gfx/layers/ipc/APZInputBridgeParent.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZInputBridgeParent_h
+#define mozilla_layers_APZInputBridgeParent_h
+
+#include "mozilla/layers/PAPZInputBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class IAPZCTreeManager;
+
+class APZInputBridgeParent : public PAPZInputBridgeParent {
+ NS_INLINE_DECL_REFCOUNTING(APZInputBridgeParent, final)
+
+ public:
+ explicit APZInputBridgeParent(const LayersId& aLayersId);
+
+ mozilla::ipc::IPCResult RecvReceiveMultiTouchInputEvent(
+ const MultiTouchInput& aEvent, APZEventResult* aOutResult,
+ MultiTouchInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceiveMouseInputEvent(const MouseInput& aEvent,
+ APZEventResult* aOutResult,
+ MouseInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceivePanGestureInputEvent(
+ const PanGestureInput& aEvent, APZEventResult* aOutResult,
+ PanGestureInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceivePinchGestureInputEvent(
+ const PinchGestureInput& aEvent, APZEventResult* aOutResult,
+ PinchGestureInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceiveTapGestureInputEvent(
+ const TapGestureInput& aEvent, APZEventResult* aOutResult,
+ TapGestureInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceiveScrollWheelInputEvent(
+ const ScrollWheelInput& aEvent, APZEventResult* aOutResult,
+ ScrollWheelInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceiveKeyboardInputEvent(
+ const KeyboardInput& aEvent, APZEventResult* aOutResult,
+ KeyboardInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvUpdateWheelTransaction(
+ const LayoutDeviceIntPoint& aRefPoint, const EventMessage& aEventMessage);
+
+ mozilla::ipc::IPCResult RecvProcessUnhandledEvent(
+ const LayoutDeviceIntPoint& aRefPoint, LayoutDeviceIntPoint* aOutRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutFocusSequenceNumber,
+ LayersId* aOutLayersId);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ protected:
+ virtual ~APZInputBridgeParent();
+
+ private:
+ RefPtr<IAPZCTreeManager> mTreeManager;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZInputBridgeParent_h
diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp
new file mode 100644
index 0000000000..144d383d89
--- /dev/null
+++ b/gfx/layers/ipc/CanvasChild.cpp
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CanvasChild.h"
+
+#include "MainThreadUtils.h"
+#include "mozilla/gfx/DrawTargetRecording.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/layers/CanvasDrawEventRecorder.h"
+#include "nsIObserverService.h"
+#include "RecordedCanvasEventImpl.h"
+
+namespace mozilla {
+namespace layers {
+
+class RingBufferWriterServices final
+ : public CanvasEventRingBuffer::WriterServices {
+ public:
+ explicit RingBufferWriterServices(RefPtr<CanvasChild> aCanvasChild)
+ : mCanvasChild(std::move(aCanvasChild)) {}
+
+ ~RingBufferWriterServices() final = default;
+
+ bool ReaderClosed() final {
+ return !mCanvasChild->GetIPCChannel()->CanSend() ||
+ ipc::ProcessChild::ExpectingShutdown();
+ }
+
+ void ResumeReader() final { mCanvasChild->ResumeTranslation(); }
+
+ private:
+ RefPtr<CanvasChild> mCanvasChild;
+};
+
+class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
+ public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording, final)
+
+ SourceSurfaceCanvasRecording(
+ const RefPtr<gfx::SourceSurface>& aRecordedSuface,
+ CanvasChild* aCanvasChild,
+ const RefPtr<CanvasDrawEventRecorder>& aRecorder)
+ : mRecordedSurface(aRecordedSuface),
+ mCanvasChild(aCanvasChild),
+ mRecorder(aRecorder) {
+ mRecorder->RecordEvent(RecordedAddSurfaceAlias(this, aRecordedSuface));
+ mRecorder->AddStoredObject(this);
+ }
+
+ ~SourceSurfaceCanvasRecording() {
+ ReleaseOnMainThread(std::move(mRecorder), this, std::move(mRecordedSurface),
+ std::move(mCanvasChild));
+ }
+
+ gfx::SurfaceType GetType() const final { return mRecordedSurface->GetType(); }
+
+ gfx::IntSize GetSize() const final { return mRecordedSurface->GetSize(); }
+
+ gfx::SurfaceFormat GetFormat() const final {
+ return mRecordedSurface->GetFormat();
+ }
+
+ already_AddRefed<gfx::DataSourceSurface> GetDataSurface() final {
+ EnsureDataSurfaceOnMainThread();
+ return do_AddRef(mDataSourceSurface);
+ }
+
+ protected:
+ void GuaranteePersistance() final { EnsureDataSurfaceOnMainThread(); }
+
+ private:
+ void EnsureDataSurfaceOnMainThread() {
+ // The data can only be retrieved on the main thread.
+ if (!mDataSourceSurface && NS_IsMainThread()) {
+ mDataSourceSurface = mCanvasChild->GetDataSurface(mRecordedSurface);
+ }
+ }
+
+ // Used to ensure that clean-up that requires it is done on the main thread.
+ static void ReleaseOnMainThread(RefPtr<CanvasDrawEventRecorder> aRecorder,
+ ReferencePtr aSurfaceAlias,
+ RefPtr<gfx::SourceSurface> aAliasedSurface,
+ RefPtr<CanvasChild> aCanvasChild) {
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(NewRunnableFunction(
+ "SourceSurfaceCanvasRecording::ReleaseOnMainThread",
+ SourceSurfaceCanvasRecording::ReleaseOnMainThread,
+ std::move(aRecorder), aSurfaceAlias, std::move(aAliasedSurface),
+ std::move(aCanvasChild)));
+ return;
+ }
+
+ aRecorder->RemoveStoredObject(aSurfaceAlias);
+ aRecorder->RecordEvent(RecordedRemoveSurfaceAlias(aSurfaceAlias));
+ aAliasedSurface = nullptr;
+ aCanvasChild = nullptr;
+ aRecorder = nullptr;
+ }
+
+ RefPtr<gfx::SourceSurface> mRecordedSurface;
+ RefPtr<CanvasChild> mCanvasChild;
+ RefPtr<CanvasDrawEventRecorder> mRecorder;
+ RefPtr<gfx::DataSourceSurface> mDataSourceSurface;
+};
+
+CanvasChild::CanvasChild(Endpoint<PCanvasChild>&& aEndpoint) {
+ aEndpoint.Bind(this);
+}
+
+CanvasChild::~CanvasChild() = default;
+
+static void NotifyCanvasDeviceReset() {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "canvas-device-reset", nullptr);
+ }
+}
+
+ipc::IPCResult CanvasChild::RecvNotifyDeviceChanged() {
+ NotifyCanvasDeviceReset();
+ mRecorder->RecordEvent(RecordedDeviceChangeAcknowledged());
+ return IPC_OK();
+}
+
+/* static */ bool CanvasChild::mDeactivated = false;
+
+ipc::IPCResult CanvasChild::RecvDeactivate() {
+ mDeactivated = true;
+ NotifyCanvasDeviceReset();
+ return IPC_OK();
+}
+
+void CanvasChild::EnsureRecorder(TextureType aTextureType) {
+ if (!mRecorder) {
+ MOZ_ASSERT(mTextureType == TextureType::Unknown);
+ mTextureType = aTextureType;
+ mRecorder = MakeAndAddRef<CanvasDrawEventRecorder>();
+ SharedMemoryBasic::Handle handle;
+ CrossProcessSemaphoreHandle readerSem;
+ CrossProcessSemaphoreHandle writerSem;
+ if (!mRecorder->Init(OtherPid(), &handle, &readerSem, &writerSem,
+ MakeUnique<RingBufferWriterServices>(this))) {
+ mRecorder = nullptr;
+ return;
+ }
+
+ if (CanSend()) {
+ Unused << SendInitTranslator(mTextureType, handle, readerSem, writerSem);
+ }
+ }
+
+ MOZ_RELEASE_ASSERT(mTextureType == aTextureType,
+ "We only support one remote TextureType currently.");
+}
+
+void CanvasChild::ActorDestroy(ActorDestroyReason aWhy) {
+ // Explicitly drop our reference to the recorder, because it holds a reference
+ // to us via the ResumeTranslation callback.
+ mRecorder = nullptr;
+}
+
+void CanvasChild::ResumeTranslation() {
+ if (CanSend()) {
+ SendResumeTranslation();
+ }
+}
+
+void CanvasChild::Destroy() {
+ if (CanSend()) {
+ Close();
+ }
+}
+
+void CanvasChild::OnTextureWriteLock() {
+ // We drop mRecorder in ActorDestroy to break the reference cycle.
+ if (!mRecorder) {
+ return;
+ }
+
+ mHasOutstandingWriteLock = true;
+ mLastWriteLockCheckpoint = mRecorder->CreateCheckpoint();
+}
+
+void CanvasChild::OnTextureForwarded() {
+ // We drop mRecorder in ActorDestroy to break the reference cycle.
+ if (!mRecorder) {
+ return;
+ }
+
+ if (mHasOutstandingWriteLock) {
+ mRecorder->RecordEvent(RecordedCanvasFlush());
+ if (!mRecorder->WaitForCheckpoint(mLastWriteLockCheckpoint)) {
+ gfxWarning() << "Timed out waiting for last write lock to be processed.";
+ }
+
+ mHasOutstandingWriteLock = false;
+ }
+}
+
+void CanvasChild::EnsureBeginTransaction() {
+ // We drop mRecorder in ActorDestroy to break the reference cycle.
+ if (!mRecorder) {
+ return;
+ }
+
+ if (!mIsInTransaction) {
+ mRecorder->RecordEvent(RecordedCanvasBeginTransaction());
+ mIsInTransaction = true;
+ }
+}
+
+void CanvasChild::EndTransaction() {
+ // We drop mRecorder in ActorDestroy to break the reference cycle.
+ if (!mRecorder) {
+ return;
+ }
+
+ if (mIsInTransaction) {
+ mRecorder->RecordEvent(RecordedCanvasEndTransaction());
+ mIsInTransaction = false;
+ mLastNonEmptyTransaction = TimeStamp::NowLoRes();
+ }
+
+ ++mTransactionsSinceGetDataSurface;
+}
+
+bool CanvasChild::ShouldBeCleanedUp() const {
+ // Always return true if we've been deactivated.
+ if (Deactivated()) {
+ return true;
+ }
+
+ // We can only be cleaned up if nothing else references our recorder.
+ if (mRecorder && !mRecorder->hasOneRef()) {
+ return false;
+ }
+
+ static const TimeDuration kCleanUpCanvasThreshold =
+ TimeDuration::FromSeconds(10);
+ return TimeStamp::NowLoRes() - mLastNonEmptyTransaction >
+ kCleanUpCanvasThreshold;
+}
+
+already_AddRefed<gfx::DrawTarget> CanvasChild::CreateDrawTarget(
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
+ // We drop mRecorder in ActorDestroy to break the reference cycle.
+ if (!mRecorder) {
+ return nullptr;
+ }
+
+ RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
+ gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
+ RefPtr<gfx::DrawTarget> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
+ mRecorder, dummyDt, gfx::IntRect(gfx::IntPoint(0, 0), aSize));
+ return dt.forget();
+}
+
+void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
+ // We drop mRecorder in ActorDestroy to break the reference cycle.
+ if (!mRecorder) {
+ return;
+ }
+
+ mRecorder->RecordEvent(aEvent);
+}
+
+already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
+ const gfx::SourceSurface* aSurface) {
+ MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+
+ // We drop mRecorder in ActorDestroy to break the reference cycle.
+ if (!mRecorder) {
+ return nullptr;
+ }
+
+ mTransactionsSinceGetDataSurface = 0;
+ EnsureBeginTransaction();
+ mRecorder->RecordEvent(RecordedPrepareDataForSurface(aSurface));
+ uint32_t checkpoint = mRecorder->CreateCheckpoint();
+
+ gfx::IntSize ssSize = aSurface->GetSize();
+ gfx::SurfaceFormat ssFormat = aSurface->GetFormat();
+ size_t dataFormatWidth = ssSize.width * BytesPerPixel(ssFormat);
+ RefPtr<gfx::DataSourceSurface> dataSurface =
+ gfx::Factory::CreateDataSourceSurfaceWithStride(ssSize, ssFormat,
+ dataFormatWidth);
+ if (!dataSurface) {
+ gfxWarning() << "Failed to create DataSourceSurface.";
+ return nullptr;
+ }
+ gfx::DataSourceSurface::ScopedMap map(dataSurface,
+ gfx::DataSourceSurface::READ_WRITE);
+ char* dest = reinterpret_cast<char*>(map.GetData());
+ if (!mRecorder->WaitForCheckpoint(checkpoint)) {
+ gfxWarning() << "Timed out preparing data for DataSourceSurface.";
+ return dataSurface.forget();
+ }
+
+ mRecorder->RecordEvent(RecordedGetDataForSurface(aSurface));
+ mRecorder->ReturnRead(dest, ssSize.height * dataFormatWidth);
+
+ return dataSurface.forget();
+}
+
+already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface(
+ const RefPtr<gfx::SourceSurface>& aSurface) {
+ MOZ_ASSERT(aSurface);
+ // We drop mRecorder in ActorDestroy to break the reference cycle.
+ if (!mRecorder) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<SourceSurfaceCanvasRecording>(aSurface, this, mRecorder);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CanvasChild.h b/gfx/layers/ipc/CanvasChild.h
new file mode 100644
index 0000000000..68ac863b35
--- /dev/null
+++ b/gfx/layers/ipc/CanvasChild.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CanvasChild_h
+#define mozilla_layers_CanvasChild_h
+
+#include "mozilla/gfx/RecordedEvent.h"
+#include "mozilla/ipc/CrossProcessSemaphore.h"
+#include "mozilla/layers/PCanvasChild.h"
+#include "mozilla/layers/SourceSurfaceSharedData.h"
+#include "nsRefPtrHashtable.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+namespace gfx {
+class SourceSurface;
+}
+
+namespace layers {
+class CanvasDrawEventRecorder;
+
+class CanvasChild final : public PCanvasChild {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(CanvasChild)
+
+ explicit CanvasChild(Endpoint<PCanvasChild>&& aEndpoint);
+
+ /**
+ * @returns true if remote canvas has been deactivated due to failure.
+ */
+ static bool Deactivated() { return mDeactivated; }
+
+ ipc::IPCResult RecvNotifyDeviceChanged();
+
+ ipc::IPCResult RecvDeactivate();
+
+ /**
+ * Ensures that the DrawEventRecorder has been created.
+ *
+ * @params aTextureType the TextureType to create in the CanvasTranslator.
+ */
+ void EnsureRecorder(TextureType aTextureType);
+
+ /**
+ * Send a messsage to our CanvasParent to resume translation.
+ */
+ void ResumeTranslation();
+
+ /**
+ * Clean up IPDL actor.
+ */
+ void Destroy();
+
+ /**
+ * Called when a RecordedTextureData is write locked.
+ */
+ void OnTextureWriteLock();
+
+ /**
+ * Called when a RecordedTextureData is forwarded to the compositor.
+ */
+ void OnTextureForwarded();
+
+ /**
+ * @returns true if we should be caching data surfaces in the GPU process.
+ */
+ bool ShouldCacheDataSurface() const {
+ return mTransactionsSinceGetDataSurface < kCacheDataSurfaceThreshold;
+ }
+
+ /**
+ * Ensures that we have sent a begin transaction event, since the last
+ * end transaction.
+ */
+ void EnsureBeginTransaction();
+
+ /**
+ * Send an end transaction event to indicate the end of events for this frame.
+ */
+ void EndTransaction();
+
+ /**
+ * @returns true if the canvas IPC classes have not been used for some time
+ * and can be cleaned up.
+ */
+ bool ShouldBeCleanedUp() const;
+
+ /**
+ * Create a DrawTargetRecording for a canvas texture.
+ * @param aSize size for the DrawTarget
+ * @param aFormat SurfaceFormat for the DrawTarget
+ * @returns newly created DrawTargetRecording
+ */
+ already_AddRefed<gfx::DrawTarget> CreateDrawTarget(
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+
+ /**
+ * Record an event for processing by the CanvasParent's CanvasTranslator.
+ * @param aEvent the event to record
+ */
+ void RecordEvent(const gfx::RecordedEvent& aEvent);
+
+ /**
+ * Wrap the given surface, so that we can provide a DataSourceSurface if
+ * required.
+ * @param aSurface the SourceSurface to wrap
+ * @returns a SourceSurface that can provide a DataSourceSurface if required
+ */
+ already_AddRefed<gfx::SourceSurface> WrapSurface(
+ const RefPtr<gfx::SourceSurface>& aSurface);
+
+ /**
+ * Get DataSourceSurface from the translated equivalent version of aSurface in
+ * the GPU process.
+ * @param aSurface the SourceSurface in this process for which we need a
+ * DataSourceSurface
+ * @returns a DataSourceSurface created from data for aSurface retrieve from
+ * GPU process
+ */
+ already_AddRefed<gfx::DataSourceSurface> GetDataSurface(
+ const gfx::SourceSurface* aSurface);
+
+ protected:
+ void ActorDestroy(ActorDestroyReason aWhy) final;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CanvasChild);
+
+ ~CanvasChild() final;
+
+ static const uint32_t kCacheDataSurfaceThreshold = 10;
+
+ static bool mDeactivated;
+
+ RefPtr<CanvasDrawEventRecorder> mRecorder;
+ TextureType mTextureType = TextureType::Unknown;
+ uint32_t mLastWriteLockCheckpoint = 0;
+ uint32_t mTransactionsSinceGetDataSurface = kCacheDataSurfaceThreshold;
+ TimeStamp mLastNonEmptyTransaction = TimeStamp::NowLoRes();
+ bool mIsInTransaction = false;
+ bool mHasOutstandingWriteLock = false;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CanvasChild_h
diff --git a/gfx/layers/ipc/CanvasThread.cpp b/gfx/layers/ipc/CanvasThread.cpp
new file mode 100644
index 0000000000..e91492c1ac
--- /dev/null
+++ b/gfx/layers/ipc/CanvasThread.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CanvasThread.h"
+
+#include "mozilla/SharedThreadPool.h"
+#include "nsThreadUtils.h"
+#include "prsystem.h"
+
+bool NS_IsInCanvasThreadOrWorker() {
+ return mozilla::layers::CanvasThreadHolder::IsInCanvasThreadOrWorker();
+}
+
+namespace mozilla {
+namespace layers {
+
+StaticDataMutex<StaticRefPtr<CanvasThreadHolder>>
+ CanvasThreadHolder::sCanvasThreadHolder("sCanvasThreadHolder");
+
+CanvasThreadHolder::CanvasThreadHolder(
+ already_AddRefed<nsISerialEventTarget> aCanvasThread,
+ already_AddRefed<nsIThreadPool> aCanvasWorkers)
+ : mCanvasThread(aCanvasThread),
+ mCanvasWorkers(aCanvasWorkers),
+ mCompositorThreadKeepAlive(CompositorThreadHolder::GetSingleton()) {
+ MOZ_ASSERT(NS_IsInCompositorThread());
+ MOZ_ASSERT(mCanvasThread);
+ MOZ_ASSERT(mCanvasWorkers);
+}
+
+CanvasThreadHolder::~CanvasThreadHolder() {
+ // Note we can't just use NS_IsInCompositorThread() here because
+ // sCompositorThreadHolder might have already gone.
+ MOZ_ASSERT(
+ mCompositorThreadKeepAlive->GetCompositorThread()->IsOnCurrentThread());
+}
+
+/* static */
+already_AddRefed<CanvasThreadHolder> CanvasThreadHolder::EnsureCanvasThread() {
+ MOZ_ASSERT(NS_IsInCompositorThread());
+
+ auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
+ if (!lockedCanvasThreadHolder.ref()) {
+ nsCOMPtr<nsISerialEventTarget> canvasThread;
+ nsresult rv =
+ NS_CreateBackgroundTaskQueue("Canvas", getter_AddRefs(canvasThread));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ // Given that the canvas workers are receiving instructions from
+ // content processes, it probably doesn't make sense to have more than
+ // half the number of processors doing canvas drawing. We set the
+ // lower limit to 2, so that even on single processor systems, if
+ // there is more than one window with canvas drawing, the OS can
+ // manage the load between them.
+ uint32_t threadLimit = std::max(2, PR_GetNumberOfProcessors() / 2);
+ nsCOMPtr<nsIThreadPool> canvasWorkers =
+ SharedThreadPool::Get("CanvasWorkers"_ns, threadLimit);
+ if (!canvasWorkers) {
+ return nullptr;
+ }
+
+ lockedCanvasThreadHolder.ref() =
+ new CanvasThreadHolder(canvasThread.forget(), canvasWorkers.forget());
+ }
+
+ return do_AddRef(lockedCanvasThreadHolder.ref());
+}
+
+/* static */
+void CanvasThreadHolder::ReleaseOnCompositorThread(
+ already_AddRefed<CanvasThreadHolder> aCanvasThreadHolder) {
+ RefPtr<CanvasThreadHolder> canvasThreadHolder = aCanvasThreadHolder;
+ auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
+ lockedCanvasThreadHolder.ref()
+ ->mCompositorThreadKeepAlive->GetCompositorThread()
+ ->Dispatch(NS_NewRunnableFunction(
+ "CanvasThreadHolder::StaticRelease",
+ [canvasThreadHolder = std::move(canvasThreadHolder)]() mutable {
+ RefPtr<CanvasThreadHolder> threadHolder =
+ canvasThreadHolder.forget();
+ threadHolder = nullptr;
+
+ auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
+ if (lockedCanvasThreadHolder.ref()->mRefCnt == 1) {
+ lockedCanvasThreadHolder.ref() = nullptr;
+ }
+ }));
+}
+
+/* static */
+bool CanvasThreadHolder::IsInCanvasThread() {
+ auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
+ return lockedCanvasThreadHolder.ref() &&
+ lockedCanvasThreadHolder.ref()->mCanvasThread->IsOnCurrentThread();
+}
+
+/* static */
+bool CanvasThreadHolder::IsInCanvasWorker() {
+ auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
+ return lockedCanvasThreadHolder.ref() &&
+ lockedCanvasThreadHolder.ref()->mCanvasWorkers->IsOnCurrentThread();
+}
+
+/* static */
+bool CanvasThreadHolder::IsInCanvasThreadOrWorker() {
+ auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
+ return lockedCanvasThreadHolder.ref() &&
+ (lockedCanvasThreadHolder.ref()->mCanvasWorkers->IsOnCurrentThread() ||
+ lockedCanvasThreadHolder.ref()->mCanvasThread->IsOnCurrentThread());
+}
+
+/* static */
+void CanvasThreadHolder::MaybeDispatchToCanvasThread(
+ already_AddRefed<nsIRunnable> aRunnable) {
+ auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock();
+ if (!lockedCanvasThreadHolder.ref()) {
+ // There is no canvas thread just release the runnable.
+ nsCOMPtr<nsIRunnable> runnable = aRunnable;
+ return;
+ }
+
+ lockedCanvasThreadHolder.ref()->mCanvasThread->Dispatch(std::move(aRunnable));
+}
+
+void CanvasThreadHolder::DispatchToCanvasThread(
+ already_AddRefed<nsIRunnable> aRunnable) {
+ mCanvasThread->Dispatch(std::move(aRunnable));
+}
+
+already_AddRefed<TaskQueue> CanvasThreadHolder::CreateWorkerTaskQueue() {
+ return MakeAndAddRef<TaskQueue>(do_AddRef(mCanvasWorkers));
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CanvasThread.h b/gfx/layers/ipc/CanvasThread.h
new file mode 100644
index 0000000000..ebcd4bc8d5
--- /dev/null
+++ b/gfx/layers/ipc/CanvasThread.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CanvasThread_h
+#define mozilla_layers_CanvasThread_h
+
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/TaskQueue.h"
+#include "nsIThreadPool.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The CanvasThreadHolder is used to manage the lifetime of the canvas
+ * thread (IPC) and workers and also provides methods for using them.
+ */
+class CanvasThreadHolder final {
+ // We only AddRef/Release on the Compositor thread.
+ NS_INLINE_DECL_REFCOUNTING(CanvasThreadHolder)
+
+ public:
+ /**
+ * Ensures that the canvas thread and workers are created.
+ * This must be called on the compositor thread.
+ * @return a CanvasThreadHolder
+ */
+ static already_AddRefed<CanvasThreadHolder> EnsureCanvasThread();
+
+ /**
+ * Used to release CanvasThreadHolder references, which must be released on
+ * the compositor thread.
+ * @param aCanvasThreadHolder the reference to release
+ */
+ static void ReleaseOnCompositorThread(
+ already_AddRefed<CanvasThreadHolder> aCanvasThreadHolder);
+
+ /**
+ * @return true if in the canvas thread
+ */
+ static bool IsInCanvasThread();
+
+ /**
+ * @return true if in a canvas worker thread
+ */
+ static bool IsInCanvasWorker();
+
+ /**
+ * @return true if in the canvas thread or worker
+ */
+ static bool IsInCanvasThreadOrWorker();
+
+ /**
+ * Static method to dispatch a runnable to the canvas thread. If there is no
+ * canvas thread this will just release aRunnable.
+ * @param aRunnable the runnable to dispatch
+ */
+ static void MaybeDispatchToCanvasThread(
+ already_AddRefed<nsIRunnable> aRunnable);
+
+ /**
+ * Dispatch a runnable to the canvas thread.
+ * @param aRunnable the runnable to dispatch
+ */
+ void DispatchToCanvasThread(already_AddRefed<nsIRunnable> aRunnable);
+
+ /**
+ * Create a TaskQueue for the canvas worker threads. This must be shutdown
+ * before the reference to CanvasThreadHolder is dropped.
+ * @return a TaskQueue that dispatches to the canvas worker threads
+ */
+ already_AddRefed<TaskQueue> CreateWorkerTaskQueue();
+
+ private:
+ static StaticDataMutex<StaticRefPtr<CanvasThreadHolder>> sCanvasThreadHolder;
+
+ CanvasThreadHolder(already_AddRefed<nsISerialEventTarget> aCanvasThread,
+ already_AddRefed<nsIThreadPool> aCanvasWorkers);
+
+ ~CanvasThreadHolder();
+
+ nsCOMPtr<nsISerialEventTarget> mCanvasThread;
+ RefPtr<nsIThreadPool> mCanvasWorkers;
+
+ // Hold a reference to prevent the compositor thread ending.
+ RefPtr<CompositorThreadHolder> mCompositorThreadKeepAlive;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CanvasThread_h
diff --git a/gfx/layers/ipc/CanvasTranslator.cpp b/gfx/layers/ipc/CanvasTranslator.cpp
new file mode 100644
index 0000000000..065bfacfd4
--- /dev/null
+++ b/gfx/layers/ipc/CanvasTranslator.cpp
@@ -0,0 +1,534 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "CanvasTranslator.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/SyncRunnable.h"
+#include "mozilla/Telemetry.h"
+#include "nsTHashtable.h"
+#include "RecordedCanvasEventImpl.h"
+
+#if defined(XP_WIN)
+# include "mozilla/gfx/DeviceManagerDx.h"
+# include "mozilla/layers/TextureD3D11.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+// When in a transaction we wait for a short time because we're expecting more
+// events from the content process. We don't want to wait for too long in case
+// other content processes are waiting for events to process.
+static const TimeDuration kReadEventTimeout = TimeDuration::FromMilliseconds(5);
+
+class RingBufferReaderServices final
+ : public CanvasEventRingBuffer::ReaderServices {
+ public:
+ explicit RingBufferReaderServices(RefPtr<CanvasTranslator> aCanvasTranslator)
+ : mCanvasTranslator(std::move(aCanvasTranslator)) {}
+
+ ~RingBufferReaderServices() final = default;
+
+ bool WriterClosed() final {
+ return !mCanvasTranslator->GetIPCChannel()->CanSend();
+ }
+
+ private:
+ RefPtr<CanvasTranslator> mCanvasTranslator;
+};
+
+TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType,
+ const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat) {
+ TextureData* textureData = nullptr;
+ switch (aTextureType) {
+#ifdef XP_WIN
+ case TextureType::D3D11: {
+ textureData =
+ D3D11TextureData::Create(aSize, aFormat, ALLOC_CLEAR_BUFFER, mDevice);
+ break;
+ }
+#endif
+ default:
+ MOZ_CRASH("Unsupported TextureType for CanvasTranslator.");
+ }
+
+ return textureData;
+}
+
+typedef nsTHashtable<nsRefPtrHashKey<CanvasTranslator>> CanvasTranslatorSet;
+
+static CanvasTranslatorSet& CanvasTranslators() {
+ MOZ_ASSERT(CanvasThreadHolder::IsInCanvasThread());
+ static CanvasTranslatorSet* sCanvasTranslator = new CanvasTranslatorSet();
+ return *sCanvasTranslator;
+}
+
+static void EnsureAllClosed() {
+ for (auto iter = CanvasTranslators().Iter(); !iter.Done(); iter.Next()) {
+ iter.Get()->GetKey()->Close();
+ }
+}
+
+/* static */ void CanvasTranslator::Shutdown() {
+ // If the dispatch fails there is no canvas thread and so no translators.
+ CanvasThreadHolder::MaybeDispatchToCanvasThread(NewRunnableFunction(
+ "CanvasTranslator::EnsureAllClosed", &EnsureAllClosed));
+}
+
+/* static */ already_AddRefed<CanvasTranslator> CanvasTranslator::Create(
+ ipc::Endpoint<PCanvasParent>&& aEndpoint) {
+ MOZ_ASSERT(NS_IsInCompositorThread());
+
+ RefPtr<CanvasThreadHolder> threadHolder =
+ CanvasThreadHolder::EnsureCanvasThread();
+ RefPtr<CanvasTranslator> canvasTranslator =
+ new CanvasTranslator(do_AddRef(threadHolder));
+ threadHolder->DispatchToCanvasThread(
+ NewRunnableMethod<Endpoint<PCanvasParent>&&>(
+ "CanvasTranslator::Bind", canvasTranslator, &CanvasTranslator::Bind,
+ std::move(aEndpoint)));
+ return canvasTranslator.forget();
+}
+
+CanvasTranslator::CanvasTranslator(
+ already_AddRefed<CanvasThreadHolder> aCanvasThreadHolder)
+ : gfx::InlineTranslator(), mCanvasThreadHolder(aCanvasThreadHolder) {
+ // Track when remote canvas has been activated.
+ Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_CANVAS_REMOTE_ACTIVATED, 1);
+}
+
+CanvasTranslator::~CanvasTranslator() {
+ if (mReferenceTextureData) {
+ mReferenceTextureData->Unlock();
+ }
+}
+
+void CanvasTranslator::Bind(Endpoint<PCanvasParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+
+ CanvasTranslators().PutEntry(this);
+}
+
+mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
+ const TextureType& aTextureType,
+ const ipc::SharedMemoryBasic::Handle& aReadHandle,
+ const CrossProcessSemaphoreHandle& aReaderSem,
+ const CrossProcessSemaphoreHandle& aWriterSem) {
+ mTextureType = aTextureType;
+
+ // We need to initialize the stream first, because it might be used to
+ // communicate other failures back to the writer.
+ mStream = MakeUnique<CanvasEventRingBuffer>();
+ if (!mStream->InitReader(aReadHandle, aReaderSem, aWriterSem,
+ MakeUnique<RingBufferReaderServices>(this))) {
+ return IPC_FAIL(this, "Failed to initialize ring buffer reader.");
+ }
+
+#if defined(XP_WIN)
+ if (!CheckForFreshCanvasDevice(__LINE__)) {
+ gfxCriticalNote << "GFX: CanvasTranslator failed to get device";
+ return IPC_OK();
+ }
+#endif
+
+ mTranslationTaskQueue = mCanvasThreadHolder->CreateWorkerTaskQueue();
+ return RecvResumeTranslation();
+}
+
+ipc::IPCResult CanvasTranslator::RecvResumeTranslation() {
+ if (mDeactivated) {
+ // The other side might have sent a resume message before we deactivated.
+ return IPC_OK();
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(mTranslationTaskQueue->Dispatch(
+ NewRunnableMethod("CanvasTranslator::StartTranslation", this,
+ &CanvasTranslator::StartTranslation)));
+
+ return IPC_OK();
+}
+
+void CanvasTranslator::StartTranslation() {
+ if (!TranslateRecording() && GetIPCChannel()->CanSend()) {
+ MOZ_ALWAYS_SUCCEEDS(mTranslationTaskQueue->Dispatch(
+ NewRunnableMethod("CanvasTranslator::StartTranslation", this,
+ &CanvasTranslator::StartTranslation)));
+ }
+
+ // If the stream has been marked as bad and the Writer hasn't failed,
+ // deactivate remote canvas.
+ if (!mStream->good() && !mStream->WriterFailed()) {
+ Telemetry::ScalarAdd(
+ Telemetry::ScalarID::GFX_CANVAS_REMOTE_DEACTIVATED_BAD_STREAM, 1);
+ Deactivate();
+ }
+}
+
+void CanvasTranslator::ActorDestroy(ActorDestroyReason why) {
+ MOZ_ASSERT(CanvasThreadHolder::IsInCanvasThread());
+
+ if (!mTranslationTaskQueue) {
+ return FinishShutdown();
+ }
+
+ mTranslationTaskQueue->BeginShutdown()->Then(
+ GetCurrentSerialEventTarget(), __func__, this,
+ &CanvasTranslator::FinishShutdown, &CanvasTranslator::FinishShutdown);
+}
+
+void CanvasTranslator::FinishShutdown() {
+ MOZ_ASSERT(CanvasThreadHolder::IsInCanvasThread());
+
+ // mTranslationTaskQueue has shutdown we can safely drop the ring buffer to
+ // break the cycle caused by RingBufferReaderServices.
+ mStream = nullptr;
+
+ // CanvasTranslators has a MOZ_ASSERT(CanvasThreadHolder::IsInCanvasThread())
+ // to ensure it is only called on the Canvas Thread. This takes a lock on
+ // CanvasThreadHolder::sCanvasThreadHolder, which is also locked in
+ // CanvasThreadHolder::StaticRelease on the compositor thread from
+ // ReleaseOnCompositorThread below. If that lock wins the race with the one in
+ // IsInCanvasThread and it is the last CanvasThreadHolder reference then it
+ // shuts down the canvas thread waiting for it to finish. However
+ // IsInCanvasThread is waiting for the lock on the canvas thread and we
+ // deadlock. So, we need to call CanvasTranslators before
+ // ReleaseOnCompositorThread.
+ CanvasTranslatorSet& canvasTranslators = CanvasTranslators();
+ CanvasThreadHolder::ReleaseOnCompositorThread(mCanvasThreadHolder.forget());
+ canvasTranslators.RemoveEntry(this);
+}
+
+void CanvasTranslator::Deactivate() {
+ if (mDeactivated) {
+ return;
+ }
+ mDeactivated = true;
+
+ // We need to tell the other side to deactivate. Make sure the stream is
+ // marked as bad so that the writing side won't wait for space to write.
+ mStream->SetIsBad();
+ mCanvasThreadHolder->DispatchToCanvasThread(
+ NewRunnableMethod("CanvasTranslator::SendDeactivate", this,
+ &CanvasTranslator::SendDeactivate));
+
+ {
+ // Unlock all of our textures.
+ gfx::AutoSerializeWithMoz2D serializeWithMoz2D(GetBackendType());
+ for (auto const& entry : mTextureDatas) {
+ entry.second->Unlock();
+ }
+ }
+
+ // Also notify anyone waiting for a surface descriptor. This must be done
+ // after mDeactivated is set to true.
+ mSurfaceDescriptorsMonitor.NotifyAll();
+}
+
+bool CanvasTranslator::TranslateRecording() {
+ MOZ_ASSERT(CanvasThreadHolder::IsInCanvasWorker());
+
+ int32_t eventType = mStream->ReadNextEvent();
+ while (mStream->good()) {
+ bool success = RecordedEvent::DoWithEventFromStream(
+ *mStream, static_cast<RecordedEvent::EventType>(eventType),
+ [&](RecordedEvent* recordedEvent) -> bool {
+ // Make sure that the whole event was read from the stream.
+ if (!mStream->good()) {
+ if (!GetIPCChannel()->CanSend()) {
+ // The other side has closed only warn about read failure.
+ gfxWarning() << "Failed to read event type: "
+ << recordedEvent->GetType();
+ } else {
+ gfxCriticalNote << "Failed to read event type: "
+ << recordedEvent->GetType();
+ }
+ return false;
+ }
+
+ return recordedEvent->PlayEvent(this);
+ });
+
+ // Check the stream is good here or we will log the issue twice.
+ if (!mStream->good()) {
+ return true;
+ }
+
+ if (!success && !HandleExtensionEvent(eventType)) {
+ if (mDeviceResetInProgress) {
+ // We've notified the recorder of a device change, so we are expecting
+ // failures. Log as a warning to prevent crash reporting being flooded.
+ gfxWarning() << "Failed to play canvas event type: " << eventType;
+ } else {
+ gfxCriticalNote << "Failed to play canvas event type: " << eventType;
+ }
+ if (!mStream->good()) {
+ return true;
+ }
+ }
+
+ if (!mIsInTransaction) {
+ return mStream->StopIfEmpty();
+ }
+
+ if (!mStream->HasDataToRead()) {
+ // We're going to wait for the next event, so take the opportunity to
+ // flush the rendering.
+ Flush();
+ if (!mStream->WaitForDataToRead(kReadEventTimeout, 0)) {
+ return true;
+ }
+ }
+
+ eventType = mStream->ReadNextEvent();
+ }
+
+ return true;
+}
+
+#define READ_AND_PLAY_CANVAS_EVENT_TYPE(_typeenum, _class) \
+ case _typeenum: { \
+ auto e = _class(*mStream); \
+ if (!mStream->good()) { \
+ if (!GetIPCChannel()->CanSend()) { \
+ /* The other side has closed only warn about read failure. */ \
+ gfxWarning() << "Failed to read event type: " << _typeenum; \
+ } else { \
+ gfxCriticalNote << "Failed to read event type: " << _typeenum; \
+ } \
+ return false; \
+ } \
+ return e.PlayCanvasEvent(this); \
+ }
+
+bool CanvasTranslator::HandleExtensionEvent(int32_t aType) {
+ // This is where we handle extensions to the Moz2D Recording events to handle
+ // canvas specific things.
+ switch (aType) {
+ FOR_EACH_CANVAS_EVENT(READ_AND_PLAY_CANVAS_EVENT_TYPE)
+ default:
+ return false;
+ }
+}
+
+void CanvasTranslator::BeginTransaction() { mIsInTransaction = true; }
+
+void CanvasTranslator::Flush() {
+#if defined(XP_WIN)
+ // We can end up without a device, due to a reset and failure to re-create.
+ if (!mDevice) {
+ return;
+ }
+
+ gfx::AutoSerializeWithMoz2D serializeWithMoz2D(GetBackendType());
+ RefPtr<ID3D11DeviceContext> deviceContext;
+ mDevice->GetImmediateContext(getter_AddRefs(deviceContext));
+ deviceContext->Flush();
+#endif
+}
+
+void CanvasTranslator::EndTransaction() {
+ Flush();
+ // At the end of a transaction is a good time to check if a new canvas device
+ // has been created, even if a reset did not occur.
+ Unused << CheckForFreshCanvasDevice(__LINE__);
+ mIsInTransaction = false;
+}
+
+void CanvasTranslator::DeviceChangeAcknowledged() {
+ mDeviceResetInProgress = false;
+}
+
+bool CanvasTranslator::CreateReferenceTexture() {
+ if (mReferenceTextureData) {
+ mReferenceTextureData->Unlock();
+ }
+
+ mReferenceTextureData.reset(CreateTextureData(
+ mTextureType, gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8));
+ if (!mReferenceTextureData) {
+ return false;
+ }
+
+ mReferenceTextureData->Lock(OpenMode::OPEN_READ_WRITE);
+ mBaseDT = mReferenceTextureData->BorrowDrawTarget();
+ if (mBaseDT) {
+ mBackendType = mBaseDT->GetBackendType();
+ }
+ return true;
+}
+
+bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) {
+#if defined(XP_WIN)
+ // If a new device has already been created, use that one.
+ RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetCanvasDevice();
+ if (device && device != mDevice) {
+ if (mDevice) {
+ // We already had a device, notify child of change.
+ NotifyDeviceChanged();
+ }
+ mDevice = device.forget();
+ return CreateReferenceTexture();
+ }
+
+ if (mDevice) {
+ if (mDevice->GetDeviceRemovedReason() == S_OK) {
+ return false;
+ }
+
+ gfxCriticalNote << "GFX: CanvasTranslator detected a device reset at "
+ << aLineNumber;
+ NotifyDeviceChanged();
+ }
+
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+ "CanvasTranslator NotifyDeviceReset",
+ []() { gfx::GPUParent::GetSingleton()->NotifyDeviceReset(); });
+
+ // It is safe to wait here because only the Compositor thread waits on us and
+ // the main thread doesn't wait on the compositor thread in the GPU process.
+ SyncRunnable::DispatchToThread(GetMainThreadEventTarget(), runnable,
+ /*aForceDispatch*/ true);
+
+ mDevice = gfx::DeviceManagerDx::Get()->GetCanvasDevice();
+ if (!mDevice) {
+ // We don't have a canvas device, we need to deactivate.
+ Telemetry::ScalarAdd(
+ Telemetry::ScalarID::GFX_CANVAS_REMOTE_DEACTIVATED_NO_DEVICE, 1);
+ Deactivate();
+ return false;
+ }
+
+ return CreateReferenceTexture();
+#else
+ return false;
+#endif
+}
+
+void CanvasTranslator::NotifyDeviceChanged() {
+ mDeviceResetInProgress = true;
+ mCanvasThreadHolder->DispatchToCanvasThread(
+ NewRunnableMethod("CanvasTranslator::SendNotifyDeviceChanged", this,
+ &CanvasTranslator::SendNotifyDeviceChanged));
+}
+
+void CanvasTranslator::AddSurfaceDescriptor(int64_t aTextureId,
+ TextureData* aTextureData) {
+ UniquePtr<SurfaceDescriptor> descriptor = MakeUnique<SurfaceDescriptor>();
+ if (!aTextureData->Serialize(*descriptor)) {
+ MOZ_CRASH("Failed to serialize");
+ }
+
+ MonitorAutoLock lock(mSurfaceDescriptorsMonitor);
+ mSurfaceDescriptors[aTextureId] = std::move(descriptor);
+ mSurfaceDescriptorsMonitor.Notify();
+}
+
+already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget(
+ gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat) {
+ RefPtr<gfx::DrawTarget> dt;
+ do {
+ // It is important that AutoSerializeWithMoz2D is called within the loop
+ // and doesn't hold during calls to CheckForFreshCanvasDevice, because that
+ // might cause a deadlock with device reset code on the main thread.
+ gfx::AutoSerializeWithMoz2D serializeWithMoz2D(GetBackendType());
+ TextureData* textureData = CreateTextureData(mTextureType, aSize, aFormat);
+ if (textureData) {
+ MOZ_DIAGNOSTIC_ASSERT(mNextTextureId >= 0, "No texture ID set");
+ textureData->Lock(OpenMode::OPEN_READ_WRITE);
+ mTextureDatas[mNextTextureId] = UniquePtr<TextureData>(textureData);
+ AddSurfaceDescriptor(mNextTextureId, textureData);
+ dt = textureData->BorrowDrawTarget();
+ }
+ } while (!dt && CheckForFreshCanvasDevice(__LINE__));
+ AddDrawTarget(aRefPtr, dt);
+ mNextTextureId = -1;
+
+ return dt.forget();
+}
+
+void CanvasTranslator::RemoveTexture(int64_t aTextureId) {
+ gfx::AutoSerializeWithMoz2D serializeWithMoz2D(GetBackendType());
+ mTextureDatas.erase(aTextureId);
+
+ // It is possible that the texture from the content process has never been
+ // forwarded to the GPU process, so make sure its descriptor is removed.
+ MonitorAutoLock lock(mSurfaceDescriptorsMonitor);
+ mSurfaceDescriptors.erase(aTextureId);
+}
+
+TextureData* CanvasTranslator::LookupTextureData(int64_t aTextureId) {
+ TextureMap::const_iterator result = mTextureDatas.find(aTextureId);
+ if (result == mTextureDatas.end()) {
+ return nullptr;
+ }
+ return result->second.get();
+}
+
+UniquePtr<SurfaceDescriptor> CanvasTranslator::WaitForSurfaceDescriptor(
+ int64_t aTextureId) {
+ MonitorAutoLock lock(mSurfaceDescriptorsMonitor);
+ DescriptorMap::iterator result;
+ while ((result = mSurfaceDescriptors.find(aTextureId)) ==
+ mSurfaceDescriptors.end()) {
+ // If remote canvas has been deactivated just return null.
+ if (mDeactivated) {
+ return nullptr;
+ }
+
+ mSurfaceDescriptorsMonitor.Wait();
+ }
+
+ UniquePtr<SurfaceDescriptor> descriptor = std::move(result->second);
+ mSurfaceDescriptors.erase(aTextureId);
+ return descriptor;
+}
+
+gfx::DataSourceSurface* CanvasTranslator::LookupDataSurface(
+ gfx::ReferencePtr aRefPtr) {
+ return mDataSurfaces.GetWeak(aRefPtr);
+}
+
+void CanvasTranslator::AddDataSurface(
+ gfx::ReferencePtr aRefPtr, RefPtr<gfx::DataSourceSurface>&& aSurface) {
+ mDataSurfaces.Put(aRefPtr, std::move(aSurface));
+}
+
+void CanvasTranslator::RemoveDataSurface(gfx::ReferencePtr aRefPtr) {
+ mDataSurfaces.Remove(aRefPtr);
+}
+
+void CanvasTranslator::SetPreparedMap(
+ gfx::ReferencePtr aSurface,
+ UniquePtr<gfx::DataSourceSurface::ScopedMap> aMap) {
+ mMappedSurface = aSurface;
+ mPreparedMap = std::move(aMap);
+}
+
+UniquePtr<gfx::DataSourceSurface::ScopedMap> CanvasTranslator::GetPreparedMap(
+ gfx::ReferencePtr aSurface) {
+ if (!mPreparedMap) {
+ // We might fail to set the map during, for example, device resets.
+ return nullptr;
+ }
+
+ MOZ_RELEASE_ASSERT(mMappedSurface == aSurface,
+ "aSurface must match previously stored surface.");
+
+ mMappedSurface = nullptr;
+ return std::move(mPreparedMap);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CanvasTranslator.h b/gfx/layers/ipc/CanvasTranslator.h
new file mode 100644
index 0000000000..eb010bbeb7
--- /dev/null
+++ b/gfx/layers/ipc/CanvasTranslator.h
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CanvasTranslator_h
+#define mozilla_layers_CanvasTranslator_h
+
+#include <unordered_map>
+#include <vector>
+
+#include "mozilla/gfx/InlineTranslator.h"
+#include "mozilla/layers/CanvasDrawEventRecorder.h"
+#include "mozilla/layers/CanvasThread.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/PCanvasParent.h"
+#include "mozilla/ipc/CrossProcessSemaphore.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureData;
+
+class CanvasTranslator final : public gfx::InlineTranslator,
+ public PCanvasParent {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasTranslator)
+
+ friend class PProtocolParent;
+
+ /**
+ * Create an uninitialized CanvasTranslator and bind it to the given endpoint
+ * on the CanvasPlaybackLoop.
+ *
+ * @param aEndpoint the endpoint to bind to
+ * @return the new CanvasTranslator
+ */
+ static already_AddRefed<CanvasTranslator> Create(
+ Endpoint<PCanvasParent>&& aEndpoint);
+
+ /**
+ * Shutdown all of the CanvasTranslators.
+ */
+ static void Shutdown();
+
+ /**
+ * Initialize the canvas translator for a particular TextureType and
+ * CanvasEventRingBuffer.
+ *
+ * @param aTextureType the TextureType the translator will create
+ * @param aReadHandle handle to the shared memory for the
+ * CanvasEventRingBuffer
+ * @param aReaderSem reading blocked semaphore for the CanvasEventRingBuffer
+ * @param aWriterSem writing blocked semaphore for the CanvasEventRingBuffer
+ */
+ ipc::IPCResult RecvInitTranslator(
+ const TextureType& aTextureType,
+ const ipc::SharedMemoryBasic::Handle& aReadHandle,
+ const CrossProcessSemaphoreHandle& aReaderSem,
+ const CrossProcessSemaphoreHandle& aWriterSem);
+
+ /**
+ * Used to tell the CanvasTranslator to start translating again after it has
+ * stopped due to a timeout waiting for events.
+ */
+ ipc::IPCResult RecvResumeTranslation();
+
+ void ActorDestroy(ActorDestroyReason why) final;
+
+ /**
+ * Translates events until no more are available or the end of a transaction
+ * If this returns false the caller of this is responsible for re-calling
+ * this function.
+ *
+ * @returns true if all events are processed and false otherwise.
+ */
+ bool TranslateRecording();
+
+ /**
+ * Marks the beginning of rendering for a transaction. While in a transaction
+ * the translator will wait for a short time for events before returning.
+ * When not in a transaction the translator will only translate one event at a
+ * time.
+ */
+ void BeginTransaction();
+
+ /**
+ * Marks the end of a transaction.
+ */
+ void EndTransaction();
+
+ /**
+ * Flushes canvas drawing, for example to a device.
+ */
+ void Flush();
+
+ /**
+ * Marks that device change processing in the writing process has finished.
+ */
+ void DeviceChangeAcknowledged();
+
+ /**
+ * Used to send data back to the writer. This is done through the same shared
+ * memory so the writer must wait and read the response after it has submitted
+ * the event that uses this.
+ *
+ * @param aData the data to be written back to the writer
+ * @param aSize the number of chars to write
+ */
+ void ReturnWrite(const char* aData, size_t aSize) {
+ mStream->ReturnWrite(aData, aSize);
+ }
+
+ /**
+ * Set the texture ID that will be used as a lookup for the texture created by
+ * the next CreateDrawTarget.
+ */
+ void SetNextTextureId(int64_t aNextTextureId) {
+ mNextTextureId = aNextTextureId;
+ }
+
+ /**
+ * Used during playback of events to create DrawTargets. For the
+ * CanvasTranslator this means creating TextureDatas and getting the
+ * DrawTargets from those.
+ *
+ * @param aRefPtr the key to store the created DrawTarget against
+ * @param aSize the size of the DrawTarget
+ * @param aFormat the surface format for the DrawTarget
+ * @returns the new DrawTarget
+ */
+ already_AddRefed<gfx::DrawTarget> CreateDrawTarget(
+ gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat) final;
+
+ /**
+ * Get the TextureData associated with a TextureData from another process.
+ *
+ * @param aTextureId the key used to find the TextureData
+ * @returns the TextureData found
+ */
+ TextureData* LookupTextureData(int64_t aTextureId);
+
+ /**
+ * Waits for the SurfaceDescriptor associated with a TextureData from another
+ * process to be created and then returns it.
+ *
+ * @param aTextureId the key used to find the SurfaceDescriptor
+ * @returns the SurfaceDescriptor found
+ */
+ UniquePtr<SurfaceDescriptor> WaitForSurfaceDescriptor(int64_t aTextureId);
+
+ /**
+ * Removes the texture and other objects associated with a texture ID.
+ *
+ * @param aTextureId the texture ID to remove
+ */
+ void RemoveTexture(int64_t aTextureId);
+
+ /**
+ * Removes the SourceSurface and other objects associated with a SourceSurface
+ * from another process.
+ *
+ * @param aRefPtr the key to the objects to remove
+ */
+ void RemoveSourceSurface(gfx::ReferencePtr aRefPtr) final {
+ RemoveDataSurface(aRefPtr);
+ InlineTranslator::RemoveSourceSurface(aRefPtr);
+ }
+
+ /**
+ * Gets the cached DataSourceSurface, if it exists, associated with a
+ * SourceSurface from another process.
+ *
+ * @param aRefPtr the key used to find the DataSourceSurface
+ * @returns the DataSourceSurface or nullptr if not found
+ */
+ gfx::DataSourceSurface* LookupDataSurface(gfx::ReferencePtr aRefPtr);
+
+ /**
+ * Used to cache the DataSourceSurface from a SourceSurface associated with a
+ * SourceSurface from another process. This is to improve performance if we
+ * require the data for that SourceSurface.
+ *
+ * @param aRefPtr the key used to store the DataSourceSurface
+ * @param aSurface the DataSourceSurface to store
+ */
+ void AddDataSurface(gfx::ReferencePtr aRefPtr,
+ RefPtr<gfx::DataSourceSurface>&& aSurface);
+
+ /**
+ * Gets the cached DataSourceSurface, if it exists, associated with a
+ * SourceSurface from another process.
+ *
+ * @param aRefPtr the key used to find the DataSourceSurface
+ * @returns the DataSourceSurface or nullptr if not found
+ */
+ void RemoveDataSurface(gfx::ReferencePtr aRefPtr);
+
+ /**
+ * Sets a ScopedMap, to be used in a later event.
+ *
+ * @param aSurface the associated surface in the other process
+ * @param aMap the ScopedMap to store
+ */
+ void SetPreparedMap(gfx::ReferencePtr aSurface,
+ UniquePtr<gfx::DataSourceSurface::ScopedMap> aMap);
+
+ /**
+ * Gets the ScopedMap stored using SetPreparedMap.
+ *
+ * @param aSurface must match the surface from the SetPreparedMap call
+ * @returns the ScopedMap if aSurface matches otherwise nullptr
+ */
+ UniquePtr<gfx::DataSourceSurface::ScopedMap> GetPreparedMap(
+ gfx::ReferencePtr aSurface);
+
+ /**
+ * @return the BackendType being used for translation
+ */
+ gfx::BackendType GetBackendType() { return mBackendType; }
+
+ private:
+ explicit CanvasTranslator(
+ already_AddRefed<CanvasThreadHolder> aCanvasThreadHolder);
+
+ ~CanvasTranslator();
+
+ void Bind(Endpoint<PCanvasParent>&& aEndpoint);
+
+ void StartTranslation();
+
+ void FinishShutdown();
+
+ void Deactivate();
+
+ TextureData* CreateTextureData(TextureType aTextureType,
+ const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat);
+
+ void AddSurfaceDescriptor(int64_t aTextureId, TextureData* atextureData);
+
+ bool HandleExtensionEvent(int32_t aType);
+
+ bool CreateReferenceTexture();
+ bool CheckForFreshCanvasDevice(int aLineNumber);
+ void NotifyDeviceChanged();
+
+ RefPtr<CanvasThreadHolder> mCanvasThreadHolder;
+ RefPtr<TaskQueue> mTranslationTaskQueue;
+#if defined(XP_WIN)
+ RefPtr<ID3D11Device> mDevice;
+#endif
+ // We hold the ring buffer as a UniquePtr so we can drop it once
+ // mTranslationTaskQueue has shutdown to break a RefPtr cycle.
+ UniquePtr<CanvasEventRingBuffer> mStream;
+ TextureType mTextureType = TextureType::Unknown;
+ UniquePtr<TextureData> mReferenceTextureData;
+ // Sometimes during device reset our reference DrawTarget can be null, so we
+ // hold the BackendType separately.
+ gfx::BackendType mBackendType = gfx::BackendType::NONE;
+ typedef std::unordered_map<int64_t, UniquePtr<TextureData>> TextureMap;
+ TextureMap mTextureDatas;
+ int64_t mNextTextureId = -1;
+ nsRefPtrHashtable<nsPtrHashKey<void>, gfx::DataSourceSurface> mDataSurfaces;
+ gfx::ReferencePtr mMappedSurface;
+ UniquePtr<gfx::DataSourceSurface::ScopedMap> mPreparedMap;
+ typedef std::unordered_map<int64_t, UniquePtr<SurfaceDescriptor>>
+ DescriptorMap;
+ DescriptorMap mSurfaceDescriptors;
+ Monitor mSurfaceDescriptorsMonitor{
+ "CanvasTranslator::mSurfaceDescriptorsMonitor"};
+ Atomic<bool> mDeactivated{false};
+ bool mIsInTransaction = false;
+ bool mDeviceResetInProgress = false;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CanvasTranslator_h
diff --git a/gfx/layers/ipc/CompositableForwarder.cpp b/gfx/layers/ipc/CompositableForwarder.cpp
new file mode 100644
index 0000000000..369dff086a
--- /dev/null
+++ b/gfx/layers/ipc/CompositableForwarder.cpp
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableForwarder.h"
+
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla::layers {
+CompositableForwarder::CompositableForwarder() = default;
+CompositableForwarder::~CompositableForwarder() = default;
+} // namespace mozilla::layers
diff --git a/gfx/layers/ipc/CompositableForwarder.h b/gfx/layers/ipc/CompositableForwarder.h
new file mode 100644
index 0000000000..412af3e859
--- /dev/null
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_COMPOSITABLEFORWARDER
+#define MOZILLA_LAYERS_COMPOSITABLEFORWARDER
+
+#include <stdint.h> // for int32_t, uint32_t, uint64_t
+#include "mozilla/Assertions.h" // for AssertionConditionType, MOZ_ASSERT, MOZ_ASSERT_HELPER1
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/layers/KnowsCompositor.h" // for KnowsCompositor
+#include "nsRect.h" // for nsIntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+namespace layers {
+class CompositableClient;
+class CompositableHandle;
+class ImageContainer;
+class PTextureChild;
+class ShadowLayerForwarder;
+class SurfaceDescriptorTiles;
+class TextureClient;
+class ThebesBufferData;
+
+/**
+ * A transaction is a set of changes that happenned on the content side, that
+ * should be sent to the compositor side.
+ * CompositableForwarder is an interface to manage a transaction of
+ * compositable objetcs.
+ *
+ * ShadowLayerForwarder is an example of a CompositableForwarder (that can
+ * additionally forward modifications of the Layer tree).
+ * ImageBridgeChild is another CompositableForwarder.
+ *
+ * CompositableForwarder implements KnowsCompositor for simplicity as all
+ * implementations of CompositableForwarder currently also implement
+ * KnowsCompositor. This dependency could be split if we add new use cases.
+ */
+class CompositableForwarder : public KnowsCompositor {
+ public:
+ CompositableForwarder();
+ ~CompositableForwarder();
+
+ /**
+ * Setup the IPDL actor for aCompositable to be part of layers
+ * transactions.
+ */
+ virtual void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer = nullptr) = 0;
+
+ /**
+ * Tell the CompositableHost on the compositor side what TiledLayerBuffer to
+ * use for the next composition.
+ */
+ virtual void UseTiledLayerBuffer(
+ CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
+
+ /**
+ * Communicate to the compositor that aRegion in the texture identified by
+ * aCompositable and aIdentifier has been updated to aThebesBuffer.
+ */
+ virtual void UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) = 0;
+
+ virtual void ReleaseCompositable(const CompositableHandle& aHandle) = 0;
+ virtual bool DestroyInTransaction(PTextureChild* aTexture) = 0;
+
+ /**
+ * Tell the CompositableHost on the compositor side to remove the texture
+ * from the CompositableHost.
+ * This function does not delete the TextureHost corresponding to the
+ * TextureClient passed in parameter.
+ * When the TextureClient has TEXTURE_DEALLOCATE_CLIENT flag,
+ * the transaction becomes synchronous.
+ */
+ virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) = 0;
+
+ struct TimedTextureClient {
+ TimedTextureClient()
+ : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {}
+
+ TextureClient* mTextureClient;
+ TimeStamp mTimeStamp;
+ nsIntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+ /**
+ * Tell the CompositableHost on the compositor side what textures to use for
+ * the next composition.
+ */
+ virtual void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) = 0;
+ virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aClientOnBlack,
+ TextureClient* aClientOnWhite) = 0;
+
+ virtual void UpdateFwdTransactionId() = 0;
+ virtual uint64_t GetFwdTransactionId() = 0;
+
+ virtual bool InForwarderThread() = 0;
+
+ void AssertInForwarderThread() { MOZ_ASSERT(InForwarderThread()); }
+
+ static uint32_t GetMaxFileDescriptorsPerMessage();
+
+ virtual ShadowLayerForwarder* AsLayerForwarder() { return nullptr; }
+
+ protected:
+ nsTArray<RefPtr<TextureClient>> mTexturesToRemove;
+ nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositableTransactionParent.cpp b/gfx/layers/ipc/CompositableTransactionParent.cpp
new file mode 100644
index 0000000000..5ca2453c54
--- /dev/null
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableTransactionParent.h"
+#include "CompositableHost.h" // for CompositableParent, etc
+#include "CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "GLContext.h" // for GLContext
+#include "Layers.h" // for Layer
+#include "RenderTrace.h" // for RenderTraceInvalidateEnd, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ContentHost.h" // for ContentHostBase
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/TiledContentHost.h"
+#include "mozilla/layers/PaintedLayerComposite.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/Unused.h"
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsRegion.h" // for nsIntRegion
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/layers/AndroidHardwareBuffer.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+class ClientTiledLayerBuffer;
+class Compositor;
+
+// This function can in some cases fail and return false without it being a bug.
+// This can theoretically happen if the ImageBridge sends frames before
+// we created the layer tree. Since we can't enforce that the layer
+// tree is already created before ImageBridge operates, there isn't much
+// we can do about it, but in practice it is very rare.
+// Typically when a tab with a video is dragged from a window to another,
+// there can be a short time when the video is still sending frames
+// asynchonously while the layer tree is not reconstructed. It's not a
+// big deal.
+// Note that Layers transactions do not need to call this because they always
+// schedule the composition, in LayerManagerComposite::EndTransaction.
+static bool ScheduleComposition(CompositableHost* aCompositable) {
+ uint64_t id = aCompositable->GetCompositorBridgeID();
+ if (!id) {
+ return false;
+ }
+ CompositorBridgeParent* cp =
+ CompositorBridgeParent::GetCompositorBridgeParent(id);
+ if (!cp) {
+ return false;
+ }
+ cp->ScheduleComposition();
+ return true;
+}
+
+bool CompositableParentManager::ReceiveCompositableUpdate(
+ const CompositableOperation& aEdit) {
+ // Ignore all operations on compositables created on stale compositors. We
+ // return true because the child is unable to handle errors.
+ RefPtr<CompositableHost> compositable =
+ FindCompositable(aEdit.compositable());
+ if (!compositable) {
+ return false;
+ }
+ return ReceiveCompositableUpdate(aEdit.detail(), WrapNotNull(compositable));
+}
+
+bool CompositableParentManager::ReceiveCompositableUpdate(
+ const CompositableOperationDetail& aDetail,
+ NotNull<CompositableHost*> aCompositable) {
+ if (TextureSourceProvider* provider =
+ aCompositable->GetTextureSourceProvider()) {
+ if (!provider->IsValid()) {
+ return false;
+ }
+ }
+
+ switch (aDetail.type()) {
+ case CompositableOperationDetail::TOpPaintTextureRegion: {
+ MOZ_LAYERS_LOG(("[ParentSide] Paint PaintedLayer"));
+
+ const OpPaintTextureRegion& op = aDetail.get_OpPaintTextureRegion();
+ Layer* layer = aCompositable->GetLayer();
+ if (!layer || layer->GetType() != Layer::TYPE_PAINTED) {
+ return false;
+ }
+ PaintedLayerComposite* thebes =
+ static_cast<PaintedLayerComposite*>(layer);
+
+ const ThebesBufferData& bufferData = op.bufferData();
+
+ RenderTraceInvalidateStart(thebes, "FF00FF",
+ op.updatedRegion().GetBounds());
+
+ if (!aCompositable->UpdateThebes(bufferData, op.updatedRegion(),
+ thebes->GetValidRegion())) {
+ return false;
+ }
+
+ RenderTraceInvalidateEnd(thebes, "FF00FF");
+ break;
+ }
+ case CompositableOperationDetail::TOpUseTiledLayerBuffer: {
+ MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer"));
+ const OpUseTiledLayerBuffer& op = aDetail.get_OpUseTiledLayerBuffer();
+ TiledContentHost* tiledHost = aCompositable->AsTiledContentHost();
+
+ NS_ASSERTION(tiledHost, "The compositable is not tiled");
+
+ const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor();
+
+ bool success = tiledHost->UseTiledLayerBuffer(this, tileDesc);
+
+ const nsTArray<TileDescriptor>& tileDescriptors = tileDesc.tiles();
+ for (size_t i = 0; i < tileDescriptors.Length(); i++) {
+ const TileDescriptor& tileDesc = tileDescriptors[i];
+ if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) {
+ continue;
+ }
+ const TexturedTileDescriptor& texturedDesc =
+ tileDesc.get_TexturedTileDescriptor();
+ RefPtr<TextureHost> texture =
+ TextureHost::AsTextureHost(texturedDesc.textureParent());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ if (texturedDesc.textureOnWhiteParent().isSome()) {
+ texture = TextureHost::AsTextureHost(
+ texturedDesc.textureOnWhiteParent().ref());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ }
+ }
+ if (!success) {
+ return false;
+ }
+ break;
+ }
+ case CompositableOperationDetail::TOpRemoveTexture: {
+ const OpRemoveTexture& op = aDetail.get_OpRemoveTexture();
+
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent());
+
+ MOZ_ASSERT(tex.get());
+ aCompositable->RemoveTextureHost(tex);
+ break;
+ }
+ case CompositableOperationDetail::TOpUseTexture: {
+ const OpUseTexture& op = aDetail.get_OpUseTexture();
+
+ AutoTArray<CompositableHost::TimedTexture, 4> textures;
+ for (auto& timedTexture : op.textures()) {
+ CompositableHost::TimedTexture* t = textures.AppendElement();
+ t->mTexture = TextureHost::AsTextureHost(timedTexture.textureParent());
+ MOZ_ASSERT(t->mTexture);
+ t->mTimeStamp = timedTexture.timeStamp();
+ t->mPictureRect = timedTexture.picture();
+ t->mFrameID = timedTexture.frameID();
+ t->mProducerID = timedTexture.producerID();
+ if (timedTexture.readLocked()) {
+ t->mTexture->SetReadLocked();
+ }
+ }
+ if (textures.Length() > 0) {
+ aCompositable->UseTextureHost(textures);
+
+ for (auto& timedTexture : op.textures()) {
+ RefPtr<TextureHost> texture =
+ TextureHost::AsTextureHost(timedTexture.textureParent());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ }
+ }
+
+ if (UsesImageBridge() && aCompositable->GetLayer()) {
+ ScheduleComposition(aCompositable);
+ }
+ break;
+ }
+ case CompositableOperationDetail::TOpUseComponentAlphaTextures: {
+ const OpUseComponentAlphaTextures& op =
+ aDetail.get_OpUseComponentAlphaTextures();
+ RefPtr<TextureHost> texOnBlack =
+ TextureHost::AsTextureHost(op.textureOnBlackParent());
+ RefPtr<TextureHost> texOnWhite =
+ TextureHost::AsTextureHost(op.textureOnWhiteParent());
+ if (op.readLockedBlack()) {
+ texOnBlack->SetReadLocked();
+ }
+ if (op.readLockedWhite()) {
+ texOnWhite->SetReadLocked();
+ }
+
+ MOZ_ASSERT(texOnBlack && texOnWhite);
+ aCompositable->UseComponentAlphaTextures(texOnBlack, texOnWhite);
+
+ if (texOnBlack) {
+ texOnBlack->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texOnBlack->NumCompositableRefs() > 0);
+ }
+
+ if (texOnWhite) {
+ texOnWhite->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texOnWhite->NumCompositableRefs() > 0);
+ }
+
+ if (UsesImageBridge()) {
+ ScheduleComposition(aCompositable);
+ }
+ break;
+ }
+ case CompositableOperationDetail::TOpDeliverAcquireFence: {
+ const OpDeliverAcquireFence& op = aDetail.get_OpDeliverAcquireFence();
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent());
+ MOZ_ASSERT(tex.get());
+ MOZ_ASSERT(tex->AsAndroidHardwareBufferTextureHost());
+
+ auto fenceFd = op.fenceFd();
+ tex->SetAcquireFence(std::move(fenceFd));
+ break;
+ }
+ default: {
+ MOZ_ASSERT(false, "bad type");
+ }
+ }
+
+ return true;
+}
+
+void CompositableParentManager::DestroyActor(const OpDestroy& aOp) {
+ switch (aOp.type()) {
+ case OpDestroy::TPTextureParent: {
+ auto actor = aOp.get_PTextureParent();
+ TextureHost::ReceivedDestroy(actor);
+ break;
+ }
+ case OpDestroy::TCompositableHandle: {
+ ReleaseCompositable(aOp.get_CompositableHandle());
+ break;
+ }
+ default: {
+ MOZ_ASSERT(false, "unsupported type");
+ }
+ }
+}
+
+RefPtr<CompositableHost> CompositableParentManager::AddCompositable(
+ const CompositableHandle& aHandle, const TextureInfo& aInfo,
+ bool aUseWebRender) {
+ if (mCompositables.find(aHandle.Value()) != mCompositables.end()) {
+ NS_ERROR("Client should not allocate duplicate handles");
+ return nullptr;
+ }
+ if (!aHandle) {
+ NS_ERROR("Client should not allocate 0 as a handle");
+ return nullptr;
+ }
+
+ RefPtr<CompositableHost> host =
+ CompositableHost::Create(aInfo, aUseWebRender);
+ if (!host) {
+ return nullptr;
+ }
+
+ mCompositables[aHandle.Value()] = host;
+ return host;
+}
+
+RefPtr<CompositableHost> CompositableParentManager::FindCompositable(
+ const CompositableHandle& aHandle, bool aAllowDisablingWebRender) {
+ auto iter = mCompositables.find(aHandle.Value());
+ if (iter == mCompositables.end()) {
+ return nullptr;
+ }
+
+ RefPtr<CompositableHost> host = iter->second;
+ if (!aAllowDisablingWebRender) {
+ return host;
+ }
+
+ if (!host->AsWebRenderImageHost() || !host->GetAsyncRef()) {
+ return host;
+ }
+
+ // Try to replace WebRenderImageHost of ImageBridge to ImageHost.
+ RefPtr<CompositableHost> newHost = CompositableHost::Create(
+ host->GetTextureInfo(), /* aUseWebRender */ false);
+ if (!newHost || !newHost->AsImageHost()) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return host;
+ }
+
+ newHost->SetAsyncRef(host->GetAsyncRef());
+ mCompositables[aHandle.Value()] = newHost;
+
+ return newHost;
+}
+
+void CompositableParentManager::ReleaseCompositable(
+ const CompositableHandle& aHandle) {
+ auto iter = mCompositables.find(aHandle.Value());
+ if (iter == mCompositables.end()) {
+ return;
+ }
+
+ RefPtr<CompositableHost> host = iter->second;
+ mCompositables.erase(iter);
+
+ host->Detach(nullptr, CompositableHost::FORCE_DETACH);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositableTransactionParent.h b/gfx/layers/ipc/CompositableTransactionParent.h
new file mode 100644
index 0000000000..8912f50bce
--- /dev/null
+++ b/gfx/layers/ipc/CompositableTransactionParent.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
+#define MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
+
+#include <vector> // for vector
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/NotNull.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
+#include "mozilla/layers/TextureClient.h"
+#include "CompositableHost.h"
+
+namespace mozilla {
+namespace layers {
+
+// Since PCompositble has two potential manager protocols, we can't just call
+// the Manager() method usually generated when there's one manager protocol,
+// so both manager protocols implement this and we keep a reference to them
+// through this interface.
+class CompositableParentManager : public HostIPCAllocator {
+ public:
+ CompositableParentManager() = default;
+
+ void DestroyActor(const OpDestroy& aOp);
+
+ void UpdateFwdTransactionId(uint64_t aTransactionId) {
+ MOZ_ASSERT(mFwdTransactionId < aTransactionId);
+ mFwdTransactionId = aTransactionId;
+ }
+
+ uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
+
+ RefPtr<CompositableHost> AddCompositable(const CompositableHandle& aHandle,
+ const TextureInfo& aInfo,
+ bool aUseWebRender);
+ RefPtr<CompositableHost> FindCompositable(
+ const CompositableHandle& aHandle, bool aAllowDisablingWebRender = false);
+
+ protected:
+ /**
+ * Handle the IPDL messages that affect PCompositable actors.
+ */
+ bool ReceiveCompositableUpdate(const CompositableOperation& aEdit);
+ bool ReceiveCompositableUpdate(const CompositableOperationDetail& aDetail,
+ NotNull<CompositableHost*> aCompositable);
+
+ void ReleaseCompositable(const CompositableHandle& aHandle);
+
+ uint64_t mFwdTransactionId = 0;
+
+ /**
+ * Mapping form IDs to CompositableHosts.
+ */
+ std::map<uint64_t, RefPtr<CompositableHost>> mCompositables;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositorBench.cpp b/gfx/layers/ipc/CompositorBench.cpp
new file mode 100644
index 0000000000..0fb4d8012b
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBench.cpp
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorBench.h"
+
+#ifdef MOZ_COMPOSITOR_BENCH
+# include "mozilla/gfx/2D.h"
+# include "mozilla/layers/Compositor.h"
+# include "mozilla/layers/Effects.h"
+# include "mozilla/ProfilerMarkers.h"
+# include "mozilla/StaticPrefs_layers.h"
+# include "mozilla/TimeStamp.h"
+# include <math.h>
+
+# define TEST_STEPS 1000
+# define DURATION_THRESHOLD 30
+# define THRESHOLD_ABORT_COUNT 5
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static float SimplePseudoRandom(int aStep, int aCount) {
+ srand(aStep * 1000 + aCount);
+ return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
+}
+
+class BenchTest {
+ public:
+ BenchTest(const char* aTestName) : mTestName(aTestName) {}
+
+ virtual ~BenchTest() = default;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {}
+ virtual void Teardown(Compositor* aCompositor) {}
+ virtual void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) = 0;
+
+ const char* ToString() { return mTestName; }
+
+ private:
+ const char* mTestName;
+};
+
+static void DrawFrameTrivialQuad(Compositor* aCompositor,
+ const gfx::Rect& aScreenRect, size_t aStep,
+ const EffectChain& effects) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
+ (int)(i / aScreenRect.height), 1, 1);
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix transform2d;
+
+ gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+}
+
+static void DrawFrameStressQuad(Compositor* aCompositor,
+ const gfx::Rect& aScreenRect, size_t aStep,
+ const EffectChain& effects) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ const gfx::Rect& rect =
+ gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
+ aScreenRect.height * SimplePseudoRandom(i, 1),
+ aScreenRect.width * SimplePseudoRandom(i, 2),
+ aScreenRect.height * SimplePseudoRandom(i, 3));
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix transform2d;
+ transform2d = transform2d.PreRotate(SimplePseudoRandom(i, 4) * 70.f);
+
+ gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+}
+
+class EffectSolidColorBench : public BenchTest {
+ public:
+ EffectSolidColorBench()
+ : BenchTest("EffectSolidColorBench (clear frame with EffectSolidColor)") {
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ float tmp;
+ float red = modff(aStep * 0.03f, &tmp);
+ EffectChain effects;
+ effects.mPrimaryEffect =
+ new EffectSolidColor(gfx::DeviceColor(red, 0.4f, 0.4f, 1.0f));
+
+ const gfx::Rect& rect = aScreenRect;
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix4x4 transform;
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+};
+
+class EffectSolidColorTrivialBench : public BenchTest {
+ public:
+ EffectSolidColorTrivialBench()
+ : BenchTest("EffectSolidColorTrivialBench (10s 1x1 EffectSolidColor)") {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ float tmp;
+ float red = modff(i * 0.03f, &tmp);
+ EffectChain effects;
+ return MakeAndAddRef<EffectSolidColor>(
+ gfx::DeviceColor(red, 0.4f, 0.4f, 1.0f));
+ }
+};
+
+class EffectSolidColorStressBench : public BenchTest {
+ public:
+ EffectSolidColorStressBench()
+ : BenchTest(
+ "EffectSolidColorStressBench (10s various EffectSolidColor)") {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ float tmp;
+ float red = modff(i * 0.03f, &tmp);
+ EffectChain effects;
+ return MakeAndAddRef<EffectSolidColor>(
+ gfx::DeviceColor(red, 0.4f, 0.4f, 1.0f));
+ }
+};
+
+class UploadBench : public BenchTest {
+ public:
+ UploadBench() : BenchTest("Upload Bench (10s 256x256 upload)") {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ int w = 256;
+ int h = 256;
+ mBuf = (uint32_t*)malloc(w * h * sizeof(uint32_t));
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h),
+ SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ mTexture->Update(mSurface);
+ }
+ }
+};
+
+class TrivialTexturedQuadBench : public BenchTest {
+ public:
+ TrivialTexturedQuadBench()
+ : BenchTest("Trvial Textured Quad (10s 256x256 quads)") {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ size_t w = 256;
+ size_t h = 256;
+ mBuf = (uint32_t*)malloc(w * h * sizeof(uint32_t));
+ for (size_t i = 0; i < w * h; i++) {
+ mBuf[i] = 0xFF00008F;
+ }
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h),
+ SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ mTexture->Update(mSurface);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture,
+ SamplingFilter::POINT, true);
+ }
+};
+
+class StressTexturedQuadBench : public BenchTest {
+ public:
+ StressTexturedQuadBench()
+ : BenchTest("Stress Textured Quad (10s 256x256 quads)") {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ size_t w = 256;
+ size_t h = 256;
+ mBuf = (uint32_t*)malloc(w * h * sizeof(uint32_t));
+ for (size_t i = 0; i < w * h; i++) {
+ mBuf[i] = 0xFF00008F;
+ }
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h),
+ SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ mTexture->Update(mSurface);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ virtual already_AddRefed<Effect> CreateEffect(size_t i) {
+ return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture,
+ SamplingFilter::POINT, true);
+ }
+};
+
+static void RunCompositorBench(Compositor* aCompositor,
+ const gfx::Rect& aScreenRect) {
+ std::vector<BenchTest*> tests;
+
+ tests.push_back(new EffectSolidColorBench());
+ tests.push_back(new UploadBench());
+ tests.push_back(new EffectSolidColorTrivialBench());
+ tests.push_back(new EffectSolidColorStressBench());
+ tests.push_back(new TrivialTexturedQuadBench());
+ tests.push_back(new StressTexturedQuadBench());
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ BenchTest* test = tests[i];
+ std::vector<TimeDuration> results;
+ int testsOverThreshold = 0;
+ PROFILER_MARKER_UNTYPED(
+ ProfilerString8View::WrapNullTerminatedString(test->ToString()),
+ GRAPHICS);
+ for (size_t j = 0; j < TEST_STEPS; j++) {
+ test->Setup(aCompositor, j);
+
+ TimeStamp start = TimeStamp::Now();
+ IntRect screenRect(aScreenRect.x, aScreenRect.y, aScreenRect.width,
+ aScreenRect.height);
+ aCompositor->BeginFrame(IntRect(screenRect.x, screenRect.y,
+ screenRect.width, screenRect.height),
+ nullptr, aScreenRect, nullptr, nullptr);
+
+ test->DrawFrame(aCompositor, aScreenRect, j);
+
+ aCompositor->EndFrame();
+ results.push_back(TimeStamp::Now() - start);
+
+ if (results[j].ToMilliseconds() > DURATION_THRESHOLD) {
+ testsOverThreshold++;
+ if (testsOverThreshold == THRESHOLD_ABORT_COUNT) {
+ test->Teardown(aCompositor);
+ break;
+ }
+ } else {
+ testsOverThreshold = 0;
+ }
+ test->Teardown(aCompositor);
+ }
+
+ printf_stderr("%s\n", test->ToString());
+ printf_stderr("Run step, Time (ms)\n");
+ for (size_t j = 0; j < results.size(); j++) {
+ printf_stderr("%i,%f\n", j, (float)results[j].ToMilliseconds());
+ }
+ printf_stderr("\n", test->ToString());
+ }
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ delete tests[i];
+ }
+}
+
+void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect) {
+ static bool sRanBenchmark = false;
+ bool wantBenchmark = StaticPrefs::layers_bench_enabled();
+ if (wantBenchmark && !sRanBenchmark) {
+ RunCompositorBench(aCompositor, aScreenRect);
+ }
+ sRanBenchmark = wantBenchmark;
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositorBench.h b/gfx/layers/ipc/CompositorBench.h
new file mode 100644
index 0000000000..4a3c5874e9
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBench.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBench_h
+#define mozilla_layers_CompositorBench_h
+
+#include "mozilla/gfx/Rect.h" // for Rect
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+// Uncomment this line to rebuild with compositor bench.
+// #define MOZ_COMPOSITOR_BENCH
+
+#ifdef MOZ_COMPOSITOR_BENCH
+void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect);
+#else
+static inline void CompositorBench(Compositor* aCompositor,
+ const gfx::IntRect& aScreenRect) {}
+#endif
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp
new file mode 100644
index 0000000000..442989a1af
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -0,0 +1,1310 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include <stddef.h> // for size_t
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "base/task.h" // for NewRunnableMethod, etc
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/layers/CompositorManagerChild.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/APZCTreeManagerChild.h"
+#include "mozilla/layers/CanvasChild.h"
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/PaintThread.h"
+#include "mozilla/layers/PLayerTransactionChild.h"
+#include "mozilla/layers/PTextureChild.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/TextureClientPool.h" // for TextureClientPool
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/layers/SyncObject.h" // for SyncObjectClient
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/webgpu/WebGPUChild.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Telemetry.h"
+#include "gfxConfig.h"
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "FrameLayerBuilder.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Unused.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsThreadUtils.h"
+#if defined(XP_WIN)
+# include "WinUtils.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetChild.h"
+#endif
+#include "VsyncSource.h"
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/layers/AndroidHardwareBuffer.h"
+#endif
+
+using mozilla::Unused;
+using mozilla::gfx::GPUProcessManager;
+using mozilla::layers::LayerTransactionChild;
+
+namespace mozilla {
+namespace layers {
+
+static int sShmemCreationCounter = 0;
+
+static void ResetShmemCounter() { sShmemCreationCounter = 0; }
+
+static void ShmemAllocated(CompositorBridgeChild* aProtocol) {
+ sShmemCreationCounter++;
+ if (sShmemCreationCounter > 256) {
+ aProtocol->SendSyncWithCompositor();
+ ResetShmemCounter();
+ MOZ_PERFORMANCE_WARNING(
+ "gfx", "The number of shmem allocations is too damn high!");
+ }
+}
+
+static StaticRefPtr<CompositorBridgeChild> sCompositorBridge;
+
+Atomic<int32_t> KnowsCompositor::sSerialCounter(0);
+
+CompositorBridgeChild::CompositorBridgeChild(CompositorManagerChild* aManager)
+ : mCompositorManager(aManager),
+ mIdNamespace(0),
+ mResourceId(0),
+ mCanSend(false),
+ mActorDestroyed(false),
+ mFwdTransactionId(0),
+ mThread(NS_GetCurrentThread()),
+ mProcessToken(0),
+ mSectionAllocator(nullptr),
+ mPaintLock("CompositorBridgeChild.mPaintLock"),
+ mTotalAsyncPaints(0),
+ mOutstandingAsyncPaints(0),
+ mOutstandingAsyncEndTransaction(false),
+ mIsDelayingForAsyncPaints(false),
+ mSlowFlushCount(0),
+ mTotalFlushCount(0) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+CompositorBridgeChild::~CompositorBridgeChild() {
+ if (mCanSend) {
+ gfxCriticalError() << "CompositorBridgeChild was not deinitialized";
+ }
+}
+
+bool CompositorBridgeChild::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void CompositorBridgeChild::PrepareFinalDestroy() {
+ // Because of medium high priority DidComposite, we need to repost to
+ // medium high priority queue to ensure the actor is destroyed after possible
+ // pending DidComposite message.
+ nsCOMPtr<nsIRunnable> runnable =
+ NewRunnableMethod("CompositorBridgeChild::AfterDestroy", this,
+ &CompositorBridgeChild::AfterDestroy);
+ NS_DispatchToCurrentThreadQueue(runnable.forget(),
+ EventQueuePriority::MediumHigh);
+}
+
+void CompositorBridgeChild::AfterDestroy() {
+ // Note that we cannot rely upon mCanSend here because we already set that to
+ // false to prevent normal IPDL calls from being made after SendWillClose.
+ // The only time we should not issue Send__delete__ is if the actor is already
+ // destroyed, e.g. the compositor process crashed.
+ if (!mActorDestroyed) {
+ Send__delete__(this);
+ mActorDestroyed = true;
+ }
+
+ if (mCanvasChild) {
+ mCanvasChild->Destroy();
+ }
+
+ if (sCompositorBridge == this) {
+ sCompositorBridge = nullptr;
+ }
+}
+
+void CompositorBridgeChild::Destroy() {
+ // This must not be called from the destructor!
+ mTexturesWaitingNotifyNotUsed.clear();
+
+ // Destroying the layer manager may cause all sorts of things to happen, so
+ // let's make sure there is still a reference to keep this alive whatever
+ // happens.
+ RefPtr<CompositorBridgeChild> selfRef = this;
+
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Destroy();
+ }
+
+ if (mSectionAllocator) {
+ delete mSectionAllocator;
+ mSectionAllocator = nullptr;
+ }
+
+ if (mLayerManager) {
+ mLayerManager->Destroy();
+ mLayerManager = nullptr;
+ }
+
+ // Flush async paints before we destroy texture data.
+ FlushAsyncPaints();
+
+ if (!mCanSend) {
+ // We may have already called destroy but still have lingering references
+ // or CompositorBridgeChild::ActorDestroy was called. Ensure that we do our
+ // post destroy clean up no matter what. It is safe to call multiple times.
+ NS_GetCurrentThread()->Dispatch(
+ NewRunnableMethod("CompositorBridgeChild::PrepareFinalDestroy", selfRef,
+ &CompositorBridgeChild::PrepareFinalDestroy));
+ return;
+ }
+
+ AutoTArray<PLayerTransactionChild*, 16> transactions;
+ ManagedPLayerTransactionChild(transactions);
+ for (int i = transactions.Length() - 1; i >= 0; --i) {
+ RefPtr<LayerTransactionChild> layers =
+ static_cast<LayerTransactionChild*>(transactions[i]);
+ layers->Destroy();
+ }
+
+ AutoTArray<PWebRenderBridgeChild*, 16> wrBridges;
+ ManagedPWebRenderBridgeChild(wrBridges);
+ for (int i = wrBridges.Length() - 1; i >= 0; --i) {
+ RefPtr<WebRenderBridgeChild> wrBridge =
+ static_cast<WebRenderBridgeChild*>(wrBridges[i]);
+ wrBridge->Destroy(/* aIsSync */ false);
+ }
+
+ AutoTArray<PAPZChild*, 16> apzChildren;
+ ManagedPAPZChild(apzChildren);
+ for (PAPZChild* child : apzChildren) {
+ Unused << child->SendDestroy();
+ }
+
+ AutoTArray<PWebGPUChild*, 16> webGPUChildren;
+ ManagedPWebGPUChild(webGPUChildren);
+ for (PWebGPUChild* child : webGPUChildren) {
+ Unused << child->SendShutdown();
+ }
+
+ const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild();
+ for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) {
+ RefPtr<TextureClient> texture =
+ TextureClient::AsTextureClient(iter.Get()->GetKey());
+
+ if (texture) {
+ texture->Destroy();
+ }
+ }
+
+ // The WillClose message is synchronous, so we know that after it returns
+ // any messages sent by the above code will have been processed on the
+ // other side.
+ SendWillClose();
+ mCanSend = false;
+
+ // We no longer care about unexpected shutdowns, in the remote process case.
+ mProcessToken = 0;
+
+ // The call just made to SendWillClose can result in IPC from the
+ // CompositorBridgeParent to the CompositorBridgeChild (e.g. caused by the
+ // destruction of shared memory). We need to ensure this gets processed by the
+ // CompositorBridgeChild before it gets destroyed. It suffices to ensure that
+ // events already in the thread get processed before the
+ // CompositorBridgeChild is destroyed, so we add a task to the thread to
+ // handle compositor destruction.
+
+ // From now on we can't send any message message.
+ NS_GetCurrentThread()->Dispatch(
+ NewRunnableMethod("CompositorBridgeChild::PrepareFinalDestroy", selfRef,
+ &CompositorBridgeChild::PrepareFinalDestroy));
+}
+
+// static
+void CompositorBridgeChild::ShutDown() {
+ if (sCompositorBridge) {
+ sCompositorBridge->Destroy();
+ SpinEventLoopUntil([&]() { return !sCompositorBridge; });
+ }
+}
+
+bool CompositorBridgeChild::LookupCompositorFrameMetrics(
+ const ScrollableLayerGuid::ViewID aId, FrameMetrics& aFrame) {
+ SharedFrameMetricsData* data = mFrameMetricsTable.Get(aId);
+ if (data) {
+ data->CopyFrameMetrics(&aFrame);
+ return true;
+ }
+ return false;
+}
+
+void CompositorBridgeChild::InitForContent(uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aNamespace);
+
+ if (RefPtr<CompositorBridgeChild> old = sCompositorBridge.forget()) {
+ // Note that at this point, ActorDestroy may not have been called yet,
+ // meaning mCanSend is still true. In this case we will try to send a
+ // synchronous WillClose message to the parent, and will certainly get
+ // a false result and a MsgDropped processing error. This is okay.
+ old->Destroy();
+ }
+
+ mCanSend = true;
+ mIdNamespace = aNamespace;
+
+ sCompositorBridge = this;
+}
+
+void CompositorBridgeChild::InitForWidget(uint64_t aProcessToken,
+ LayerManager* aLayerManager,
+ uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aProcessToken);
+ MOZ_ASSERT(aLayerManager);
+ MOZ_ASSERT(aNamespace);
+
+ mCanSend = true;
+ mProcessToken = aProcessToken;
+ mLayerManager = aLayerManager;
+ mIdNamespace = aNamespace;
+}
+
+/*static*/
+CompositorBridgeChild* CompositorBridgeChild::Get() {
+ // This is only expected to be used in child processes. While the parent
+ // process does have CompositorBridgeChild instances, it has _multiple_ (one
+ // per window), and therefore there is no global singleton available.
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ return sCompositorBridge;
+}
+
+// static
+bool CompositorBridgeChild::ChildProcessHasCompositorBridge() {
+ return sCompositorBridge != nullptr;
+}
+
+/* static */
+bool CompositorBridgeChild::CompositorIsInGPUProcess() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (XRE_IsParentProcess()) {
+ return !!GPUProcessManager::Get()->GetGPUChild();
+ }
+
+ MOZ_ASSERT(XRE_IsContentProcess());
+ CompositorBridgeChild* bridge = CompositorBridgeChild::Get();
+ if (!bridge) {
+ return false;
+ }
+
+ return bridge->OtherPid() != dom::ContentChild::GetSingleton()->OtherPid();
+}
+
+PLayerTransactionChild* CompositorBridgeChild::AllocPLayerTransactionChild(
+ const nsTArray<LayersBackend>& aBackendHints, const LayersId& aId) {
+ LayerTransactionChild* c = new LayerTransactionChild(aId);
+ c->AddIPDLReference();
+
+ return c;
+}
+
+bool CompositorBridgeChild::DeallocPLayerTransactionChild(
+ PLayerTransactionChild* actor) {
+ LayersId childId = static_cast<LayerTransactionChild*>(actor)->GetId();
+ ClearSharedFrameMetricsData(childId);
+ static_cast<LayerTransactionChild*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvInvalidateLayers(
+ const LayersId& aLayersId) {
+ if (mLayerManager) {
+ MOZ_ASSERT(!aLayersId.IsValid());
+ FrameLayerBuilder::InvalidateAllLayers(mLayerManager);
+ } else if (aLayersId.IsValid()) {
+ if (dom::BrowserChild* child = dom::BrowserChild::GetFrom(aLayersId)) {
+ child->InvalidateLayers();
+ }
+ }
+ return IPC_OK();
+}
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+static void CalculatePluginClip(
+ const LayoutDeviceIntRect& aBounds,
+ const nsTArray<LayoutDeviceIntRect>& aPluginClipRects,
+ const LayoutDeviceIntPoint& aContentOffset,
+ const LayoutDeviceIntRegion& aParentLayerVisibleRegion,
+ nsTArray<LayoutDeviceIntRect>& aResult, LayoutDeviceIntRect& aVisibleBounds,
+ bool& aPluginIsVisible) {
+ aPluginIsVisible = true;
+ LayoutDeviceIntRegion contentVisibleRegion;
+ // aPluginClipRects (plugin widget origin) - contains *visible* rects
+ for (uint32_t idx = 0; idx < aPluginClipRects.Length(); idx++) {
+ LayoutDeviceIntRect rect = aPluginClipRects[idx];
+ // shift to content origin
+ rect.MoveBy(aBounds.X(), aBounds.Y());
+ // accumulate visible rects
+ contentVisibleRegion.OrWith(rect);
+ }
+ // apply layers clip (window origin)
+ LayoutDeviceIntRegion region = aParentLayerVisibleRegion;
+ region.MoveBy(-aContentOffset.x, -aContentOffset.y);
+ contentVisibleRegion.AndWith(region);
+ if (contentVisibleRegion.IsEmpty()) {
+ aPluginIsVisible = false;
+ return;
+ }
+ // shift to plugin widget origin
+ contentVisibleRegion.MoveBy(-aBounds.X(), -aBounds.Y());
+ for (auto iter = contentVisibleRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const LayoutDeviceIntRect& rect = iter.Get();
+ aResult.AppendElement(rect);
+ aVisibleBounds.UnionRect(aVisibleBounds, rect);
+ }
+}
+#endif
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvUpdatePluginConfigurations(
+ const LayoutDeviceIntPoint& aContentOffset,
+ const LayoutDeviceIntRegion& aParentLayerVisibleRegion,
+ nsTArray<PluginWindowData>&& aPlugins) {
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+ MOZ_ASSERT_UNREACHABLE(
+ "CompositorBridgeChild::RecvUpdatePluginConfigurations"
+ " calls unexpected on this platform.");
+ return IPC_FAIL_NO_REASON(this);
+#else
+ // Now that we are on the main thread, update plugin widget config.
+ // This should happen a little before we paint to the screen assuming
+ // the main thread is running freely.
+ DebugOnly<nsresult> rv;
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Tracks visible plugins we update, so we can hide any plugins we don't.
+ nsTArray<uintptr_t> visiblePluginIds;
+ nsIWidget* parent = nullptr;
+ for (uint32_t pluginsIdx = 0; pluginsIdx < aPlugins.Length(); pluginsIdx++) {
+ nsIWidget* widget = nsIWidget::LookupRegisteredPluginWindow(
+ aPlugins[pluginsIdx].windowId());
+ if (!widget) {
+ NS_WARNING("Unexpected, plugin id not found!");
+ continue;
+ }
+ if (!parent) {
+ parent = widget->GetParent();
+ }
+ bool isVisible = aPlugins[pluginsIdx].visible();
+ if (widget && !widget->Destroyed()) {
+ LayoutDeviceIntRect bounds;
+ LayoutDeviceIntRect visibleBounds;
+ // If the plugin is visible update it's geometry.
+ if (isVisible) {
+ // Set bounds (content origin)
+ bounds = aPlugins[pluginsIdx].bounds();
+ nsTArray<LayoutDeviceIntRect> rectsOut;
+ // This call may change the value of isVisible
+ CalculatePluginClip(bounds, aPlugins[pluginsIdx].clip(), aContentOffset,
+ aParentLayerVisibleRegion, rectsOut, visibleBounds,
+ isVisible);
+ // content clipping region (widget origin)
+ rv = widget->SetWindowClipRegion(rectsOut, false);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "widget call failure");
+ // This will trigger a browser window paint event for areas uncovered
+ // by a child window move, and will call invalidate on the plugin
+ // parent window which the browser owns. The latter gets picked up in
+ // our OnPaint handler and forwarded over to the plugin process async.
+ widget->Resize(aContentOffset.x + bounds.X(),
+ aContentOffset.y + bounds.Y(), bounds.Width(),
+ bounds.Height(), true);
+ }
+
+ widget->Enable(isVisible);
+
+ // visible state - updated after clipping, prior to invalidating
+ widget->Show(isVisible);
+
+ // Handle invalidation, this can be costly, avoid if it is not needed.
+ if (isVisible) {
+ // invalidate region (widget origin)
+# if defined(XP_WIN)
+ // Work around for flash's crummy sandbox. See bug 762948. This call
+ // digs down into the window hirearchy, invalidating regions on
+ // windows owned by other processes.
+ mozilla::widget::WinUtils::InvalidatePluginAsWorkaround(widget,
+ visibleBounds);
+# else
+ widget->Invalidate(visibleBounds);
+# endif
+ visiblePluginIds.AppendElement(aPlugins[pluginsIdx].windowId());
+ }
+ }
+ }
+ // Any plugins we didn't update need to be hidden, as they are
+ // not associated with visible content.
+ nsIWidget::UpdateRegisteredPluginWindowVisibility((uintptr_t)parent,
+ visiblePluginIds);
+ if (!mCanSend) {
+ return IPC_OK();
+ }
+ SendRemotePluginsReady();
+ return IPC_OK();
+#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+}
+
+#if defined(XP_WIN)
+static void ScheduleSendAllPluginsCaptured(CompositorBridgeChild* aThis,
+ nsISerialEventTarget* aThread) {
+ aThread->Dispatch(NewNonOwningRunnableMethod(
+ "CompositorBridgeChild::SendAllPluginsCaptured", aThis,
+ &CompositorBridgeChild::SendAllPluginsCaptured));
+}
+#endif
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvCaptureAllPlugins(
+ const uintptr_t& aParentWidget) {
+#if defined(XP_WIN)
+ MOZ_ASSERT(NS_IsMainThread());
+ nsIWidget::CaptureRegisteredPlugins(aParentWidget);
+
+ // Bounce the call to SendAllPluginsCaptured off the ImageBridgeChild thread,
+ // to make sure that the image updates on that thread have been processed.
+ ImageBridgeChild::GetSingleton()->GetThread()->Dispatch(NewRunnableFunction(
+ "ScheduleSendAllPluginsCapturedRunnable", &ScheduleSendAllPluginsCaptured,
+ this, NS_GetCurrentThread()));
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "CompositorBridgeChild::RecvCaptureAllPlugins calls unexpected.");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvHideAllPlugins(
+ const uintptr_t& aParentWidget) {
+#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+ MOZ_ASSERT_UNREACHABLE(
+ "CompositorBridgeChild::RecvHideAllPlugins calls "
+ "unexpected on this platform.");
+ return IPC_FAIL_NO_REASON(this);
+#else
+ MOZ_ASSERT(NS_IsMainThread());
+ nsTArray<uintptr_t> list;
+ nsIWidget::UpdateRegisteredPluginWindowVisibility(aParentWidget, list);
+ if (!mCanSend) {
+ return IPC_OK();
+ }
+ SendRemotePluginsReady();
+ return IPC_OK();
+#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvDidComposite(
+ const LayersId& aId, const TransactionId& aTransactionId,
+ const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd) {
+ // Hold a reference to keep texture pools alive. See bug 1387799
+ const auto texturePools = mTexturePools.Clone();
+
+ if (mLayerManager) {
+ MOZ_ASSERT(!aId.IsValid());
+ MOZ_ASSERT(mLayerManager->GetBackendType() ==
+ LayersBackend::LAYERS_CLIENT ||
+ mLayerManager->GetBackendType() == LayersBackend::LAYERS_WR);
+ // Hold a reference to keep LayerManager alive. See Bug 1242668.
+ RefPtr<LayerManager> m = mLayerManager;
+ m->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+ } else if (aId.IsValid()) {
+ RefPtr<dom::BrowserChild> child = dom::BrowserChild::GetFrom(aId);
+ if (child) {
+ child->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+ }
+
+ for (size_t i = 0; i < texturePools.Length(); i++) {
+ texturePools[i]->ReturnDeferredClients();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvNotifyFrameStats(
+ nsTArray<FrameStats>&& aFrameStats) {
+ gfxPlatform::GetPlatform()->NotifyFrameStats(std::move(aFrameStats));
+ return IPC_OK();
+}
+
+void CompositorBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ if (aWhy == AbnormalShutdown) {
+ // If the parent side runs into a problem then the actor will be destroyed.
+ // There is nothing we can do in the child side, here sets mCanSend as
+ // false.
+ gfxCriticalNote << "Receive IPC close with reason=AbnormalShutdown";
+ }
+
+ {
+ // We take the lock to update these fields, since they are read from the
+ // paint thread. We don't need the lock to init them, since that happens
+ // on the main thread before the paint thread can ever grab a reference
+ // to the CompositorBridge object.
+ //
+ // Note that it is useful to take this lock for one other reason: It also
+ // tells us whether GetIPCChannel is safe to call. If we access the IPC
+ // channel within this lock, when mCanSend is true, then we know it has not
+ // been zapped by IPDL.
+ MonitorAutoLock lock(mPaintLock);
+ mCanSend = false;
+ mActorDestroyed = true;
+ }
+
+ if (mProcessToken && XRE_IsParentProcess()) {
+ GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+ }
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvSharedCompositorFrameMetrics(
+ const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle, const LayersId& aLayersId,
+ const uint32_t& aAPZCId) {
+ SharedFrameMetricsData* data =
+ new SharedFrameMetricsData(metrics, handle, aLayersId, aAPZCId);
+ mFrameMetricsTable.Put(data->GetViewID(), data);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+CompositorBridgeChild::RecvReleaseSharedCompositorFrameMetrics(
+ const ViewID& aId, const uint32_t& aAPZCId) {
+ if (auto entry = mFrameMetricsTable.Lookup(aId)) {
+ // The SharedFrameMetricsData may have been removed previously if
+ // a SharedFrameMetricsData with the same ViewID but later APZCId had
+ // been store and over wrote it.
+ if (entry.Data()->GetAPZCId() == aAPZCId) {
+ entry.Remove();
+ }
+ }
+ return IPC_OK();
+}
+
+CompositorBridgeChild::SharedFrameMetricsData::SharedFrameMetricsData(
+ const ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle, const LayersId& aLayersId,
+ const uint32_t& aAPZCId)
+ : mMutex(nullptr), mLayersId(aLayersId), mAPZCId(aAPZCId) {
+ mBuffer = new ipc::SharedMemoryBasic;
+ mBuffer->SetHandle(metrics, ipc::SharedMemory::RightsReadOnly);
+ mBuffer->Map(sizeof(FrameMetrics));
+ mMutex = new CrossProcessMutex(handle);
+ MOZ_COUNT_CTOR(SharedFrameMetricsData);
+}
+
+CompositorBridgeChild::SharedFrameMetricsData::~SharedFrameMetricsData() {
+ // When the hash table deletes the class, delete
+ // the shared memory and mutex.
+ delete mMutex;
+ mBuffer = nullptr;
+ MOZ_COUNT_DTOR(SharedFrameMetricsData);
+}
+
+void CompositorBridgeChild::SharedFrameMetricsData::CopyFrameMetrics(
+ FrameMetrics* aFrame) {
+ const FrameMetrics* frame =
+ static_cast<const FrameMetrics*>(mBuffer->memory());
+ MOZ_ASSERT(frame);
+ mMutex->Lock();
+ *aFrame = *frame;
+ mMutex->Unlock();
+}
+
+ScrollableLayerGuid::ViewID
+CompositorBridgeChild::SharedFrameMetricsData::GetViewID() {
+ const FrameMetrics* frame =
+ static_cast<const FrameMetrics*>(mBuffer->memory());
+ MOZ_ASSERT(frame);
+ // Not locking to read of mScrollId since it should not change after being
+ // initially set.
+ return frame->GetScrollId();
+}
+
+LayersId CompositorBridgeChild::SharedFrameMetricsData::GetLayersId() const {
+ return mLayersId;
+}
+
+uint32_t CompositorBridgeChild::SharedFrameMetricsData::GetAPZCId() {
+ return mAPZCId;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvRemotePaintIsReady() {
+ // Used on the content thread, this bounces the message to the
+ // BrowserParent (via the BrowserChild) if the notification was previously
+ // requested. XPCOM gives a soup of compiler errors when trying to
+ // do_QueryReference so I'm using static_cast<>
+ MOZ_LAYERS_LOG(
+ ("[RemoteGfx] CompositorBridgeChild received RemotePaintIsReady"));
+ RefPtr<nsIBrowserChild> iBrowserChild(do_QueryReferent(mWeakBrowserChild));
+ if (!iBrowserChild) {
+ MOZ_LAYERS_LOG(
+ ("[RemoteGfx] Note: BrowserChild was released before "
+ "RemotePaintIsReady. "
+ "MozAfterRemotePaint will not be sent to listener."));
+ return IPC_OK();
+ }
+ BrowserChild* browserChild = static_cast<BrowserChild*>(iBrowserChild.get());
+ MOZ_ASSERT(browserChild);
+ Unused << browserChild->SendRemotePaintIsReady();
+ mWeakBrowserChild = nullptr;
+ return IPC_OK();
+}
+
+void CompositorBridgeChild::RequestNotifyAfterRemotePaint(
+ BrowserChild* aBrowserChild) {
+ MOZ_ASSERT(aBrowserChild,
+ "NULL BrowserChild not allowed in "
+ "CompositorBridgeChild::RequestNotifyAfterRemotePaint");
+ mWeakBrowserChild =
+ do_GetWeakReference(static_cast<dom::BrowserChild*>(aBrowserChild));
+ if (!mCanSend) {
+ return;
+ }
+ Unused << SendRequestNotifyAfterRemotePaint();
+}
+
+void CompositorBridgeChild::CancelNotifyAfterRemotePaint(
+ BrowserChild* aBrowserChild) {
+ RefPtr<nsIBrowserChild> iBrowserChild(do_QueryReferent(mWeakBrowserChild));
+ if (!iBrowserChild) {
+ return;
+ }
+ BrowserChild* browserChild = static_cast<BrowserChild*>(iBrowserChild.get());
+ if (browserChild == aBrowserChild) {
+ mWeakBrowserChild = nullptr;
+ }
+}
+
+bool CompositorBridgeChild::SendWillClose() {
+ MOZ_RELEASE_ASSERT(mCanSend);
+ return PCompositorBridgeChild::SendWillClose();
+}
+
+bool CompositorBridgeChild::SendPause() {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendPause();
+}
+
+bool CompositorBridgeChild::SendResume() {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendResume();
+}
+
+bool CompositorBridgeChild::SendResumeAsync() {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendResumeAsync();
+}
+
+bool CompositorBridgeChild::SendNotifyChildCreated(
+ const LayersId& id, CompositorOptions* aOptions) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendNotifyChildCreated(id, aOptions);
+}
+
+bool CompositorBridgeChild::SendAdoptChild(const LayersId& id) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendAdoptChild(id);
+}
+
+bool CompositorBridgeChild::SendMakeSnapshot(
+ const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendMakeSnapshot(inSnapshot, dirtyRect);
+}
+
+bool CompositorBridgeChild::SendFlushRendering() {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendFlushRendering();
+}
+
+bool CompositorBridgeChild::SendStartFrameTimeRecording(
+ const int32_t& bufferSize, uint32_t* startIndex) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendStartFrameTimeRecording(bufferSize,
+ startIndex);
+}
+
+bool CompositorBridgeChild::SendStopFrameTimeRecording(
+ const uint32_t& startIndex, nsTArray<float>* intervals) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendStopFrameTimeRecording(startIndex,
+ intervals);
+}
+
+bool CompositorBridgeChild::SendNotifyRegionInvalidated(
+ const nsIntRegion& region) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendNotifyRegionInvalidated(region);
+}
+
+bool CompositorBridgeChild::SendRequestNotifyAfterRemotePaint() {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendRequestNotifyAfterRemotePaint();
+}
+
+bool CompositorBridgeChild::SendAllPluginsCaptured() {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendAllPluginsCaptured();
+}
+
+PTextureChild* CompositorBridgeChild::AllocPTextureChild(
+ const SurfaceDescriptor&, const ReadLockDescriptor&, const LayersBackend&,
+ const TextureFlags&, const LayersId&, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ return TextureClient::CreateIPDLActor();
+}
+
+bool CompositorBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvParentAsyncMessages(
+ nsTArray<AsyncParentMessageData>&& aMessages) {
+ for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
+ const AsyncParentMessageData& message = aMessages[i];
+
+ switch (message.type()) {
+ case AsyncParentMessageData::TOpNotifyNotUsed: {
+ const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
+ NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
+ break;
+ }
+ case AsyncParentMessageData::TOpDeliverReleaseFence: {
+ // Release fences are delivered via ImageBridge.
+ // Since some TextureClients are recycled without recycle callback.
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ break;
+ }
+ default:
+ NS_ERROR("unknown AsyncParentMessageData type");
+ return IPC_FAIL_NO_REASON(this);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvObserveLayersUpdate(
+ const LayersId& aLayersId, const LayersObserverEpoch& aEpoch,
+ const bool& aActive) {
+ // This message is sent via the window compositor, not the tab compositor -
+ // however it still has a layers id.
+ MOZ_ASSERT(aLayersId.IsValid());
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (RefPtr<dom::BrowserParent> tab =
+ dom::BrowserParent::GetBrowserParentFromLayersId(aLayersId)) {
+ tab->LayerTreeUpdate(aEpoch, aActive);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvCompositorOptionsChanged(
+ const LayersId& aLayersId, const CompositorOptions& aNewOptions) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (RefPtr<dom::BrowserParent> tab =
+ dom::BrowserParent::GetBrowserParentFromLayersId(aLayersId)) {
+ Unused << tab->SendCompositorOptionsChanged(aNewOptions);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvNotifyJankedAnimations(
+ const LayersId& aLayersId, nsTArray<uint64_t>&& aJankedAnimations) {
+ if (mLayerManager) {
+ MOZ_ASSERT(!aLayersId.IsValid());
+ mLayerManager->UpdatePartialPrerenderedAnimations(aJankedAnimations);
+ } else if (aLayersId.IsValid()) {
+ RefPtr<dom::BrowserChild> child = dom::BrowserChild::GetFrom(aLayersId);
+ if (child) {
+ child->NotifyJankedAnimations(aJankedAnimations);
+ }
+ }
+
+ return IPC_OK();
+}
+
+void CompositorBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(
+ TextureClient* aClient) {
+ if (!aClient) {
+ return;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ auto bufferId = aClient->GetInternalData()->GetBufferId();
+ if (bufferId.isSome()) {
+ MOZ_ASSERT(aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END);
+ AndroidHardwareBufferManager::Get()->HoldUntilNotifyNotUsed(
+ bufferId.ref(), GetFwdTransactionId(), /* aUsesImageBridge */ false);
+ }
+#endif
+
+ bool waitNotifyNotUsed =
+ aClient->GetFlags() & TextureFlags::RECYCLE ||
+ aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END;
+ if (!waitNotifyNotUsed) {
+ return;
+ }
+
+ aClient->SetLastFwdTransactionId(GetFwdTransactionId());
+ mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient);
+}
+
+void CompositorBridgeChild::NotifyNotUsed(uint64_t aTextureId,
+ uint64_t aFwdTransactionId) {
+ auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId);
+ if (it != mTexturesWaitingNotifyNotUsed.end()) {
+ if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) {
+ // Released on host side, but client already requested newer use texture.
+ return;
+ }
+ mTexturesWaitingNotifyNotUsed.erase(it);
+ }
+}
+
+void CompositorBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) {
+ mTexturesWaitingNotifyNotUsed.erase(aTextureId);
+}
+
+TextureClientPool* CompositorBridgeChild::GetTexturePool(
+ KnowsCompositor* aAllocator, SurfaceFormat aFormat, TextureFlags aFlags) {
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ if (mTexturePools[i]->GetBackend() ==
+ aAllocator->GetCompositorBackendType() &&
+ mTexturePools[i]->GetMaxTextureSize() ==
+ aAllocator->GetMaxTextureSize() &&
+ mTexturePools[i]->GetFormat() == aFormat &&
+ mTexturePools[i]->GetFlags() == aFlags) {
+ return mTexturePools[i];
+ }
+ }
+
+ mTexturePools.AppendElement(new TextureClientPool(
+ aAllocator, aFormat, gfx::gfxVars::TileSize(), aFlags,
+ StaticPrefs::layers_tile_pool_shrink_timeout_AtStartup(),
+ StaticPrefs::layers_tile_pool_clear_timeout_AtStartup(),
+ StaticPrefs::layers_tile_initial_pool_size_AtStartup(),
+ StaticPrefs::layers_tile_pool_unused_size_AtStartup(), this));
+
+ return mTexturePools.LastElement();
+}
+
+void CompositorBridgeChild::HandleMemoryPressure() {
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Clear();
+ }
+}
+
+void CompositorBridgeChild::ClearTexturePool() {
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Clear();
+ }
+}
+
+FixedSizeSmallShmemSectionAllocator*
+CompositorBridgeChild::GetTileLockAllocator() {
+ if (!IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!mSectionAllocator) {
+ mSectionAllocator = new FixedSizeSmallShmemSectionAllocator(this);
+ }
+ return mSectionAllocator;
+}
+
+PTextureChild* CompositorBridgeChild::CreateTexture(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) {
+ PTextureChild* textureChild =
+ AllocPTextureChild(aSharedData, aReadLock, aLayersBackend, aFlags,
+ LayersId{0} /* FIXME */, aSerial, aExternalImageId);
+
+ // Do the DOM labeling.
+ if (aTarget) {
+ SetEventTargetForActor(textureChild, aTarget);
+ }
+
+ return SendPTextureConstructor(
+ textureChild, aSharedData, aReadLock, aLayersBackend, aFlags,
+ LayersId{0} /* FIXME? */, aSerial, aExternalImageId);
+}
+
+already_AddRefed<CanvasChild> CompositorBridgeChild::GetCanvasChild() {
+ MOZ_ASSERT(gfx::gfxVars::RemoteCanvasEnabled());
+
+ if (CanvasChild::Deactivated()) {
+ return nullptr;
+ }
+
+ if (!mCanvasChild) {
+ ipc::Endpoint<PCanvasParent> parentEndpoint;
+ ipc::Endpoint<PCanvasChild> childEndpoint;
+ nsresult rv = PCanvas::CreateEndpoints(OtherPid(), base::GetCurrentProcId(),
+ &parentEndpoint, &childEndpoint);
+ if (NS_SUCCEEDED(rv)) {
+ Unused << SendInitPCanvasParent(std::move(parentEndpoint));
+ mCanvasChild = new CanvasChild(std::move(childEndpoint));
+ }
+ }
+
+ return do_AddRef(mCanvasChild);
+}
+
+void CompositorBridgeChild::EndCanvasTransaction() {
+ if (mCanvasChild) {
+ mCanvasChild->EndTransaction();
+ if (mCanvasChild->ShouldBeCleanedUp()) {
+ mCanvasChild->Destroy();
+ Unused << SendReleasePCanvasParent();
+ mCanvasChild = nullptr;
+ }
+ }
+}
+
+RefPtr<webgpu::WebGPUChild> CompositorBridgeChild::GetWebGPUChild() {
+ MOZ_ASSERT(gfx::gfxConfig::IsEnabled(gfx::Feature::WEBGPU));
+ if (!mWebGPUChild) {
+ webgpu::PWebGPUChild* bridge = SendPWebGPUConstructor();
+ mWebGPUChild = static_cast<webgpu::WebGPUChild*>(bridge);
+ }
+
+ return mWebGPUChild;
+}
+
+bool CompositorBridgeChild::AllocUnsafeShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ ShmemAllocated(this);
+ return PCompositorBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool CompositorBridgeChild::AllocShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ ShmemAllocated(this);
+ return PCompositorBridgeChild::AllocShmem(aSize, aType, aShmem);
+}
+
+bool CompositorBridgeChild::DeallocShmem(ipc::Shmem& aShmem) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::DeallocShmem(aShmem);
+}
+
+widget::PCompositorWidgetChild*
+CompositorBridgeChild::AllocPCompositorWidgetChild(
+ const CompositorWidgetInitData& aInitData) {
+ // We send the constructor manually.
+ MOZ_CRASH("Should not be called");
+ return nullptr;
+}
+
+bool CompositorBridgeChild::DeallocPCompositorWidgetChild(
+ PCompositorWidgetChild* aActor) {
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+ delete aActor;
+ return true;
+#else
+ return false;
+#endif
+}
+
+PAPZCTreeManagerChild* CompositorBridgeChild::AllocPAPZCTreeManagerChild(
+ const LayersId& aLayersId) {
+ APZCTreeManagerChild* child = new APZCTreeManagerChild();
+ child->AddIPDLReference();
+
+ return child;
+}
+
+PAPZChild* CompositorBridgeChild::AllocPAPZChild(const LayersId& aLayersId) {
+ // We send the constructor manually.
+ MOZ_CRASH("Should not be called");
+ return nullptr;
+}
+
+bool CompositorBridgeChild::DeallocPAPZChild(PAPZChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+bool CompositorBridgeChild::DeallocPAPZCTreeManagerChild(
+ PAPZCTreeManagerChild* aActor) {
+ APZCTreeManagerChild* child = static_cast<APZCTreeManagerChild*>(aActor);
+ child->ReleaseIPDLReference();
+ return true;
+}
+
+// -
+
+void CompositorBridgeChild::WillEndTransaction() { ResetShmemCounter(); }
+
+PWebRenderBridgeChild* CompositorBridgeChild::AllocPWebRenderBridgeChild(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize&,
+ const WindowKind&) {
+ WebRenderBridgeChild* child = new WebRenderBridgeChild(aPipelineId);
+ child->AddIPDLReference();
+ return child;
+}
+
+bool CompositorBridgeChild::DeallocPWebRenderBridgeChild(
+ PWebRenderBridgeChild* aActor) {
+ WebRenderBridgeChild* child = static_cast<WebRenderBridgeChild*>(aActor);
+ ClearSharedFrameMetricsData(wr::AsLayersId(child->GetPipeline()));
+ child->ReleaseIPDLReference();
+ return true;
+}
+
+webgpu::PWebGPUChild* CompositorBridgeChild::AllocPWebGPUChild() {
+ webgpu::WebGPUChild* child = new webgpu::WebGPUChild();
+ child->AddIPDLReference();
+ return child;
+}
+
+bool CompositorBridgeChild::DeallocPWebGPUChild(webgpu::PWebGPUChild* aActor) {
+ webgpu::WebGPUChild* child = static_cast<webgpu::WebGPUChild*>(aActor);
+ child->ReleaseIPDLReference();
+ return true;
+}
+
+void CompositorBridgeChild::ClearSharedFrameMetricsData(LayersId aLayersId) {
+ for (auto iter = mFrameMetricsTable.Iter(); !iter.Done(); iter.Next()) {
+ auto data = iter.UserData();
+ if (data->GetLayersId() == aLayersId) {
+ iter.Remove();
+ }
+ }
+}
+
+uint64_t CompositorBridgeChild::GetNextResourceId() {
+ ++mResourceId;
+ MOZ_RELEASE_ASSERT(mResourceId != UINT32_MAX);
+
+ uint64_t id = mIdNamespace;
+ id = (id << 32) | mResourceId;
+
+ return id;
+}
+
+wr::MaybeExternalImageId CompositorBridgeChild::GetNextExternalImageId() {
+ return Some(wr::ToExternalImageId(GetNextResourceId()));
+}
+
+wr::PipelineId CompositorBridgeChild::GetNextPipelineId() {
+ return wr::AsPipelineId(GetNextResourceId());
+}
+
+void CompositorBridgeChild::FlushAsyncPaints() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ Maybe<TimeStamp> start;
+ if (XRE_IsContentProcess() && gfx::gfxVars::UseOMTP()) {
+ start = Some(TimeStamp::Now());
+ }
+
+ {
+ MonitorAutoLock lock(mPaintLock);
+ while (mOutstandingAsyncPaints > 0 || mOutstandingAsyncEndTransaction) {
+ lock.Wait();
+ }
+
+ // It's now safe to free any TextureClients that were used during painting.
+ mTextureClientsForAsyncPaint.Clear();
+ }
+
+ if (start) {
+ float ms = (TimeStamp::Now() - start.value()).ToMilliseconds();
+
+ // Anything above 200us gets recorded.
+ if (ms >= 0.2) {
+ mSlowFlushCount++;
+ Telemetry::Accumulate(Telemetry::GFX_OMTP_PAINT_WAIT_TIME, int32_t(ms));
+ }
+ mTotalFlushCount++;
+
+ double ratio = double(mSlowFlushCount) / double(mTotalFlushCount);
+ Telemetry::ScalarSet(Telemetry::ScalarID::GFX_OMTP_PAINT_WAIT_RATIO,
+ uint32_t(ratio * 100 * 100));
+ }
+}
+
+void CompositorBridgeChild::NotifyBeginAsyncPaint(PaintTask* aTask) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MonitorAutoLock lock(mPaintLock);
+
+ if (mTotalAsyncPaints == 0) {
+ mAsyncTransactionBegin = TimeStamp::Now();
+ }
+ mTotalAsyncPaints += 1;
+
+ // We must not be waiting for paints or buffer copying to complete yet. This
+ // would imply we started a new paint without waiting for a previous one,
+ // which could lead to incorrect rendering or IPDL deadlocks.
+ MOZ_ASSERT(!mIsDelayingForAsyncPaints);
+
+ mOutstandingAsyncPaints++;
+
+ // Mark texture clients that they are being used for async painting, and
+ // make sure we hold them alive on the main thread.
+ for (auto& client : aTask->mClients) {
+ client->AddPaintThreadRef();
+ mTextureClientsForAsyncPaint.AppendElement(client);
+ };
+}
+
+// Must only be called from the paint thread. Notifies the CompositorBridge
+// that the paint thread has finished an asynchronous paint request.
+bool CompositorBridgeChild::NotifyFinishedAsyncWorkerPaint(PaintTask* aTask) {
+ MOZ_ASSERT(PaintThread::Get()->IsOnPaintWorkerThread());
+
+ MonitorAutoLock lock(mPaintLock);
+ mOutstandingAsyncPaints--;
+
+ for (auto& client : aTask->mClients) {
+ client->DropPaintThreadRef();
+ };
+ aTask->DropTextureClients();
+
+ // If the main thread has completed queuing work and this was the
+ // last paint, then it is time to end the layer transaction and sync
+ return mOutstandingAsyncEndTransaction && mOutstandingAsyncPaints == 0;
+}
+
+bool CompositorBridgeChild::NotifyBeginAsyncEndLayerTransaction(
+ SyncObjectClient* aSyncObject) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MonitorAutoLock lock(mPaintLock);
+
+ MOZ_ASSERT(!mOutstandingAsyncEndTransaction);
+ mOutstandingAsyncEndTransaction = true;
+ mOutstandingAsyncSyncObject = aSyncObject;
+ return mOutstandingAsyncPaints == 0;
+}
+
+void CompositorBridgeChild::NotifyFinishedAsyncEndLayerTransaction() {
+ MOZ_ASSERT(PaintThread::Get()->IsOnPaintWorkerThread());
+
+ if (mOutstandingAsyncSyncObject) {
+ mOutstandingAsyncSyncObject->Synchronize();
+ mOutstandingAsyncSyncObject = nullptr;
+ }
+
+ MonitorAutoLock lock(mPaintLock);
+
+ if (mTotalAsyncPaints > 0) {
+ float tenthMs =
+ (TimeStamp::Now() - mAsyncTransactionBegin).ToMilliseconds() * 10;
+ Telemetry::Accumulate(Telemetry::GFX_OMTP_PAINT_TASK_COUNT,
+ int32_t(mTotalAsyncPaints));
+ Telemetry::Accumulate(Telemetry::GFX_OMTP_PAINT_TIME, int32_t(tenthMs));
+ mTotalAsyncPaints = 0;
+ }
+
+ // Since this should happen after ALL paints are done and
+ // at the end of a transaction, this should always be true.
+ MOZ_RELEASE_ASSERT(mOutstandingAsyncPaints == 0);
+ MOZ_ASSERT(mOutstandingAsyncEndTransaction);
+
+ mOutstandingAsyncEndTransaction = false;
+
+ // It's possible that we painted so fast that the main thread never reached
+ // the code that starts delaying messages. If so, mIsDelayingForAsyncPaints
+ // will be false, and we can safely return.
+ if (mIsDelayingForAsyncPaints) {
+ ResumeIPCAfterAsyncPaint();
+ }
+
+ // Notify the main thread in case it's blocking. We do this unconditionally
+ // to avoid deadlocking.
+ lock.Notify();
+}
+
+void CompositorBridgeChild::ResumeIPCAfterAsyncPaint() {
+ // Note: the caller is responsible for holding the lock.
+ mPaintLock.AssertCurrentThreadOwns();
+ MOZ_ASSERT(PaintThread::Get()->IsOnPaintWorkerThread());
+ MOZ_ASSERT(mOutstandingAsyncPaints == 0);
+ MOZ_ASSERT(!mOutstandingAsyncEndTransaction);
+ MOZ_ASSERT(mIsDelayingForAsyncPaints);
+
+ mIsDelayingForAsyncPaints = false;
+
+ // It's also possible that the channel has shut down already.
+ if (!mCanSend || mActorDestroyed) {
+ return;
+ }
+
+ GetIPCChannel()->StopPostponingSends();
+}
+
+void CompositorBridgeChild::PostponeMessagesIfAsyncPainting() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MonitorAutoLock lock(mPaintLock);
+
+ MOZ_ASSERT(!mIsDelayingForAsyncPaints);
+
+ // We need to wait for async paints and the async end transaction as
+ // it will do texture synchronization
+ if (mOutstandingAsyncPaints > 0 || mOutstandingAsyncEndTransaction) {
+ mIsDelayingForAsyncPaints = true;
+ GetIPCChannel()->BeginPostponingSends();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h
new file mode 100644
index 0000000000..ebfd0f10c0
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBridgeChild_h
+#define mozilla_layers_CompositorBridgeChild_h
+
+#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Monitor.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PCompositorBridgeChild.h"
+#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
+#include "mozilla/layers/PaintThread.h" // for PaintThread
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "nsClassHashtable.h" // for nsClassHashtable
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsHashKeys.h" // for nsUint64HashKey
+#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "nsIWeakReferenceUtils.h"
+
+#include <unordered_map>
+
+namespace mozilla {
+
+namespace dom {
+class BrowserChild;
+} // namespace dom
+
+namespace webgpu {
+class PWebGPUChild;
+class WebGPUChild;
+} // namespace webgpu
+
+namespace widget {
+class CompositorWidget;
+} // namespace widget
+
+namespace layers {
+
+using mozilla::dom::BrowserChild;
+
+class IAPZCTreeManager;
+class APZCTreeManagerChild;
+class CanvasChild;
+class ClientLayerManager;
+class CompositorBridgeParent;
+class CompositorManagerChild;
+class CompositorOptions;
+class LayerManager;
+class TextureClient;
+class TextureClientPool;
+struct FrameMetrics;
+
+class CompositorBridgeChild final : public PCompositorBridgeChild,
+ public TextureForwarder {
+ typedef nsTArray<AsyncParentMessageData> AsyncParentMessageArray;
+
+ friend class PCompositorBridgeChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorBridgeChild, override);
+
+ explicit CompositorBridgeChild(CompositorManagerChild* aManager);
+
+ /**
+ * Initialize the singleton compositor bridge for a content process.
+ */
+ void InitForContent(uint32_t aNamespace);
+
+ void InitForWidget(uint64_t aProcessToken, LayerManager* aLayerManager,
+ uint32_t aNamespace);
+
+ void Destroy();
+
+ /**
+ * Lookup the FrameMetrics shared by the compositor process with the
+ * associated ScrollableLayerGuid::ViewID. The returned FrameMetrics is used
+ * in progressive paint calculations.
+ */
+ bool LookupCompositorFrameMetrics(const ScrollableLayerGuid::ViewID aId,
+ FrameMetrics&);
+
+ static CompositorBridgeChild* Get();
+
+ static bool ChildProcessHasCompositorBridge();
+
+ // Returns whether the compositor is in the GPU process (false if in the UI
+ // process). This may only be called on the main thread.
+ static bool CompositorIsInGPUProcess();
+
+ mozilla::ipc::IPCResult RecvDidComposite(const LayersId& aId,
+ const TransactionId& aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd);
+
+ mozilla::ipc::IPCResult RecvNotifyFrameStats(
+ nsTArray<FrameStats>&& aFrameStats);
+
+ mozilla::ipc::IPCResult RecvInvalidateLayers(const LayersId& aLayersId);
+
+ mozilla::ipc::IPCResult RecvUpdatePluginConfigurations(
+ const LayoutDeviceIntPoint& aContentOffset,
+ const LayoutDeviceIntRegion& aVisibleRegion,
+ nsTArray<PluginWindowData>&& aPlugins);
+
+ mozilla::ipc::IPCResult RecvCaptureAllPlugins(const uintptr_t& aParentWidget);
+
+ mozilla::ipc::IPCResult RecvHideAllPlugins(const uintptr_t& aParentWidget);
+
+ mozilla::ipc::IPCResult RecvNotifyJankedAnimations(
+ const LayersId& aLayersId, nsTArray<uint64_t>&& aJankedAnimations);
+
+ PTextureChild* AllocPTextureChild(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+
+ bool DeallocPTextureChild(PTextureChild* actor);
+
+ mozilla::ipc::IPCResult RecvParentAsyncMessages(
+ nsTArray<AsyncParentMessageData>&& aMessages);
+ PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aReadLock,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId,
+ nsISerialEventTarget* aTarget) override;
+
+ already_AddRefed<CanvasChild> GetCanvasChild() final;
+
+ void EndCanvasTransaction();
+
+ RefPtr<webgpu::WebGPUChild> GetWebGPUChild();
+
+ /**
+ * Request that the parent tell us when graphics are ready on GPU.
+ * When we get that message, we bounce it to the BrowserParent via
+ * the BrowserChild
+ * @param browserChild The object to bounce the note to. Non-NULL.
+ */
+ void RequestNotifyAfterRemotePaint(BrowserChild* aBrowserChild);
+
+ void CancelNotifyAfterRemotePaint(BrowserChild* aBrowserChild);
+
+ // Beware that these methods don't override their super-class equivalent
+ // (which are not virtual), they just overload them. All of these Send*
+ // methods just add a sanity check (that it is not too late send a message)
+ // and forward the call to the super-class's equivalent method. This means
+ // that it is correct to call directly the super-class methods, but you won't
+ // get the extra safety provided here.
+ bool SendWillClose();
+ bool SendPause();
+ bool SendResume();
+ bool SendResumeAsync();
+ bool SendNotifyChildCreated(const LayersId& id, CompositorOptions* aOptions);
+ bool SendAdoptChild(const LayersId& id);
+ bool SendMakeSnapshot(const SurfaceDescriptor& inSnapshot,
+ const gfx::IntRect& dirtyRect);
+ bool SendFlushRendering();
+ bool SendGetTileSize(int32_t* tileWidth, int32_t* tileHeight);
+ bool SendStartFrameTimeRecording(const int32_t& bufferSize,
+ uint32_t* startIndex);
+ bool SendStopFrameTimeRecording(const uint32_t& startIndex,
+ nsTArray<float>* intervals);
+ bool SendNotifyRegionInvalidated(const nsIntRegion& region);
+ bool SendRequestNotifyAfterRemotePaint();
+ bool SendAllPluginsCaptured();
+ bool IsSameProcess() const override;
+
+ bool IPCOpen() const override { return mCanSend; }
+
+ static void ShutDown();
+
+ void UpdateFwdTransactionId() { ++mFwdTransactionId; }
+ uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
+
+ /**
+ * Hold TextureClient ref until end of usage on host side if
+ * TextureFlags::RECYCLE is set. Host side's usage is checked via
+ * CompositableRef.
+ */
+ void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient);
+
+ /**
+ * Notify id of Texture When host side end its use. Transaction id is used to
+ * make sure if there is no newer usage.
+ */
+ void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
+
+ void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override;
+
+ TextureClientPool* GetTexturePool(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags);
+ void ClearTexturePool();
+
+ FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() override;
+
+ void HandleMemoryPressure();
+
+ nsISerialEventTarget* GetThread() const override { return mThread; }
+
+ base::ProcessId GetParentPid() const override { return OtherPid(); }
+
+ bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ PCompositorWidgetChild* AllocPCompositorWidgetChild(
+ const CompositorWidgetInitData& aInitData);
+ bool DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor);
+
+ PAPZCTreeManagerChild* AllocPAPZCTreeManagerChild(const LayersId& aLayersId);
+ bool DeallocPAPZCTreeManagerChild(PAPZCTreeManagerChild* aActor);
+
+ PAPZChild* AllocPAPZChild(const LayersId& aLayersId);
+ bool DeallocPAPZChild(PAPZChild* aActor);
+
+ void WillEndTransaction();
+
+ PWebRenderBridgeChild* AllocPWebRenderBridgeChild(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize&,
+ const WindowKind&);
+ bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor);
+
+ webgpu::PWebGPUChild* AllocPWebGPUChild();
+ bool DeallocPWebGPUChild(webgpu::PWebGPUChild* aActor);
+
+ wr::MaybeExternalImageId GetNextExternalImageId() override;
+
+ wr::PipelineId GetNextPipelineId();
+
+ // Must only be called from the main thread. Ensures that any paints from
+ // previous frames have been flushed. The main thread blocks until the
+ // operation completes.
+ void FlushAsyncPaints();
+
+ // Must only be called from the main thread. Notifies the CompositorBridge
+ // that the paint thread is going to begin painting asynchronously.
+ void NotifyBeginAsyncPaint(PaintTask* aTask);
+
+ // Must only be called from the paint thread. Notifies the CompositorBridge
+ // that the paint thread has finished an asynchronous paint request.
+ bool NotifyFinishedAsyncWorkerPaint(PaintTask* aTask);
+
+ // Must only be called from the main thread. Notifies the CompositorBridge
+ // that all work has been submitted to the paint thread or paint worker
+ // threads, and returns whether all paints are completed. If this returns
+ // true, then an AsyncEndLayerTransaction must be queued, otherwise once
+ // NotifyFinishedAsyncWorkerPaint returns true, an AsyncEndLayerTransaction
+ // must be executed.
+ bool NotifyBeginAsyncEndLayerTransaction(SyncObjectClient* aSyncObject);
+
+ // Must only be called from the paint thread. Notifies the CompositorBridge
+ // that the paint thread has finished all async paints and and may do the
+ // requested texture sync and resume sending messages.
+ void NotifyFinishedAsyncEndLayerTransaction();
+
+ // Must only be called from the main thread. Notifies the CompoistorBridge
+ // that a transaction is about to be sent, and if the paint thread is
+ // currently painting, to begin delaying IPC messages.
+ void PostponeMessagesIfAsyncPainting();
+
+ private:
+ // Private destructor, to discourage deletion outside of Release():
+ virtual ~CompositorBridgeChild();
+
+ // Must only be called from the paint thread. If the main thread is delaying
+ // IPC messages, this forwards all such delayed IPC messages to the I/O thread
+ // and resumes IPC.
+ void ResumeIPCAfterAsyncPaint();
+
+ void PrepareFinalDestroy();
+ void AfterDestroy();
+
+ PLayerTransactionChild* AllocPLayerTransactionChild(
+ const nsTArray<LayersBackend>& aBackendHints, const LayersId& aId);
+
+ bool DeallocPLayerTransactionChild(PLayerTransactionChild* aChild);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ mozilla::ipc::IPCResult RecvSharedCompositorFrameMetrics(
+ const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle, const LayersId& aLayersId,
+ const uint32_t& aAPZCId);
+
+ mozilla::ipc::IPCResult RecvReleaseSharedCompositorFrameMetrics(
+ const ViewID& aId, const uint32_t& aAPZCId);
+
+ mozilla::ipc::IPCResult RecvRemotePaintIsReady();
+
+ mozilla::ipc::IPCResult RecvObserveLayersUpdate(
+ const LayersId& aLayersId, const LayersObserverEpoch& aEpoch,
+ const bool& aActive);
+
+ mozilla::ipc::IPCResult RecvCompositorOptionsChanged(
+ const LayersId& aLayersId, const CompositorOptions& aNewOptions);
+
+ uint64_t GetNextResourceId();
+
+ void ClearSharedFrameMetricsData(LayersId aLayersId);
+
+ // Class used to store the shared FrameMetrics, mutex, and APZCId in a hash
+ // table
+ class SharedFrameMetricsData final {
+ public:
+ SharedFrameMetricsData(
+ const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
+ const CrossProcessMutexHandle& handle, const LayersId& aLayersId,
+ const uint32_t& aAPZCId);
+
+ ~SharedFrameMetricsData();
+
+ void CopyFrameMetrics(FrameMetrics* aFrame);
+ ScrollableLayerGuid::ViewID GetViewID();
+ LayersId GetLayersId() const;
+ uint32_t GetAPZCId();
+
+ private:
+ // Pointer to the class that allows access to the shared memory that
+ // contains the shared FrameMetrics
+ RefPtr<mozilla::ipc::SharedMemoryBasic> mBuffer;
+ CrossProcessMutex* mMutex;
+ LayersId mLayersId;
+ // Unique ID of the APZC that is sharing the FrameMetrics
+ uint32_t mAPZCId;
+ };
+
+ RefPtr<CompositorManagerChild> mCompositorManager;
+
+ RefPtr<LayerManager> mLayerManager;
+
+ uint32_t mIdNamespace;
+ uint32_t mResourceId;
+
+ // When not multi-process, hold a reference to the CompositorBridgeParent to
+ // keep it alive. This reference should be null in multi-process.
+ RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
+
+ // The ViewID of the FrameMetrics is used as the key for this hash table.
+ // While this should be safe to use since the ViewID is unique
+ nsClassHashtable<nsUint64HashKey, SharedFrameMetricsData> mFrameMetricsTable;
+
+ // Weakly hold the BrowserChild that made a request to be alerted when
+ // the transaction has been received.
+ nsWeakPtr mWeakBrowserChild; // type is BrowserChild
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeChild);
+
+ // True until the beginning of the two-step shutdown sequence of this actor.
+ bool mCanSend;
+
+ // False until the actor is destroyed.
+ bool mActorDestroyed;
+
+ /**
+ * Transaction id of ShadowLayerForwarder.
+ * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction()
+ * call.
+ */
+ uint64_t mFwdTransactionId;
+
+ /**
+ * Hold TextureClients refs until end of their usages on host side.
+ * It defer calling of TextureClient recycle callback.
+ */
+ std::unordered_map<uint64_t, RefPtr<TextureClient>>
+ mTexturesWaitingNotifyNotUsed;
+
+ nsCOMPtr<nsISerialEventTarget> mThread;
+
+ AutoTArray<RefPtr<TextureClientPool>, 2> mTexturePools;
+
+ uint64_t mProcessToken;
+
+ FixedSizeSmallShmemSectionAllocator* mSectionAllocator;
+
+ // TextureClients that must be kept alive during async painting. This
+ // is only accessed on the main thread.
+ nsTArray<RefPtr<TextureClient>> mTextureClientsForAsyncPaint;
+
+ // Off-Main-Thread Painting state. This covers access to the OMTP-related
+ // state below.
+ Monitor mPaintLock;
+
+ // Contains the number of asynchronous paints that were queued since the
+ // beginning of the last async transaction, and the time stamp of when
+ // that was
+ size_t mTotalAsyncPaints;
+ TimeStamp mAsyncTransactionBegin;
+
+ // Contains the number of outstanding asynchronous paints tied to a
+ // PLayerTransaction on this bridge. This is R/W on both the main and paint
+ // threads, and must be accessed within the paint lock.
+ size_t mOutstandingAsyncPaints;
+
+ // Whether we are waiting for an async paint end transaction
+ bool mOutstandingAsyncEndTransaction;
+ RefPtr<SyncObjectClient> mOutstandingAsyncSyncObject;
+
+ // True if this CompositorBridge is currently delaying its messages until the
+ // paint thread completes. This is R/W on both the main and paint threads, and
+ // must be accessed within the paint lock.
+ bool mIsDelayingForAsyncPaints;
+
+ uintptr_t mSlowFlushCount;
+ uintptr_t mTotalFlushCount;
+
+ RefPtr<CanvasChild> mCanvasChild;
+
+ RefPtr<webgpu::WebGPUChild> mWebGPUChild;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorBrigedChild_h
diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp
new file mode 100644
index 0000000000..130cf28d7b
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -0,0 +1,2990 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorBridgeParent.h"
+
+#include <stdio.h> // for fprintf, stdout
+#include <stdint.h> // for uint64_t
+#include <map> // for _Rb_tree_iterator, etc
+#include <utility> // for pair
+
+#include "apz/src/APZCTreeManager.h" // for APZCTreeManager
+#include "LayerTransactionParent.h" // for LayerTransactionParent
+#include "RenderTrace.h" // for RenderTraceLayers
+#include "base/process.h" // for ProcessId
+#include "gfxContext.h" // for gfxContext
+#include "gfxPlatform.h" // for gfxPlatform
+#include "TreeTraversal.h" // for ForEachNode
+#ifdef MOZ_WIDGET_GTK
+# include "gfxPlatformGtk.h" // for gfxPlatform
+#endif
+#include "mozilla/AutoRestore.h" // for AutoRestore
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for IntSize
+#include "mozilla/gfx/gfxVars.h" // for gfxVars
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZSampler.h" // for APZSampler
+#include "mozilla/layers/APZThreadUtils.h" // for APZThreadUtils
+#include "mozilla/layers/APZUpdater.h" // for APZUpdater
+#include "mozilla/layers/AsyncCompositionManager.h"
+#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
+#include "mozilla/layers/CompositionRecorder.h" // for CompositionRecorder
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorAnimationStorage.h" // for CompositorAnimationStorage
+#include "mozilla/layers/CompositorManagerParent.h" // for CompositorManagerParent
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/CompositorVsyncScheduler.h"
+#include "mozilla/layers/ContentCompositorBridgeParent.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayerManagerMLGPU.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/OMTASampler.h"
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "mozilla/webgpu/WebGPUParent.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/PerfStats.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Telemetry.h"
+#ifdef MOZ_WIDGET_GTK
+# include "basic/X11BasicCompositor.h" // for X11BasicCompositor
+#endif
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsTArray.h" // for nsTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#ifdef XP_WIN
+# include "mozilla/layers/CompositorD3D11.h"
+# include "mozilla/widget/WinCompositorWidget.h"
+# include "mozilla/WindowsVersion.h"
+#endif
+#include "GeckoProfiler.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/VsyncDispatcher.h"
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+# include "VsyncSource.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetParent.h"
+#endif
+#ifdef XP_WIN
+# include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+#include "LayerScope.h"
+
+namespace mozilla {
+
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+
+using base::ProcessId;
+
+using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON;
+
+/// Equivalent to asserting CompositorThreadHolder::IsInCompositorThread with
+/// the addition that it doesn't assert if the compositor thread holder is
+/// already gone during late shutdown.
+static void AssertIsInCompositorThread() {
+ MOZ_RELEASE_ASSERT(!CompositorThread() ||
+ CompositorThreadHolder::IsInCompositorThread());
+}
+
+CompositorBridgeParentBase::CompositorBridgeParentBase(
+ CompositorManagerParent* aManager)
+ : mCanSend(true), mCompositorManager(aManager) {}
+
+CompositorBridgeParentBase::~CompositorBridgeParentBase() = default;
+
+ProcessId CompositorBridgeParentBase::GetChildProcessId() { return OtherPid(); }
+
+void CompositorBridgeParentBase::NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) {
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
+ if (!texture) {
+ return;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (texture->GetAndroidHardwareBuffer()) {
+ MOZ_ASSERT(texture->GetFlags() & TextureFlags::RECYCLE);
+ ImageBridgeParent::NotifyBufferNotUsedOfCompositorBridge(
+ GetChildProcessId(), texture, aTransactionId);
+ }
+#endif
+
+ if (!(texture->GetFlags() & TextureFlags::RECYCLE) &&
+ !(texture->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END)) {
+ return;
+ }
+
+ uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
+ mPendingAsyncMessage.push_back(OpNotifyNotUsed(textureId, aTransactionId));
+}
+
+void CompositorBridgeParentBase::SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) {
+ Unused << SendParentAsyncMessages(aMessage);
+}
+
+bool CompositorBridgeParentBase::AllocShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool CompositorBridgeParentBase::AllocUnsafeShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem) {
+ return PCompositorBridgeParent::DeallocShmem(aShmem);
+}
+
+base::ProcessId CompositorBridgeParentBase::RemotePid() { return OtherPid(); }
+
+bool CompositorBridgeParentBase::StartSharingMetrics(
+ ipc::SharedMemoryBasic::Handle aHandle,
+ CrossProcessMutexHandle aMutexHandle, LayersId aLayersId,
+ uint32_t aApzcId) {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ MOZ_ASSERT(CompositorThread());
+ CompositorThread()->Dispatch(
+ NewRunnableMethod<ipc::SharedMemoryBasic::Handle,
+ CrossProcessMutexHandle, LayersId, uint32_t>(
+ "layers::CompositorBridgeParent::StartSharingMetrics", this,
+ &CompositorBridgeParentBase::StartSharingMetrics, aHandle,
+ aMutexHandle, aLayersId, aApzcId));
+ return true;
+ }
+
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeParent::SendSharedCompositorFrameMetrics(
+ aHandle, aMutexHandle, aLayersId, aApzcId);
+}
+
+bool CompositorBridgeParentBase::StopSharingMetrics(
+ ScrollableLayerGuid::ViewID aScrollId, uint32_t aApzcId) {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ MOZ_ASSERT(CompositorThread());
+ CompositorThread()->Dispatch(
+ NewRunnableMethod<ScrollableLayerGuid::ViewID, uint32_t>(
+ "layers::CompositorBridgeParent::StopSharingMetrics", this,
+ &CompositorBridgeParentBase::StopSharingMetrics, aScrollId,
+ aApzcId));
+ return true;
+ }
+
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeParent::SendReleaseSharedCompositorFrameMetrics(
+ aScrollId, aApzcId);
+}
+
+CompositorBridgeParent::LayerTreeState::LayerTreeState()
+ : mApzcTreeManagerParent(nullptr),
+ mParent(nullptr),
+ mLayerManager(nullptr),
+ mContentCompositorBridgeParent(nullptr),
+ mLayerTree(nullptr),
+ mUpdatedPluginDataAvailable(false) {}
+
+CompositorBridgeParent::LayerTreeState::~LayerTreeState() {
+ if (mController) {
+ mController->Destroy();
+ }
+}
+
+typedef std::map<LayersId, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
+LayerTreeMap sIndirectLayerTrees;
+StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock;
+
+static void EnsureLayerTreeMapReady() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sIndirectLayerTreesLock) {
+ sIndirectLayerTreesLock = new Monitor("IndirectLayerTree");
+ mozilla::ClearOnShutdown(&sIndirectLayerTreesLock);
+ }
+}
+
+template <typename Lambda>
+inline void CompositorBridgeParent::ForEachIndirectLayerTree(
+ const Lambda& aCallback) {
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ for (auto it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end();
+ it++) {
+ LayerTreeState* state = &it->second;
+ if (state->mParent == this) {
+ aCallback(state, it->first);
+ }
+ }
+}
+
+/*static*/ template <typename Lambda>
+inline void CompositorBridgeParent::ForEachWebRenderBridgeParent(
+ const Lambda& aCallback) {
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ for (auto& it : sIndirectLayerTrees) {
+ LayerTreeState* state = &it.second;
+ if (state->mWrBridge) {
+ aCallback(state->mWrBridge);
+ }
+ }
+}
+
+/**
+ * A global map referencing each compositor by ID.
+ *
+ * This map is used by the ImageBridge protocol to trigger
+ * compositions without having to keep references to the
+ * compositor
+ */
+typedef std::map<uint64_t, CompositorBridgeParent*> CompositorMap;
+static StaticAutoPtr<CompositorMap> sCompositorMap;
+
+void CompositorBridgeParent::Setup() {
+ EnsureLayerTreeMapReady();
+
+ MOZ_ASSERT(!sCompositorMap);
+ sCompositorMap = new CompositorMap;
+}
+
+void CompositorBridgeParent::FinishShutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sCompositorMap) {
+ MOZ_ASSERT(sCompositorMap->empty());
+ sCompositorMap = nullptr;
+ }
+
+ // TODO: this should be empty by now...
+ sIndirectLayerTrees.clear();
+}
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+static int32_t CalculateCompositionFrameRate() {
+ // Used when layout.frame_rate is -1. Needs to be kept in sync with
+ // DEFAULT_FRAME_RATE in nsRefreshDriver.cpp.
+ // TODO: This should actually return the vsync rate.
+ const int32_t defaultFrameRate = 60;
+ int32_t compositionFrameRatePref =
+ StaticPrefs::layers_offmainthreadcomposition_frame_rate();
+ if (compositionFrameRatePref < 0) {
+ // Use the same frame rate for composition as for layout.
+ int32_t layoutFrameRatePref = StaticPrefs::layout_frame_rate();
+ if (layoutFrameRatePref < 0) {
+ // TODO: The main thread frame scheduling code consults the actual
+ // monitor refresh rate in this case. We should do the same.
+ return defaultFrameRate;
+ }
+ return layoutFrameRatePref;
+ }
+ return compositionFrameRatePref;
+}
+#endif
+
+CompositorBridgeParent::CompositorBridgeParent(
+ CompositorManagerParent* aManager, CSSToLayoutDeviceScale aScale,
+ const TimeDuration& aVsyncRate, const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize)
+ : CompositorBridgeParentBase(aManager),
+ mWidget(nullptr),
+ mScale(aScale),
+ mVsyncRate(aVsyncRate),
+ mPendingTransaction{0},
+ mPaused(false),
+ mHaveCompositionRecorder(false),
+ mIsForcedFirstPaint(false),
+ mUseExternalSurfaceSize(aUseExternalSurfaceSize),
+ mEGLSurfaceSize(aSurfaceSize),
+ mOptions(aOptions),
+ mPauseCompositionMonitor("PauseCompositionMonitor"),
+ mResumeCompositionMonitor("ResumeCompositionMonitor"),
+ mCompositorBridgeID(0),
+ mRootLayerTreeID{0},
+ mOverrideComposeReadiness(false),
+ mForceCompositionTask(nullptr),
+ mCompositorScheduler(nullptr),
+ mAnimationStorage(nullptr),
+ mPaintTime(TimeDuration::Forever())
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ ,
+ mLastPluginUpdateLayerTreeId{0},
+ mDeferPluginWindows(false),
+ mPluginWindowsHidden(false)
+#endif
+{
+}
+
+void CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget,
+ const LayersId& aLayerTreeId) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mWidget = aWidget;
+ mRootLayerTreeID = aLayerTreeId;
+
+ Initialize();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvInitialize(
+ const LayersId& aRootLayerTreeId) {
+ MOZ_ASSERT(XRE_IsGPUProcess());
+
+ mRootLayerTreeID = aRootLayerTreeId;
+#ifdef XP_WIN
+ if (XRE_IsGPUProcess()) {
+ mWidget->AsWindows()->SetRootLayerTreeID(mRootLayerTreeID);
+ }
+#endif
+
+ Initialize();
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::Initialize() {
+ MOZ_ASSERT(CompositorThread(),
+ "The compositor thread must be Initialized before instanciating a "
+ "CompositorBridgeParent.");
+
+ if (mOptions.UseAPZ()) {
+ MOZ_ASSERT(!mApzcTreeManager);
+ MOZ_ASSERT(!mApzSampler);
+ MOZ_ASSERT(!mApzUpdater);
+ mApzcTreeManager =
+ new APZCTreeManager(mRootLayerTreeID, mOptions.UseWebRender());
+ mApzSampler = new APZSampler(mApzcTreeManager, mOptions.UseWebRender());
+ mApzUpdater = new APZUpdater(mApzcTreeManager, mOptions.UseWebRender());
+ }
+
+ if (mOptions.UseWebRender()) {
+ CompositorAnimationStorage* animationStorage = GetAnimationStorage();
+ mOMTASampler = new OMTASampler(animationStorage, mRootLayerTreeID);
+ }
+
+ mPaused = mOptions.InitiallyPaused();
+
+ mCompositorBridgeID = 0;
+ // FIXME: This holds on the the fact that right now the only thing that
+ // can destroy this instance is initialized on the compositor thread after
+ // this task has been processed.
+ MOZ_ASSERT(CompositorThread());
+ CompositorThread()->Dispatch(NewRunnableFunction(
+ "AddCompositorRunnable", &AddCompositor, this, &mCompositorBridgeID));
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
+ }
+
+ LayerScope::SetPixelScale(mScale.scale);
+
+ if (!mOptions.UseWebRender()) {
+ mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
+ }
+}
+
+LayersId CompositorBridgeParent::RootLayerTreeId() {
+ MOZ_ASSERT(mRootLayerTreeID.IsValid());
+ return mRootLayerTreeID;
+}
+
+CompositorBridgeParent::~CompositorBridgeParent() {
+ nsTArray<PTextureParent*> textures;
+ ManagedPTextureParent(textures);
+ // We expect all textures to be destroyed by now.
+ MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0);
+ for (unsigned int i = 0; i < textures.Length(); ++i) {
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
+ tex->DeallocateDeviceData();
+ }
+}
+
+void CompositorBridgeParent::ForceIsFirstPaint() {
+ if (mWrBridge) {
+ mIsForcedFirstPaint = true;
+ } else {
+ mCompositionManager->ForceIsFirstPaint();
+ }
+}
+
+void CompositorBridgeParent::StopAndClearResources() {
+ if (mForceCompositionTask) {
+ mForceCompositionTask->Cancel();
+ mForceCompositionTask = nullptr;
+ }
+
+ mPaused = true;
+
+ // We need to clear the APZ tree before we destroy the WebRender API below,
+ // because in the case of async scene building that will shut down the updater
+ // thread and we need to run the task before that happens.
+ MOZ_ASSERT((mApzSampler != nullptr) == (mApzcTreeManager != nullptr));
+ MOZ_ASSERT((mApzUpdater != nullptr) == (mApzcTreeManager != nullptr));
+ if (mApzUpdater) {
+ mApzSampler->Destroy();
+ mApzSampler = nullptr;
+ mApzUpdater->ClearTree(mRootLayerTreeID);
+ mApzUpdater = nullptr;
+ mApzcTreeManager = nullptr;
+ }
+
+ // Ensure that the layer manager is destroyed before CompositorBridgeChild.
+ if (mLayerManager) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([this](LayerTreeState* lts, LayersId) -> void {
+ mLayerManager->ClearCachedResources(lts->mRoot);
+ lts->mLayerManager = nullptr;
+ lts->mParent = nullptr;
+ });
+ mLayerManager->Destroy();
+ mLayerManager = nullptr;
+ mCompositionManager = nullptr;
+ }
+
+ if (mWrBridge) {
+ // Ensure we are not holding the sIndirectLayerTreesLock when destroying
+ // the WebRenderBridgeParent instances because it may block on WR.
+ std::vector<RefPtr<WebRenderBridgeParent>> indirectBridgeParents;
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([&](LayerTreeState* lts, LayersId) -> void {
+ if (lts->mWrBridge) {
+ indirectBridgeParents.emplace_back(lts->mWrBridge.forget());
+ }
+ lts->mParent = nullptr;
+ });
+ }
+ for (const RefPtr<WebRenderBridgeParent>& bridge : indirectBridgeParents) {
+ bridge->Destroy();
+ }
+ indirectBridgeParents.clear();
+
+ RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+ // Ensure we are not holding the sIndirectLayerTreesLock here because we
+ // are going to block on WR threads in order to shut it down properly.
+ mWrBridge->Destroy();
+ mWrBridge = nullptr;
+
+ if (api) {
+ // Make extra sure we are done cleaning WebRender up before continuing.
+ // After that we wont have a way to talk to a lot of the webrender parts.
+ api->FlushSceneBuilder();
+ api = nullptr;
+ }
+
+ if (mAsyncImageManager) {
+ mAsyncImageManager->Destroy();
+ // WebRenderAPI should be already destructed
+ mAsyncImageManager = nullptr;
+ }
+ }
+
+ if (mCompositor) {
+ mCompositor->Destroy();
+ mCompositor = nullptr;
+ }
+
+ // This must be destroyed now since it accesses the widget.
+ if (mCompositorScheduler) {
+ mCompositorScheduler->Destroy();
+ mCompositorScheduler = nullptr;
+ }
+
+ if (mOMTASampler) {
+ mOMTASampler->Destroy();
+ mOMTASampler = nullptr;
+ }
+
+ // After this point, it is no longer legal to access the widget.
+ mWidget = nullptr;
+
+ // Clear mAnimationStorage here to ensure that the compositor thread
+ // still exists when we destroy it.
+ mAnimationStorage = nullptr;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvWillClose() {
+ StopAndClearResources();
+ // Once we get the WillClose message, the client side is going to go away
+ // soon and we can't be guaranteed that sending messages will work.
+ mCanSend = false;
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::DeferredDestroy() {
+ MOZ_ASSERT(!NS_IsMainThread());
+ mSelfRef = nullptr;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvPause() {
+ PauseComposition();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvRequestFxrOutput() {
+#ifdef XP_WIN
+ // Continue forwarding the request to the Widget + SwapChain
+ mWidget->AsWindows()->RequestFxrOutput();
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvResume() {
+ ResumeComposition();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvResumeAsync() {
+ ResumeComposition();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvMakeSnapshot(
+ const SurfaceDescriptor& aInSnapshot, const gfx::IntRect& aRect) {
+ RefPtr<DrawTarget> target =
+ GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO);
+ MOZ_ASSERT(target);
+ if (!target) {
+ // We kill the content process rather than have it continue with an invalid
+ // snapshot, that may be too harsh and we could decide to return some sort
+ // of error to the child process and let it deal with it...
+ return IPC_FAIL_NO_REASON(this);
+ }
+ ForceComposeToTarget(target, &aRect);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+CompositorBridgeParent::RecvWaitOnTransactionProcessed() {
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRendering() {
+ if (mWrBridge) {
+ mWrBridge->FlushRendering();
+ return IPC_OK();
+ }
+
+ if (mCompositorScheduler->NeedsComposite()) {
+ CancelCurrentCompositeTask();
+ ForceComposeToTarget(nullptr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRenderingAsync() {
+ if (mWrBridge) {
+ mWrBridge->FlushRendering(false);
+ return IPC_OK();
+ }
+
+ return RecvFlushRendering();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvForcePresent() {
+ if (mWrBridge) {
+ mWrBridge->ScheduleForcedGenerateFrame();
+ }
+ // During the shutdown sequence mLayerManager may be null
+ if (mLayerManager) {
+ mLayerManager->ForcePresent();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyRegionInvalidated(
+ const nsIntRegion& aRegion) {
+ if (mLayerManager) {
+ mLayerManager->AddInvalidRegion(aRegion);
+ }
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::Invalidate() {
+ if (mLayerManager) {
+ mLayerManager->InvalidateAll();
+ }
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvStartFrameTimeRecording(
+ const int32_t& aBufferSize, uint32_t* aOutStartIndex) {
+ if (mLayerManager) {
+ *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize);
+ } else if (mWrBridge) {
+ *aOutStartIndex = mWrBridge->StartFrameTimeRecording(aBufferSize);
+ } else {
+ *aOutStartIndex = 0;
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvStopFrameTimeRecording(
+ const uint32_t& aStartIndex, nsTArray<float>* intervals) {
+ if (mLayerManager) {
+ mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals);
+ } else if (mWrBridge) {
+ mWrBridge->StopFrameTimeRecording(aStartIndex, *intervals);
+ }
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) {
+ mCanSend = false;
+
+ StopAndClearResources();
+
+ RemoveCompositor(mCompositorBridgeID);
+
+ mCompositionManager = nullptr;
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees.erase(mRootLayerTreeID);
+ }
+
+ // There are chances that the ref count reaches zero on the main thread
+ // shortly after this function returns while some ipdl code still needs to run
+ // on this thread. We must keep the compositor parent alive untill the code
+ // handling message reception is finished on this thread.
+ mSelfRef = this;
+ NS_GetCurrentThread()->Dispatch(
+ NewRunnableMethod("layers::CompositorBridgeParent::DeferredDestroy", this,
+ &CompositorBridgeParent::DeferredDestroy));
+}
+
+void CompositorBridgeParent::ScheduleRenderOnCompositorThread() {
+ MOZ_ASSERT(CompositorThread());
+ CompositorThread()->Dispatch(
+ NewRunnableMethod("layers::CompositorBridgeParent::ScheduleComposition",
+ this, &CompositorBridgeParent::ScheduleComposition));
+}
+
+void CompositorBridgeParent::PauseComposition() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "PauseComposition() can only be called on the compositor thread");
+
+ MonitorAutoLock lock(mPauseCompositionMonitor);
+
+ if (!mPaused) {
+ mPaused = true;
+
+ TimeStamp now = TimeStamp::Now();
+ if (mCompositor) {
+ mCompositor->Pause();
+ DidComposite(VsyncId(), now, now);
+ } else if (mWrBridge) {
+ mWrBridge->Pause();
+ NotifyPipelineRendered(mWrBridge->PipelineId(),
+ mWrBridge->GetCurrentEpoch(), VsyncId(), now, now,
+ now);
+ }
+ }
+
+ // if anyone's waiting to make sure that composition really got paused, tell
+ // them
+ lock.NotifyAll();
+}
+
+void CompositorBridgeParent::ResumeComposition() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "ResumeComposition() can only be called on the compositor thread");
+
+ MonitorAutoLock lock(mResumeCompositionMonitor);
+
+ bool resumed =
+ mOptions.UseWebRender() ? mWrBridge->Resume() : mCompositor->Resume();
+ if (!resumed) {
+#ifdef MOZ_WIDGET_ANDROID
+ // We can't get a surface. This could be because the activity changed
+ // between the time resume was scheduled and now.
+ __android_log_print(
+ ANDROID_LOG_INFO, "CompositorBridgeParent",
+ "Unable to renew compositor surface; remaining in paused state");
+#endif
+ lock.NotifyAll();
+ return;
+ }
+
+ mPaused = false;
+
+ Invalidate();
+ mCompositorScheduler->ForceComposeToTarget(nullptr, nullptr);
+
+ // if anyone's waiting to make sure that composition really got resumed, tell
+ // them
+ lock.NotifyAll();
+}
+
+void CompositorBridgeParent::ForceComposition() {
+ // Cancel the orientation changed state to force composition
+ mForceCompositionTask = nullptr;
+ ScheduleRenderOnCompositorThread();
+}
+
+void CompositorBridgeParent::CancelCurrentCompositeTask() {
+ mCompositorScheduler->CancelCurrentCompositeTask();
+}
+
+void CompositorBridgeParent::SetEGLSurfaceRect(int x, int y, int width,
+ int height) {
+ NS_ASSERTION(mUseExternalSurfaceSize,
+ "Compositor created without UseExternalSurfaceSize provided");
+ mEGLSurfaceSize.SizeTo(width, height);
+ if (mCompositor) {
+ mCompositor->SetDestinationSurfaceSize(
+ gfx::IntSize(mEGLSurfaceSize.width, mEGLSurfaceSize.height));
+ if (mCompositor->AsCompositorOGL()) {
+ mCompositor->AsCompositorOGL()->SetSurfaceOrigin(ScreenIntPoint(x, y));
+ }
+ }
+}
+
+void CompositorBridgeParent::ResumeCompositionAndResize(int x, int y, int width,
+ int height) {
+ SetEGLSurfaceRect(x, y, width, height);
+ ResumeComposition();
+}
+
+void CompositorBridgeParent::UpdatePaintTime(LayerTransactionParent* aLayerTree,
+ const TimeDuration& aPaintTime) {
+ // We get a lot of paint timings for things with empty transactions.
+ if (!mLayerManager || aPaintTime.ToMilliseconds() < 1.0) {
+ return;
+ }
+
+ mLayerManager->SetPaintTime(aPaintTime);
+}
+
+void CompositorBridgeParent::RegisterPayloads(
+ LayerTransactionParent* aLayerTree,
+ const nsTArray<CompositionPayload>& aPayload) {
+ // We get a lot of paint timings for things with empty transactions.
+ if (!mLayerManager) {
+ return;
+ }
+
+ mLayerManager->RegisterPayloads(aPayload);
+}
+
+void CompositorBridgeParent::NotifyShadowTreeTransaction(
+ LayersId aId, bool aIsFirstPaint, const FocusTarget& aFocusTarget,
+ bool aScheduleComposite, uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction, bool aHitTestUpdate) {
+ if (!aIsRepeatTransaction && mLayerManager && mLayerManager->GetRoot()) {
+ // Process plugin data here to give time for them to update before the next
+ // composition.
+ bool pluginsUpdatedFlag = true;
+ AutoResolveRefLayers resolve(mCompositionManager, this, nullptr,
+ &pluginsUpdatedFlag);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // If plugins haven't been updated, stop waiting.
+ if (!pluginsUpdatedFlag) {
+ mWaitForPluginsUntil = TimeStamp();
+ mHaveBlockedForPlugins = false;
+ }
+#endif
+
+ if (mApzUpdater) {
+ mApzUpdater->UpdateFocusState(mRootLayerTreeID, aId, aFocusTarget);
+ if (aHitTestUpdate) {
+ mApzUpdater->UpdateHitTestingTree(
+ mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber);
+ }
+ }
+
+ mLayerManager->NotifyShadowTreeTransaction();
+ }
+ if (aScheduleComposite) {
+ ScheduleComposition();
+ }
+}
+
+void CompositorBridgeParent::ScheduleComposition() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mPaused) {
+ return;
+ }
+
+ if (mWrBridge) {
+ mWrBridge->ScheduleGenerateFrame();
+ } else {
+ mCompositorScheduler->ScheduleComposition();
+ }
+}
+
+// Go down the composite layer tree, setting properties to match their
+// content-side counterparts.
+/* static */
+void CompositorBridgeParent::SetShadowProperties(Layer* aLayer) {
+ ForEachNode<ForwardIterator>(aLayer, [](Layer* layer) {
+ if (Layer* maskLayer = layer->GetMaskLayer()) {
+ SetShadowProperties(maskLayer);
+ }
+ for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
+ SetShadowProperties(layer->GetAncestorMaskLayerAt(i));
+ }
+
+ // FIXME: Bug 717688 -- Do these updates in
+ // LayerTransactionParent::RecvUpdate.
+ HostLayer* layerCompositor = layer->AsHostLayer();
+ // Set the layerComposite's base transform to the layer's base transform.
+ const auto& animations = layer->GetPropertyAnimationGroups();
+ // If there is any animation, the animation value will override
+ // non-animated value later, so we don't need to set the non-animated
+ // value here.
+ if (animations.IsEmpty()) {
+ layerCompositor->SetShadowBaseTransform(layer->GetBaseTransform());
+ layerCompositor->SetShadowTransformSetByAnimation(false);
+ layerCompositor->SetShadowOpacity(layer->GetOpacity());
+ layerCompositor->SetShadowOpacitySetByAnimation(false);
+ }
+ layerCompositor->SetShadowVisibleRegion(layer->GetVisibleRegion());
+ layerCompositor->SetShadowClipRect(layer->GetClipRect());
+ });
+}
+
+void CompositorBridgeParent::CompositeToTarget(VsyncId aId, DrawTarget* aTarget,
+ const gfx::IntRect* aRect) {
+ AUTO_PROFILER_TRACING_MARKER("Paint", "Composite", GRAPHICS);
+ AUTO_PROFILER_LABEL("CompositorBridgeParent::CompositeToTarget", GRAPHICS);
+ PerfStats::AutoMetricRecording<PerfStats::Metric::Compositing> autoRecording;
+
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "Composite can only be called on the compositor thread");
+ TimeStamp start = TimeStamp::Now();
+
+ if (!CanComposite()) {
+ TimeStamp end = TimeStamp::Now();
+ DidComposite(aId, start, end);
+ return;
+ }
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ if (!mWaitForPluginsUntil.IsNull() && mWaitForPluginsUntil > start) {
+ mHaveBlockedForPlugins = true;
+ ScheduleComposition();
+ return;
+ }
+#endif
+
+ /*
+ * AutoResolveRefLayers handles two tasks related to Windows and Linux
+ * plugin window management:
+ * 1) calculating if we have remote content in the view. If we do not have
+ * remote content, all plugin windows for this CompositorBridgeParent (window)
+ * can be hidden since we do not support plugins in chrome when running
+ * under e10s.
+ * 2) Updating plugin position, size, and clip. We do this here while the
+ * remote layer tree is hooked up to to chrome layer tree. This is needed
+ * since plugin clipping can depend on chrome (for example, due to tab modal
+ * prompts). Updates in step 2 are applied via an async ipc message sent
+ * to the main thread.
+ */
+ bool hasRemoteContent = false;
+ bool updatePluginsFlag = true;
+ AutoResolveRefLayers resolve(mCompositionManager, this, &hasRemoteContent,
+ &updatePluginsFlag);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // We do not support plugins in local content. When switching tabs
+ // to local pages, hide every plugin associated with the window.
+ if (!hasRemoteContent && gfxVars::BrowserTabsRemoteAutostart() &&
+ mCachedPluginData.Length()) {
+ Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey());
+ mCachedPluginData.Clear();
+ }
+#endif
+
+ nsCString none;
+ if (aTarget) {
+ mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect);
+ } else {
+ mLayerManager->BeginTransaction(none);
+ }
+
+ SetShadowProperties(mLayerManager->GetRoot());
+
+ if (mForceCompositionTask && !mOverrideComposeReadiness) {
+ if (mCompositionManager->ReadyForCompose()) {
+ mForceCompositionTask->Cancel();
+ mForceCompositionTask = nullptr;
+ } else {
+ return;
+ }
+ }
+
+ mCompositionManager->ComputeRotation();
+
+ SampleTime time = mTestTime ? SampleTime::FromTest(*mTestTime)
+ : mCompositorScheduler->GetLastComposeTime();
+ bool requestNextFrame =
+ mCompositionManager->TransformShadowTree(time, mVsyncRate);
+
+ if (requestNextFrame) {
+ ScheduleComposition();
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // If we have visible windowed plugins then we need to wait for content (and
+ // then the plugins) to have been updated by the active animation.
+ if (!mPluginWindowsHidden && mCachedPluginData.Length()) {
+ mWaitForPluginsUntil =
+ mCompositorScheduler->GetLastComposeTime().Time() + (mVsyncRate * 2);
+ }
+#endif
+ }
+
+ RenderTraceLayers(mLayerManager->GetRoot(), "0000");
+
+ if (StaticPrefs::layers_dump_host_layers() || StaticPrefs::layers_dump()) {
+ printf_stderr("Painting --- compositing layer tree:\n");
+ mLayerManager->Dump(/* aSorted = */ true);
+ }
+ mLayerManager->SetDebugOverlayWantsNextFrame(false);
+ mLayerManager->EndTransaction(time.Time());
+
+ if (!aTarget) {
+ TimeStamp end = TimeStamp::Now();
+ DidComposite(aId, start, end);
+ }
+
+ // We're not really taking advantage of the stored composite-again-time here.
+ // We might be able to skip the next few composites altogether. However,
+ // that's a bit complex to implement and we'll get most of the advantage
+ // by skipping compositing when we detect there's nothing invalid. This is why
+ // we do "composite until" rather than "composite again at".
+ //
+ // TODO(bug 1328602) Figure out what we should do here with the render thread.
+ if (!mLayerManager->GetCompositeUntilTime().IsNull() ||
+ mLayerManager->DebugOverlayWantsNextFrame()) {
+ ScheduleComposition();
+ }
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ TimeDuration executionTime =
+ TimeStamp::Now() - mCompositorScheduler->GetLastComposeTime().Time();
+ TimeDuration frameBudget = TimeDuration::FromMilliseconds(15);
+ int32_t frameRate = CalculateCompositionFrameRate();
+ if (frameRate > 0) {
+ frameBudget = TimeDuration::FromSeconds(1.0 / frameRate);
+ }
+ if (executionTime > frameBudget) {
+ printf_stderr("Compositor: Composite execution took %4.1f ms\n",
+ executionTime.ToMilliseconds());
+ }
+#endif
+
+ // 0 -> Full-tilt composite
+ if (StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
+ mLayerManager->AlwaysScheduleComposite()) {
+ // Special full-tilt composite mode for performance testing
+ ScheduleComposition();
+ }
+
+ // TODO(bug 1328602) Need an equivalent that works with the rende thread.
+ mLayerManager->SetCompositionTime(TimeStamp());
+
+ mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME,
+ start);
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvRemotePluginsReady() {
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ mWaitForPluginsUntil = TimeStamp();
+ if (mHaveBlockedForPlugins) {
+ mHaveBlockedForPlugins = false;
+ ForceComposeToTarget(nullptr);
+ } else {
+ ScheduleComposition();
+ }
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "CompositorBridgeParent::RecvRemotePluginsReady calls "
+ "unexpected on this platform.");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+void CompositorBridgeParent::ForceComposeToTarget(DrawTarget* aTarget,
+ const gfx::IntRect* aRect) {
+ AUTO_PROFILER_LABEL("CompositorBridgeParent::ForceComposeToTarget", GRAPHICS);
+
+ AutoRestore<bool> override(mOverrideComposeReadiness);
+ mOverrideComposeReadiness = true;
+ mCompositorScheduler->ForceComposeToTarget(aTarget, aRect);
+}
+
+PAPZCTreeManagerParent* CompositorBridgeParent::AllocPAPZCTreeManagerParent(
+ const LayersId& aLayersId) {
+ // This should only ever get called in the GPU process.
+ MOZ_ASSERT(XRE_IsGPUProcess());
+ // We should only ever get this if APZ is enabled in this compositor.
+ MOZ_ASSERT(mOptions.UseAPZ());
+ // The mApzcTreeManager and mApzUpdater should have been created via
+ // RecvInitialize()
+ MOZ_ASSERT(mApzcTreeManager);
+ MOZ_ASSERT(mApzUpdater);
+ // The main process should pass in 0 because we assume mRootLayerTreeID
+ MOZ_ASSERT(!aLayersId.IsValid());
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state =
+ sIndirectLayerTrees[mRootLayerTreeID];
+ MOZ_ASSERT(state.mParent.get() == this);
+ MOZ_ASSERT(!state.mApzcTreeManagerParent);
+ state.mApzcTreeManagerParent = new APZCTreeManagerParent(
+ mRootLayerTreeID, mApzcTreeManager, mApzUpdater);
+
+ return state.mApzcTreeManagerParent;
+}
+
+bool CompositorBridgeParent::DeallocPAPZCTreeManagerParent(
+ PAPZCTreeManagerParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+void CompositorBridgeParent::AllocateAPZCTreeManagerParent(
+ const MonitorAutoLock& aProofOfLayerTreeStateLock,
+ const LayersId& aLayersId, LayerTreeState& aState) {
+ MOZ_ASSERT(aState.mParent == this);
+ MOZ_ASSERT(mApzcTreeManager);
+ MOZ_ASSERT(mApzUpdater);
+ MOZ_ASSERT(!aState.mApzcTreeManagerParent);
+ aState.mApzcTreeManagerParent =
+ new APZCTreeManagerParent(aLayersId, mApzcTreeManager, mApzUpdater);
+}
+
+PAPZParent* CompositorBridgeParent::AllocPAPZParent(const LayersId& aLayersId) {
+ // This is the CompositorBridgeParent for a window, and so should only be
+ // creating a PAPZ instance if it lives in the GPU process. Instances that
+ // live in the UI process should going through SetControllerForLayerTree.
+ MOZ_RELEASE_ASSERT(XRE_IsGPUProcess());
+
+ // We should only ever get this if APZ is enabled on this compositor.
+ MOZ_RELEASE_ASSERT(mOptions.UseAPZ());
+
+ // The main process should pass in 0 because we assume mRootLayerTreeID
+ MOZ_RELEASE_ASSERT(!aLayersId.IsValid());
+
+ RemoteContentController* controller = new RemoteContentController();
+
+ // Increment the controller's refcount before we return it. This will keep the
+ // controller alive until it is released by IPDL in DeallocPAPZParent.
+ controller->AddRef();
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state =
+ sIndirectLayerTrees[mRootLayerTreeID];
+ MOZ_RELEASE_ASSERT(!state.mController);
+ state.mController = controller;
+
+ return controller;
+}
+
+bool CompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor) {
+ RemoteContentController* controller =
+ static_cast<RemoteContentController*>(aActor);
+ controller->Release();
+ return true;
+}
+
+RefPtr<APZSampler> CompositorBridgeParent::GetAPZSampler() const {
+ return mApzSampler;
+}
+
+RefPtr<APZUpdater> CompositorBridgeParent::GetAPZUpdater() const {
+ return mApzUpdater;
+}
+
+RefPtr<OMTASampler> CompositorBridgeParent::GetOMTASampler() const {
+ return mOMTASampler;
+}
+
+CompositorBridgeParent*
+CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ const LayersId& aLayersId) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ return sIndirectLayerTrees[aLayersId].mParent;
+}
+
+/*static*/
+RefPtr<CompositorBridgeParent>
+CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(
+ const wr::WindowId& aWindowId) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ for (auto it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end();
+ it++) {
+ LayerTreeState* state = &it->second;
+ if (!state->mWrBridge) {
+ continue;
+ }
+ // state->mWrBridge might be a root WebRenderBridgeParent or one of a
+ // content process, but in either case the state->mParent will be the same.
+ // So we don't need to distinguish between the two.
+ if (RefPtr<wr::WebRenderAPI> api = state->mWrBridge->GetWebRenderAPI()) {
+ if (api->GetId() == aWindowId) {
+ return state->mParent;
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool CompositorBridgeParent::CanComposite() {
+ return mLayerManager && mLayerManager->GetRoot() && !mPaused;
+}
+
+void CompositorBridgeParent::ScheduleRotationOnCompositorThread(
+ const TargetConfig& aTargetConfig, bool aIsFirstPaint) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ if (!aIsFirstPaint && !mCompositionManager->IsFirstPaint() &&
+ mCompositionManager->RequiresReorientation(aTargetConfig.orientation())) {
+ if (mForceCompositionTask != nullptr) {
+ mForceCompositionTask->Cancel();
+ }
+ RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod(
+ "layers::CompositorBridgeParent::ForceComposition", this,
+ &CompositorBridgeParent::ForceComposition);
+ mForceCompositionTask = task;
+ if (StaticPrefs::layers_orientation_sync_timeout() == 0) {
+ CompositorThread()->Dispatch(task.forget());
+ } else {
+ CompositorThread()->DelayedDispatch(
+ task.forget(), StaticPrefs::layers_orientation_sync_timeout());
+ }
+ }
+}
+
+void CompositorBridgeParent::ShadowLayersUpdated(
+ LayerTransactionParent* aLayerTree, const TransactionInfo& aInfo,
+ bool aHitTestUpdate) {
+ const TargetConfig& targetConfig = aInfo.targetConfig();
+
+ ScheduleRotationOnCompositorThread(targetConfig, aInfo.isFirstPaint());
+
+ // Instruct the LayerManager to update its render bounds now. Since all the
+ // orientation change, dimension change would be done at the stage, update the
+ // size here is free of race condition.
+ mLayerManager->UpdateRenderBounds(targetConfig.naturalBounds());
+ mLayerManager->SetRegionToClear(targetConfig.clearRegion());
+ if (mLayerManager->GetCompositor()) {
+ mLayerManager->GetCompositor()->SetScreenRotation(targetConfig.rotation());
+ }
+
+ mCompositionManager->Updated(aInfo.isFirstPaint(), targetConfig);
+ Layer* root = aLayerTree->GetRoot();
+ mLayerManager->SetRoot(root);
+
+ if (mApzUpdater && !aInfo.isRepeatTransaction()) {
+ mApzUpdater->UpdateFocusState(mRootLayerTreeID, mRootLayerTreeID,
+ aInfo.focusTarget());
+
+ if (aHitTestUpdate) {
+ AutoResolveRefLayers resolve(mCompositionManager);
+
+ mApzUpdater->UpdateHitTestingTree(root, aInfo.isFirstPaint(),
+ mRootLayerTreeID,
+ aInfo.paintSequenceNumber());
+ }
+ }
+
+ // The transaction ID might get reset to 1 if the page gets reloaded, see
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41
+ // Otherwise, it should be continually increasing.
+ MOZ_ASSERT(aInfo.id() == TransactionId{1} ||
+ aInfo.id() > mPendingTransaction);
+ mPendingTransaction = aInfo.id();
+ mRefreshStartTime = aInfo.refreshStart();
+ mTxnStartTime = aInfo.transactionStart();
+ mFwdTime = aInfo.fwdTime();
+ RegisterPayloads(aLayerTree, aInfo.payload());
+
+ if (root) {
+ SetShadowProperties(root);
+ }
+ if (aInfo.scheduleComposite()) {
+ ScheduleComposition();
+ if (mPaused) {
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(VsyncId(), now, now);
+ }
+ }
+ mLayerManager->NotifyShadowTreeTransaction();
+}
+
+void CompositorBridgeParent::ScheduleComposite(
+ LayerTransactionParent* aLayerTree) {
+ ScheduleComposition();
+}
+
+bool CompositorBridgeParent::SetTestSampleTime(const LayersId& aId,
+ const TimeStamp& aTime) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ if (aTime.IsNull()) {
+ return false;
+ }
+
+ mTestTime = Some(aTime);
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetTestSampleTime(mTestTime);
+ }
+
+ if (mWrBridge) {
+ mWrBridge->FlushRendering();
+ return true;
+ }
+
+ bool testComposite =
+ mCompositionManager && mCompositorScheduler->NeedsComposite();
+
+ // Update but only if we were already scheduled to animate
+ if (testComposite) {
+ AutoResolveRefLayers resolve(mCompositionManager);
+ bool requestNextFrame = mCompositionManager->TransformShadowTree(
+ SampleTime::FromTest(aTime), mVsyncRate);
+ if (!requestNextFrame) {
+ CancelCurrentCompositeTask();
+ // Pretend we composited in case someone is wating for this event.
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(VsyncId(), now, now);
+ }
+ }
+
+ return true;
+}
+
+void CompositorBridgeParent::LeaveTestMode(const LayersId& aId) {
+ mTestTime = Nothing();
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetTestSampleTime(mTestTime);
+ }
+}
+
+void CompositorBridgeParent::ApplyAsyncProperties(
+ LayerTransactionParent* aLayerTree, TransformsToSkip aSkip) {
+ // NOTE: This should only be used for testing. For example, when mTestTime is
+ // non-empty, or when called from test-only methods like
+ // LayerTransactionParent::RecvGetAnimationTransform.
+
+ // Synchronously update the layer tree
+ if (aLayerTree->GetRoot()) {
+ AutoResolveRefLayers resolve(mCompositionManager);
+ SetShadowProperties(mLayerManager->GetRoot());
+
+ SampleTime time;
+ if (mTestTime) {
+ time = SampleTime::FromTest(*mTestTime);
+ } else {
+ time = mCompositorScheduler->GetLastComposeTime();
+ }
+ bool requestNextFrame =
+ mCompositionManager->TransformShadowTree(time, mVsyncRate, aSkip);
+ if (!requestNextFrame) {
+ CancelCurrentCompositeTask();
+ // Pretend we composited in case someone is waiting for this event.
+ TimeStamp now = TimeStamp::Now();
+ DidComposite(VsyncId(), now, now);
+ }
+ }
+}
+
+CompositorAnimationStorage* CompositorBridgeParent::GetAnimationStorage() {
+ if (!mAnimationStorage) {
+ mAnimationStorage = new CompositorAnimationStorage(this);
+ }
+ return mAnimationStorage;
+}
+
+void CompositorBridgeParent::NotifyJankedAnimations(
+ const JankedAnimations& aJankedAnimations) {
+ MOZ_ASSERT(!aJankedAnimations.empty());
+
+ if (StaticPrefs::layout_animation_prerender_partial_jank()) {
+ return;
+ }
+
+ for (const auto& entry : aJankedAnimations) {
+ const LayersId& layersId = entry.first;
+ const nsTArray<uint64_t>& animations = entry.second;
+ if (layersId == mRootLayerTreeID) {
+ if (mLayerManager) {
+ Unused << SendNotifyJankedAnimations(LayersId{0}, animations);
+ }
+ // It unlikely happens multiple processes have janked animations at same
+ // time, so it should be fine with enumerating sIndirectLayerTrees every
+ // time.
+ } else if (const LayerTreeState* state = GetIndirectShadowTree(layersId)) {
+ if (ContentCompositorBridgeParent* cpcp =
+ state->mContentCompositorBridgeParent) {
+ Unused << cpcp->SendNotifyJankedAnimations(layersId, animations);
+ }
+ }
+ }
+}
+
+void CompositorBridgeParent::SetTestAsyncScrollOffset(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) {
+ if (mApzUpdater) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ mApzUpdater->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
+ }
+}
+
+void CompositorBridgeParent::SetTestAsyncZoom(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) {
+ if (mApzUpdater) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ mApzUpdater->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
+ }
+}
+
+void CompositorBridgeParent::FlushApzRepaints(const LayersId& aLayersId) {
+ MOZ_ASSERT(mApzUpdater);
+ MOZ_ASSERT(aLayersId.IsValid());
+ mApzUpdater->RunOnControllerThread(
+ aLayersId, NS_NewRunnableFunction(
+ "layers::CompositorBridgeParent::FlushApzRepaints",
+ [=]() { APZCTreeManager::FlushApzRepaints(aLayersId); }));
+}
+
+void CompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) {
+ if (mApzUpdater) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ mApzUpdater->GetAPZTestData(aLayersId, aOutData);
+ }
+}
+
+void CompositorBridgeParent::GetFrameUniformity(const LayersId& aLayersId,
+ FrameUniformityData* aOutData) {
+ if (mCompositionManager) {
+ mCompositionManager->GetFrameUniformity(aOutData);
+ }
+}
+
+void CompositorBridgeParent::SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) {
+ if (!mApzcTreeManager || !mApzUpdater) {
+ return;
+ }
+ // Need to specifically bind this since it's overloaded.
+ void (APZCTreeManager::*setTargetApzcFunc)(
+ uint64_t, const nsTArray<ScrollableLayerGuid>&) =
+ &APZCTreeManager::SetTargetAPZC;
+ RefPtr<Runnable> task =
+ NewRunnableMethod<uint64_t,
+ StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
+ "layers::CompositorBridgeParent::SetConfirmedTargetAPZC",
+ mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId,
+ std::move(aTargets));
+ mApzUpdater->RunOnControllerThread(aLayersId, task.forget());
+}
+
+void CompositorBridgeParent::SetFixedLayerMargins(ScreenIntCoord aTop,
+ ScreenIntCoord aBottom) {
+ if (AsyncCompositionManager* manager = GetCompositionManager(nullptr)) {
+ manager->SetFixedLayerMargins(aTop, aBottom);
+ }
+
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetFixedLayerMargins(aTop, aBottom);
+ }
+
+ Invalidate();
+ ScheduleComposition();
+}
+
+void CompositorBridgeParent::InitializeLayerManager(
+ const nsTArray<LayersBackend>& aBackendHints) {
+ NS_ASSERTION(!mLayerManager, "Already initialised mLayerManager");
+ NS_ASSERTION(!mCompositor, "Already initialised mCompositor");
+
+ if (!InitializeAdvancedLayers(aBackendHints, nullptr)) {
+ mCompositor = NewCompositor(aBackendHints);
+ if (!mCompositor) {
+ return;
+ }
+#ifdef XP_WIN
+ if (mCompositor->AsBasicCompositor() && XRE_IsGPUProcess()) {
+ // BasicCompositor does not use CompositorWindow,
+ // then if CompositorWindow exists, it needs to be destroyed.
+ mWidget->AsWindows()->DestroyCompositorWindow();
+ }
+#endif
+ mLayerManager = new LayerManagerComposite(mCompositor);
+ }
+ mLayerManager->SetCompositorBridgeID(mCompositorBridgeID);
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[mRootLayerTreeID].mLayerManager = mLayerManager;
+}
+
+bool CompositorBridgeParent::InitializeAdvancedLayers(
+ const nsTArray<LayersBackend>& aBackendHints,
+ TextureFactoryIdentifier* aOutIdentifier) {
+#ifdef XP_WIN
+ if (!mOptions.UseAdvancedLayers()) {
+ return false;
+ }
+
+ // Currently LayerManagerMLGPU hardcodes a D3D11 device, so we reject using
+ // AL if LAYERS_D3D11 isn't in the backend hints.
+ if (!aBackendHints.Contains(LayersBackend::LAYERS_D3D11)) {
+ return false;
+ }
+
+ RefPtr<LayerManagerMLGPU> manager = new LayerManagerMLGPU(mWidget);
+ if (!manager->Initialize()) {
+ return false;
+ }
+
+ if (aOutIdentifier) {
+ *aOutIdentifier = manager->GetTextureFactoryIdentifier();
+ }
+ mLayerManager = manager;
+ return true;
+#else
+ return false;
+#endif
+}
+
+RefPtr<Compositor> CompositorBridgeParent::NewCompositor(
+ const nsTArray<LayersBackend>& aBackendHints) {
+ for (size_t i = 0; i < aBackendHints.Length(); ++i) {
+ RefPtr<Compositor> compositor;
+ if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) {
+ compositor =
+ new CompositorOGL(this, mWidget, mEGLSurfaceSize.width,
+ mEGLSurfaceSize.height, mUseExternalSurfaceSize);
+ } else if (aBackendHints[i] == LayersBackend::LAYERS_BASIC) {
+#ifdef MOZ_WIDGET_GTK
+ if (gfxVars::UseXRender()) {
+ compositor = new X11BasicCompositor(this, mWidget);
+ } else
+#endif
+ {
+ compositor = new BasicCompositor(this, mWidget);
+ }
+#ifdef XP_WIN
+ } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) {
+ compositor = new CompositorD3D11(this, mWidget);
+#endif
+ }
+ nsCString failureReason;
+
+ // Some software GPU emulation implementations will happily try to create
+ // unreasonably big surfaces and then fail in awful ways.
+ // Let's at least limit this to the default max texture size we use for
+ // content, anything larger than that will fail to render on the content
+ // side anyway. We can revisit this value and make it even tighter if need
+ // be.
+ const int max_fb_size = 32767;
+ const LayoutDeviceIntSize size = mWidget->GetClientSize();
+ if (size.width > max_fb_size || size.height > max_fb_size) {
+ failureReason = "FEATURE_FAILURE_MAX_FRAMEBUFFER_SIZE";
+ return nullptr;
+ }
+
+ MOZ_ASSERT(!gfxVars::UseWebRender() ||
+ aBackendHints[i] == LayersBackend::LAYERS_BASIC);
+ if (compositor && compositor->Initialize(&failureReason)) {
+ if (failureReason.IsEmpty()) {
+ failureReason = "SUCCESS";
+ }
+
+ // should only report success here
+ if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) {
+ Telemetry::Accumulate(Telemetry::OPENGL_COMPOSITING_FAILURE_ID,
+ failureReason);
+ }
+#ifdef XP_WIN
+ else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) {
+ Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID,
+ failureReason);
+ }
+#endif
+
+ return compositor;
+ }
+
+ // report any failure reasons here
+ if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) {
+ gfxCriticalNote << "[OPENGL] Failed to init compositor with reason: "
+ << failureReason.get();
+ Telemetry::Accumulate(Telemetry::OPENGL_COMPOSITING_FAILURE_ID,
+ failureReason);
+ }
+#ifdef XP_WIN
+ else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) {
+ gfxCriticalNote << "[D3D11] Failed to init compositor with reason: "
+ << failureReason.get();
+ Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID,
+ failureReason);
+ }
+#endif
+ }
+
+ return nullptr;
+}
+
+PLayerTransactionParent* CompositorBridgeParent::AllocPLayerTransactionParent(
+ const nsTArray<LayersBackend>& aBackendHints, const LayersId& aId) {
+ MOZ_ASSERT(!aId.IsValid());
+
+#ifdef XP_WIN
+ // This is needed to avoid freezing the window on a device crash on double
+ // buffering, see bug 1549674.
+ if (gfxVars::UseDoubleBufferingWithCompositor() && XRE_IsGPUProcess() &&
+ aBackendHints.Contains(LayersBackend::LAYERS_D3D11)) {
+ mWidget->AsWindows()->EnsureCompositorWindow();
+ }
+#endif
+
+ InitializeLayerManager(aBackendHints);
+
+ if (!mLayerManager) {
+ NS_WARNING("Failed to initialise Compositor");
+ LayerTransactionParent* p = new LayerTransactionParent(
+ /* aManager */ nullptr, this, /* aAnimStorage */ nullptr,
+ mRootLayerTreeID, mVsyncRate);
+ p->AddIPDLReference();
+ return p;
+ }
+
+ mCompositionManager = new AsyncCompositionManager(this, mLayerManager);
+
+ LayerTransactionParent* p = new LayerTransactionParent(
+ mLayerManager, this, GetAnimationStorage(), mRootLayerTreeID, mVsyncRate);
+ p->AddIPDLReference();
+ return p;
+}
+
+bool CompositorBridgeParent::DeallocPLayerTransactionParent(
+ PLayerTransactionParent* actor) {
+ static_cast<LayerTransactionParent*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+CompositorBridgeParent* CompositorBridgeParent::GetCompositorBridgeParent(
+ uint64_t id) {
+ AssertIsInCompositorThread();
+ CompositorMap::iterator it = sCompositorMap->find(id);
+ return it != sCompositorMap->end() ? it->second : nullptr;
+}
+
+void CompositorBridgeParent::AddCompositor(CompositorBridgeParent* compositor,
+ uint64_t* outID) {
+ AssertIsInCompositorThread();
+
+ static uint64_t sNextID = 1;
+
+ ++sNextID;
+ (*sCompositorMap)[sNextID] = compositor;
+ *outID = sNextID;
+}
+
+CompositorBridgeParent* CompositorBridgeParent::RemoveCompositor(uint64_t id) {
+ AssertIsInCompositorThread();
+
+ CompositorMap::iterator it = sCompositorMap->find(id);
+ if (it == sCompositorMap->end()) {
+ return nullptr;
+ }
+ CompositorBridgeParent* retval = it->second;
+ sCompositorMap->erase(it);
+ return retval;
+}
+
+void CompositorBridgeParent::NotifyVsync(const VsyncEvent& aVsync,
+ const LayersId& aLayersId) {
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ auto it = sIndirectLayerTrees.find(aLayersId);
+ if (it == sIndirectLayerTrees.end()) return;
+
+ CompositorBridgeParent* cbp = it->second.mParent;
+ if (!cbp || !cbp->mWidget) return;
+
+ RefPtr<VsyncObserver> obs = cbp->mWidget->GetVsyncObserver();
+ if (!obs) return;
+
+ obs->NotifyVsync(aVsync);
+}
+
+/* static */
+void CompositorBridgeParent::ScheduleForcedComposition(
+ const LayersId& aLayersId) {
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ auto it = sIndirectLayerTrees.find(aLayersId);
+ if (it == sIndirectLayerTrees.end()) {
+ return;
+ }
+
+ CompositorBridgeParent* cbp = it->second.mParent;
+ if (!cbp || !cbp->mWidget) {
+ return;
+ }
+
+ if (cbp->mWrBridge) {
+ cbp->mWrBridge->ScheduleForcedGenerateFrame();
+ } else if (cbp->CanComposite()) {
+ cbp->mCompositorScheduler->ScheduleComposition();
+ }
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyChildCreated(
+ const LayersId& child, CompositorOptions* aOptions) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ NotifyChildCreated(child);
+ *aOptions = mOptions;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyChildRecreated(
+ const LayersId& aChild, CompositorOptions* aOptions) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+
+ if (sIndirectLayerTrees.find(aChild) != sIndirectLayerTrees.end()) {
+ NS_WARNING("Invalid to register the same layer tree twice");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ NotifyChildCreated(aChild);
+ *aOptions = mOptions;
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::NotifyChildCreated(LayersId aChild) {
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ sIndirectLayerTrees[aChild].mParent = this;
+ sIndirectLayerTrees[aChild].mLayerManager = mLayerManager;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvMapAndNotifyChildCreated(
+ const LayersId& aChild, const base::ProcessId& aOwnerPid,
+ CompositorOptions* aOptions) {
+ // We only use this message when the remote compositor is in the GPU process.
+ // It is harmless to call it, though.
+ MOZ_ASSERT(XRE_IsGPUProcess());
+
+ LayerTreeOwnerTracker::Get()->Map(aChild, aOwnerPid);
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ NotifyChildCreated(aChild);
+ *aOptions = mOptions;
+ return IPC_OK();
+}
+
+enum class CompositorOptionsChangeKind {
+ eSupported,
+ eBestEffort,
+ eUnsupported
+};
+
+static CompositorOptionsChangeKind ClassifyCompositorOptionsChange(
+ const CompositorOptions& aOld, const CompositorOptions& aNew) {
+ if (aOld == aNew) {
+ return CompositorOptionsChangeKind::eSupported;
+ }
+ if (aOld.UseAdvancedLayers() == aNew.UseAdvancedLayers() &&
+ aOld.UseWebRender() == aNew.UseWebRender() &&
+ aOld.InitiallyPaused() == aNew.InitiallyPaused()) {
+ // Only APZ enablement changed.
+ return CompositorOptionsChangeKind::eBestEffort;
+ }
+ return CompositorOptionsChangeKind::eUnsupported;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvAdoptChild(
+ const LayersId& child) {
+ RefPtr<APZUpdater> oldApzUpdater;
+ APZCTreeManagerParent* parent;
+ bool scheduleComposition = false;
+ bool apzEnablementChanged = false;
+ RefPtr<ContentCompositorBridgeParent> cpcp;
+ RefPtr<WebRenderBridgeParent> childWrBridge;
+
+ // Before adopting the child, save the old compositor's root content
+ // controller. We may need this to clear old layer transforms associated
+ // with the child.
+ // This is outside the lock because GetGeckoContentControllerForRoot()
+ // does its own locking.
+ RefPtr<GeckoContentController> oldRootController =
+ GetGeckoContentControllerForRoot(child);
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ // If child is already belong to this CompositorBridgeParent,
+ // no need to handle adopting child.
+ if (sIndirectLayerTrees[child].mParent == this) {
+ return IPC_OK();
+ }
+
+ if (sIndirectLayerTrees[child].mParent) {
+ switch (ClassifyCompositorOptionsChange(
+ sIndirectLayerTrees[child].mParent->mOptions, mOptions)) {
+ case CompositorOptionsChangeKind::eUnsupported: {
+ MOZ_ASSERT(false,
+ "Moving tab between windows whose compositor options"
+ "differ in unsupported ways. Things may break in "
+ "unexpected ways");
+ break;
+ }
+ case CompositorOptionsChangeKind::eBestEffort: {
+ NS_WARNING(
+ "Moving tab between windows with different APZ enablement. "
+ "This is supported on a best-effort basis, but some things may "
+ "break.");
+ apzEnablementChanged = true;
+ break;
+ }
+ case CompositorOptionsChangeKind::eSupported: {
+ // The common case, no action required.
+ break;
+ }
+ }
+ oldApzUpdater = sIndirectLayerTrees[child].mParent->mApzUpdater;
+ }
+ NotifyChildCreated(child);
+ if (sIndirectLayerTrees[child].mLayerTree) {
+ sIndirectLayerTrees[child].mLayerTree->SetLayerManager(
+ mLayerManager, GetAnimationStorage());
+ // Trigger composition to handle a case that mLayerTree was not composited
+ // yet by previous CompositorBridgeParent, since nsRefreshDriver might
+ // wait composition complete.
+ scheduleComposition = true;
+ }
+ if (mWrBridge) {
+ childWrBridge = sIndirectLayerTrees[child].mWrBridge;
+ cpcp = sIndirectLayerTrees[child].mContentCompositorBridgeParent;
+ }
+ parent = sIndirectLayerTrees[child].mApzcTreeManagerParent;
+ }
+
+ if (scheduleComposition) {
+ ScheduleComposition();
+ }
+
+ if (childWrBridge) {
+ MOZ_ASSERT(mWrBridge);
+ RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+ api = api->Clone();
+ wr::Epoch newEpoch = childWrBridge->UpdateWebRender(
+ mWrBridge->CompositorScheduler(), std::move(api),
+ mWrBridge->AsyncImageManager(),
+ mWrBridge->GetTextureFactoryIdentifier());
+ // Pretend we composited, since parent CompositorBridgeParent was replaced.
+ TimeStamp now = TimeStamp::Now();
+ NotifyPipelineRendered(childWrBridge->PipelineId(), newEpoch, VsyncId(),
+ now, now, now);
+ }
+
+ if (oldApzUpdater) {
+ // If we are moving a child from an APZ-enabled window to an APZ-disabled
+ // window (which can happen if e.g. a WebExtension moves a tab into a
+ // popup window), try to handle it gracefully by clearing the old layer
+ // transforms associated with the child. (Since the new compositor is
+ // APZ-disabled, there will be nothing to update the transforms going
+ // forward.)
+ if (!mApzUpdater && oldRootController) {
+ // Tell the old APZCTreeManager not to send any more layer transforms
+ // for this layers ids.
+ oldApzUpdater->MarkAsDetached(child);
+
+ // Clear the current transforms.
+ nsTArray<MatrixMessage> clear;
+ clear.AppendElement(MatrixMessage(Nothing(), ScreenRect(), child));
+ oldRootController->NotifyLayerTransforms(std::move(clear));
+ }
+ }
+ if (mApzUpdater) {
+ if (parent) {
+ MOZ_ASSERT(mApzcTreeManager);
+ parent->ChildAdopted(mApzcTreeManager, mApzUpdater);
+ }
+ mApzUpdater->NotifyLayerTreeAdopted(child, oldApzUpdater);
+ }
+ if (apzEnablementChanged) {
+ Unused << SendCompositorOptionsChanged(child, mOptions);
+ }
+ return IPC_OK();
+}
+
+PWebRenderBridgeParent* CompositorBridgeParent::AllocPWebRenderBridgeParent(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize,
+ const WindowKind& aWindowKind) {
+ MOZ_ASSERT(wr::AsLayersId(aPipelineId) == mRootLayerTreeID);
+ MOZ_ASSERT(!mWrBridge);
+ MOZ_ASSERT(!mCompositor);
+ MOZ_ASSERT(!mCompositorScheduler);
+ MOZ_ASSERT(mWidget);
+
+#ifdef XP_WIN
+ if (mWidget && (DeviceManagerDx::Get()->CanUseDComp() ||
+ gfxVars::UseWebRenderFlipSequentialWin())) {
+ mWidget->AsWindows()->EnsureCompositorWindow();
+ }
+#endif
+
+ RefPtr<widget::CompositorWidget> widget = mWidget;
+ wr::WrWindowId windowId = wr::NewWindowId();
+ if (mApzUpdater) {
+ // If APZ is enabled, we need to register the APZ updater with the window id
+ // before the updater thread is created in WebRenderAPI::Create, so
+ // that the callback from the updater thread can find the right APZUpdater.
+ mApzUpdater->SetWebRenderWindowId(windowId);
+ }
+ if (mApzSampler) {
+ // Same as for mApzUpdater, but for the sampler thread.
+ mApzSampler->SetWebRenderWindowId(windowId);
+ }
+ if (mOMTASampler) {
+ // Same, but for the OMTA sampler.
+ mOMTASampler->SetWebRenderWindowId(windowId);
+ }
+
+ nsCString error("FEATURE_FAILURE_WEBRENDER_INITIALIZE_UNSPECIFIED");
+ RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(
+ this, std::move(widget), windowId, aSize, aWindowKind, error);
+ if (!api) {
+ mWrBridge =
+ WebRenderBridgeParent::CreateDestroyed(aPipelineId, std::move(error));
+ mWrBridge.get()->AddRef(); // IPDL reference
+ return mWrBridge;
+ }
+
+ wr::TransactionBuilder txn;
+ txn.SetRootPipeline(aPipelineId);
+ api->SendTransaction(txn);
+
+ bool useCompositorWnd = false;
+#ifdef XP_WIN
+ // Headless mode uses HeadlessWidget.
+ if (mWidget->AsWindows()) {
+ useCompositorWnd = !!mWidget->AsWindows()->GetCompositorHwnd();
+ }
+#endif
+ mAsyncImageManager =
+ new AsyncImagePipelineManager(api->Clone(), useCompositorWnd);
+ RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager;
+ mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr,
+ std::move(api), std::move(asyncMgr),
+ mVsyncRate);
+ mWrBridge.get()->AddRef(); // IPDL reference
+
+ mCompositorScheduler = mWrBridge->CompositorScheduler();
+ MOZ_ASSERT(mCompositorScheduler);
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ MOZ_ASSERT(sIndirectLayerTrees[mRootLayerTreeID].mWrBridge == nullptr);
+ sIndirectLayerTrees[mRootLayerTreeID].mWrBridge = mWrBridge;
+ }
+ return mWrBridge;
+}
+
+bool CompositorBridgeParent::DeallocPWebRenderBridgeParent(
+ PWebRenderBridgeParent* aActor) {
+ WebRenderBridgeParent* parent = static_cast<WebRenderBridgeParent*>(aActor);
+ {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ auto it = sIndirectLayerTrees.find(wr::AsLayersId(parent->PipelineId()));
+ if (it != sIndirectLayerTrees.end()) {
+ it->second.mWrBridge = nullptr;
+ }
+ }
+ parent->Release(); // IPDL reference
+ return true;
+}
+
+webgpu::PWebGPUParent* CompositorBridgeParent::AllocPWebGPUParent() {
+ MOZ_ASSERT(!mWebGPUBridge);
+ mWebGPUBridge = new webgpu::WebGPUParent();
+ mWebGPUBridge.get()->AddRef(); // IPDL reference
+ return mWebGPUBridge;
+}
+
+bool CompositorBridgeParent::DeallocPWebGPUParent(
+ webgpu::PWebGPUParent* aActor) {
+ webgpu::WebGPUParent* parent = static_cast<webgpu::WebGPUParent*>(aActor);
+ MOZ_ASSERT(mWebGPUBridge == parent);
+ parent->Release(); // IPDL reference
+ mWebGPUBridge = nullptr;
+ return true;
+}
+
+void CompositorBridgeParent::NotifyMemoryPressure() {
+ if (mWrBridge) {
+ RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+ if (api) {
+ api->NotifyMemoryPressure();
+ }
+ }
+}
+
+void CompositorBridgeParent::AccumulateMemoryReport(wr::MemoryReport* aReport) {
+ if (mWrBridge) {
+ RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+ if (api) {
+ api->AccumulateMemoryReport(aReport);
+ }
+ }
+}
+
+/*static*/
+void CompositorBridgeParent::InitializeStatics() {
+ gfxVars::SetForceSubpixelAAWherePossibleListener(&UpdateQualitySettings);
+ gfxVars::SetWebRenderDebugFlagsListener(&UpdateDebugFlags);
+ gfxVars::SetUseWebRenderMultithreadingListener(
+ &UpdateWebRenderMultithreading);
+ gfxVars::SetWebRenderBatchingLookbackListener(
+ &UpdateWebRenderBatchingParameters);
+ gfxVars::SetWebRenderProfilerUIListener(&UpdateWebRenderProfilerUI);
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateQualitySettings() {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ if (CompositorThread()) {
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("CompositorBridgeParent::UpdateQualitySettings",
+ &CompositorBridgeParent::UpdateQualitySettings));
+ }
+
+ // If there is no compositor thread, e.g. due to shutdown, then we can
+ // safefully just ignore this request.
+ return;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ wrBridge->UpdateQualitySettings();
+ });
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateDebugFlags() {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ if (CompositorThread()) {
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("CompositorBridgeParent::UpdateDebugFlags",
+ &CompositorBridgeParent::UpdateDebugFlags));
+ }
+
+ // If there is no compositor thread, e.g. due to shutdown, then we can
+ // safefully just ignore this request.
+ return;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ wrBridge->UpdateDebugFlags();
+ });
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateWebRenderMultithreading() {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ if (CompositorThread()) {
+ CompositorThread()->Dispatch(NewRunnableFunction(
+ "CompositorBridgeParent::UpdateWebRenderMultithreading",
+ &CompositorBridgeParent::UpdateWebRenderMultithreading));
+ }
+
+ return;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ wrBridge->UpdateMultithreading();
+ });
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateWebRenderBatchingParameters() {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ if (CompositorThread()) {
+ CompositorThread()->Dispatch(NewRunnableFunction(
+ "CompositorBridgeParent::UpdateWebRenderBatchingParameters",
+ &CompositorBridgeParent::UpdateWebRenderBatchingParameters));
+ }
+
+ return;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ wrBridge->UpdateBatchingParameters();
+ });
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateWebRenderProfilerUI() {
+ if (!sIndirectLayerTreesLock) {
+ return;
+ }
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ wrBridge->UpdateProfilerUI();
+ });
+}
+
+RefPtr<WebRenderBridgeParent> CompositorBridgeParent::GetWebRenderBridgeParent()
+ const {
+ return mWrBridge;
+}
+
+Maybe<TimeStamp> CompositorBridgeParent::GetTestingTimeStamp() const {
+ return mTestTime;
+}
+
+void EraseLayerState(LayersId aId) {
+ RefPtr<APZUpdater> apz;
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ auto iter = sIndirectLayerTrees.find(aId);
+ if (iter != sIndirectLayerTrees.end()) {
+ CompositorBridgeParent* parent = iter->second.mParent;
+ if (parent) {
+ apz = parent->GetAPZUpdater();
+ }
+ sIndirectLayerTrees.erase(iter);
+ }
+ }
+
+ if (apz) {
+ apz->NotifyLayerTreeRemoved(aId);
+ }
+}
+
+/*static*/
+void CompositorBridgeParent::DeallocateLayerTreeId(LayersId aId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ // Here main thread notifies compositor to remove an element from
+ // sIndirectLayerTrees. This removed element might be queried soon.
+ // Checking the elements of sIndirectLayerTrees exist or not before using.
+ if (!CompositorThread()) {
+ gfxCriticalError() << "Attempting to post to an invalid Compositor Thread";
+ return;
+ }
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("EraseLayerStateRunnable", &EraseLayerState, aId));
+}
+
+static void UpdateControllerForLayersId(LayersId aLayersId,
+ GeckoContentController* aController) {
+ // Adopt ref given to us by SetControllerForLayerTree()
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[aLayersId].mController =
+ already_AddRefed<GeckoContentController>(aController);
+}
+
+ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(
+ APZCTreeManager* aApzctm, LayersId aLayersId, Layer* aRoot,
+ GeckoContentController* aController)
+ : mLayersId(aLayersId) {
+ EnsureLayerTreeMapReady();
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[aLayersId].mRoot = aRoot;
+ sIndirectLayerTrees[aLayersId].mController = aController;
+}
+
+ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration() {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees.erase(mLayersId);
+}
+
+/*static*/
+void CompositorBridgeParent::SetControllerForLayerTree(
+ LayersId aLayersId, GeckoContentController* aController) {
+ // This ref is adopted by UpdateControllerForLayersId().
+ aController->AddRef();
+ CompositorThread()->Dispatch(NewRunnableFunction(
+ "UpdateControllerForLayersIdRunnable", &UpdateControllerForLayersId,
+ aLayersId, aController));
+}
+
+/*static*/
+already_AddRefed<IAPZCTreeManager> CompositorBridgeParent::GetAPZCTreeManager(
+ LayersId aLayersId) {
+ EnsureLayerTreeMapReady();
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aLayersId);
+ if (sIndirectLayerTrees.end() == cit) {
+ return nullptr;
+ }
+ LayerTreeState* lts = &cit->second;
+
+ RefPtr<IAPZCTreeManager> apzctm =
+ lts->mParent ? lts->mParent->mApzcTreeManager.get() : nullptr;
+ return apzctm.forget();
+}
+
+#if defined(MOZ_GECKO_PROFILER)
+static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (profiler_thread_is_being_profiled()) {
+ // Tracks when a vsync occurs according to the HardwareComposer.
+ struct VsyncMarker {
+ static constexpr mozilla::Span<const char> MarkerTypeName() {
+ return mozilla::MakeStringSpan("VsyncTimestamp");
+ }
+ static void StreamJSONMarkerData(
+ baseprofiler::SpliceableJSONWriter& aWriter) {}
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::markerChart, MS::Location::markerTable};
+ // Nothing outside the defaults.
+ return schema;
+ }
+ };
+ profiler_add_marker("VsyncTimestamp", geckoprofiler::category::GRAPHICS,
+ MarkerTiming::InstantAt(aVsyncTimestamp),
+ VsyncMarker{});
+ }
+}
+#endif
+
+/*static */
+void CompositorBridgeParent::PostInsertVsyncProfilerMarker(
+ TimeStamp aVsyncTimestamp) {
+#if defined(MOZ_GECKO_PROFILER)
+ // Called in the vsync thread
+ if (profiler_is_active() && CompositorThreadHolder::IsActive()) {
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("InsertVsyncProfilerMarkerRunnable",
+ InsertVsyncProfilerMarker, aVsyncTimestamp));
+ }
+#endif
+}
+
+widget::PCompositorWidgetParent*
+CompositorBridgeParent::AllocPCompositorWidgetParent(
+ const CompositorWidgetInitData& aInitData) {
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+ if (mWidget) {
+ // Should not create two widgets on the same compositor.
+ return nullptr;
+ }
+
+ widget::CompositorWidgetParent* widget =
+ new widget::CompositorWidgetParent(aInitData, mOptions);
+ widget->AddRef();
+
+ // Sending the constructor acts as initialization as well.
+ mWidget = widget;
+ return widget;
+#else
+ return nullptr;
+#endif
+}
+
+bool CompositorBridgeParent::DeallocPCompositorWidgetParent(
+ PCompositorWidgetParent* aActor) {
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+ static_cast<widget::CompositorWidgetParent*>(aActor)->Release();
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool CompositorBridgeParent::IsPendingComposite() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mCompositor) {
+ return false;
+ }
+ return mCompositor->IsPendingComposite();
+}
+
+void CompositorBridgeParent::FinishPendingComposite() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mCompositor) {
+ return;
+ }
+ return mCompositor->FinishPendingComposite();
+}
+
+CompositorController*
+CompositorBridgeParent::LayerTreeState::GetCompositorController() const {
+ return mParent;
+}
+
+MetricsSharingController*
+CompositorBridgeParent::LayerTreeState::CrossProcessSharingController() const {
+ return mContentCompositorBridgeParent;
+}
+
+MetricsSharingController*
+CompositorBridgeParent::LayerTreeState::InProcessSharingController() const {
+ return mParent;
+}
+
+void CompositorBridgeParent::DidComposite(const VsyncId& aId,
+ TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd) {
+ if (mWrBridge) {
+ MOZ_ASSERT(false); // This should never get called for a WR compositor
+ } else {
+ NotifyDidComposite(mPendingTransaction, aId, aCompositeStart,
+ aCompositeEnd);
+#if defined(ENABLE_FRAME_LATENCY_LOG)
+ if (mPendingTransaction.IsValid()) {
+ if (mRefreshStartTime) {
+ int32_t latencyMs =
+ lround((aCompositeEnd - mRefreshStartTime).ToMilliseconds());
+ printf_stderr(
+ "From transaction start to end of generate frame latencyMs %d this "
+ "%p\n",
+ latencyMs, this);
+ }
+ if (mFwdTime) {
+ int32_t latencyMs = lround((aCompositeEnd - mFwdTime).ToMilliseconds());
+ printf_stderr(
+ "From forwarding transaction to end of generate frame latencyMs %d "
+ "this %p\n",
+ latencyMs, this);
+ }
+ }
+ mRefreshStartTime = TimeStamp();
+ mTxnStartTime = TimeStamp();
+ mFwdTime = TimeStamp();
+#endif
+ mPendingTransaction = TransactionId{0};
+ }
+}
+
+void CompositorBridgeParent::NotifyDidSceneBuild(
+ RefPtr<const wr::WebRenderPipelineInfo> aInfo) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mPaused) {
+ return;
+ }
+
+ if (mWrBridge) {
+ mWrBridge->NotifyDidSceneBuild(aInfo);
+ } else {
+ mCompositorScheduler->ScheduleComposition();
+ }
+}
+
+void CompositorBridgeParent::NotifyDidRender(const VsyncId& aCompositeStartId,
+ TimeStamp& aCompositeStart,
+ TimeStamp& aRenderStart,
+ TimeStamp& aCompositeEnd,
+ wr::RendererStats* aStats) {
+ if (!mWrBridge) {
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(mWrBridge->IsRootWebRenderBridgeParent());
+
+ RefPtr<UiCompositorControllerParent> uiController =
+ UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeID);
+
+ if (uiController && mIsForcedFirstPaint) {
+ uiController->NotifyFirstPaint();
+ mIsForcedFirstPaint = false;
+ }
+
+ nsTArray<CompositionPayload> payload =
+ mWrBridge->TakePendingScrollPayload(aCompositeStartId);
+ if (!payload.IsEmpty()) {
+ RecordCompositionPayloadsPresented(aCompositeEnd, payload);
+ }
+
+ nsTArray<ImageCompositeNotificationInfo> notifications;
+ mWrBridge->ExtractImageCompositeNotifications(&notifications);
+ if (!notifications.IsEmpty()) {
+ Unused << ImageBridgeParent::NotifyImageComposites(notifications);
+ }
+}
+
+void CompositorBridgeParent::NotifyPipelineRendered(
+ const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
+ const VsyncId& aCompositeStartId, TimeStamp& aCompositeStart,
+ TimeStamp& aRenderStart, TimeStamp& aCompositeEnd,
+ wr::RendererStats* aStats) {
+ if (!mWrBridge || !mAsyncImageManager) {
+ return;
+ }
+
+ bool isRoot = mWrBridge->PipelineId() == aPipelineId;
+ RefPtr<WebRenderBridgeParent> wrBridge =
+ isRoot ? mWrBridge
+ : RefPtr<WebRenderBridgeParent>(
+ mAsyncImageManager->GetWrBridge(aPipelineId));
+ if (!wrBridge) {
+ return;
+ }
+
+ CompositorBridgeParentBase* compBridge =
+ isRoot ? this : wrBridge->GetCompositorBridge();
+ if (!compBridge) {
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(isRoot == wrBridge->IsRootWebRenderBridgeParent());
+
+ wrBridge->RemoveEpochDataPriorTo(aEpoch);
+
+ nsTArray<FrameStats> stats;
+
+ RefPtr<UiCompositorControllerParent> uiController =
+ UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeID);
+
+ TransactionId transactionId = wrBridge->FlushTransactionIdsForEpoch(
+ aEpoch, aCompositeStartId, aCompositeStart, aRenderStart, aCompositeEnd,
+ uiController, aStats, &stats);
+
+ LayersId layersId = isRoot ? LayersId{0} : wrBridge->GetLayersId();
+ Unused << compBridge->SendDidComposite(layersId, transactionId,
+ aCompositeStart, aCompositeEnd);
+
+ if (!stats.IsEmpty()) {
+ Unused << SendNotifyFrameStats(stats);
+ }
+}
+
+RefPtr<AsyncImagePipelineManager>
+CompositorBridgeParent::GetAsyncImagePipelineManager() const {
+ return mAsyncImageManager;
+}
+
+void CompositorBridgeParent::NotifyDidComposite(TransactionId aTransactionId,
+ VsyncId aId,
+ TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd) {
+ MOZ_ASSERT(!mWrBridge,
+ "We should be going through NotifyDidRender and "
+ "NotifyPipelineRendered instead");
+
+ Unused << SendDidComposite(LayersId{0}, aTransactionId, aCompositeStart,
+ aCompositeEnd);
+
+ if (mLayerManager) {
+ nsTArray<ImageCompositeNotificationInfo> notifications;
+ mLayerManager->ExtractImageCompositeNotifications(&notifications);
+ if (!notifications.IsEmpty()) {
+ Unused << ImageBridgeParent::NotifyImageComposites(notifications);
+ }
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([&](LayerTreeState* lts,
+ const LayersId& aLayersId) -> void {
+ if (lts->mContentCompositorBridgeParent && lts->mParent == this) {
+ ContentCompositorBridgeParent* cpcp = lts->mContentCompositorBridgeParent;
+ cpcp->DidCompositeLocked(aLayersId, aId, aCompositeStart, aCompositeEnd);
+ }
+ });
+}
+
+void CompositorBridgeParent::InvalidateRemoteLayers() {
+ MOZ_ASSERT(CompositorThread()->IsOnCurrentThread());
+
+ Unused << PCompositorBridgeParent::SendInvalidateLayers(LayersId{0});
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([](LayerTreeState* lts,
+ const LayersId& aLayersId) -> void {
+ if (lts->mContentCompositorBridgeParent) {
+ ContentCompositorBridgeParent* cpcp = lts->mContentCompositorBridgeParent;
+ Unused << cpcp->SendInvalidateLayers(aLayersId);
+ }
+ });
+}
+
+void UpdateIndirectTree(LayersId aId, Layer* aRoot,
+ const TargetConfig& aTargetConfig) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[aId].mRoot = aRoot;
+ sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig;
+}
+
+/* static */ CompositorBridgeParent::LayerTreeState*
+CompositorBridgeParent::GetIndirectShadowTree(LayersId aId) {
+ // Only the compositor thread should use this method variant
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() == cit) {
+ return nullptr;
+ }
+ return &cit->second;
+}
+
+/* static */
+bool CompositorBridgeParent::CallWithIndirectShadowTree(
+ LayersId aId,
+ const std::function<void(CompositorBridgeParent::LayerTreeState&)>& aFunc) {
+ if (!sIndirectLayerTreesLock) {
+ // Can hapen during shutdown
+ return false;
+ }
+ // Note that this does not make things universally threadsafe just because the
+ // sIndirectLayerTreesLock mutex is held. This is because the compositor
+ // thread can mutate the LayerTreeState outside the lock. It does however
+ // ensure that the *storage* for the LayerTreeState remains stable, since we
+ // should always hold the lock when adding/removing entries to the map.
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() == cit) {
+ return false;
+ }
+ aFunc(cit->second);
+ return true;
+}
+
+static CompositorBridgeParent::LayerTreeState* GetStateForRoot(
+ LayersId aContentLayersId, const MonitorAutoLock& aProofOfLock) {
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+ LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aContentLayersId);
+ if (sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ // |state| is the state for the content process, but we want the APZCTMParent
+ // for the parent process owning that content process. So we have to jump to
+ // the LayerTreeState for the root layer tree id for that layer tree, and use
+ // the mApzcTreeManagerParent from that. This should also work with nested
+ // content processes, because RootLayerTreeId() will bypass any intermediate
+ // processes' ids and go straight to the root.
+ if (state && state->mParent) {
+ LayersId rootLayersId = state->mParent->RootLayerTreeId();
+ itr = sIndirectLayerTrees.find(rootLayersId);
+ state = (sIndirectLayerTrees.end() != itr) ? &itr->second : nullptr;
+ }
+
+ return state;
+}
+
+/* static */
+APZCTreeManagerParent* CompositorBridgeParent::GetApzcTreeManagerParentForRoot(
+ LayersId aContentLayersId) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState* state =
+ GetStateForRoot(aContentLayersId, lock);
+ return state ? state->mApzcTreeManagerParent : nullptr;
+}
+
+/* static */
+GeckoContentController*
+CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ LayersId aContentLayersId) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState* state =
+ GetStateForRoot(aContentLayersId, lock);
+ return state ? state->mController.get() : nullptr;
+}
+
+PTextureParent* CompositorBridgeParent::AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ return TextureHost::CreateIPDLActor(this, aSharedData, aReadLock,
+ aLayersBackend, aFlags, aSerial,
+ aExternalImageId);
+}
+
+bool CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) {
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvInitPCanvasParent(
+ Endpoint<PCanvasParent>&& aEndpoint) {
+ MOZ_CRASH("PCanvasParent shouldn't be created via CompositorBridgeParent.");
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvReleasePCanvasParent() {
+ MOZ_CRASH("PCanvasParent shouldn't be released via CompositorBridgeParent.");
+}
+
+bool CompositorBridgeParent::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void CompositorBridgeParent::NotifyWebRenderDisableNativeCompositor() {
+ MOZ_ASSERT(CompositorThread()->IsOnCurrentThread());
+ if (mWrBridge) {
+ mWrBridge->DisableNativeCompositor();
+ }
+}
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+//#define PLUGINS_LOG(...) printf_stderr("CP [%s]: ", __FUNCTION__);
+// printf_stderr(__VA_ARGS__);
+// printf_stderr("\n");
+# define PLUGINS_LOG(...)
+
+bool CompositorBridgeParent::UpdatePluginWindowState(LayersId aId) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& lts = sIndirectLayerTrees[aId];
+ if (!lts.mParent) {
+ PLUGINS_LOG("[%" PRIu64 "] layer tree compositor parent pointer is null",
+ aId);
+ return false;
+ }
+
+ // Check if this layer tree has received any shadow layer updates
+ if (!lts.mUpdatedPluginDataAvailable) {
+ PLUGINS_LOG("[%" PRIu64 "] no plugin data", aId);
+ return false;
+ }
+
+ // pluginMetricsChanged tracks whether we need to send plugin update
+ // data to the main thread. If we do we'll have to block composition,
+ // which we want to avoid if at all possible.
+ bool pluginMetricsChanged = false;
+
+ // Same layer tree checks
+ if (mLastPluginUpdateLayerTreeId == aId) {
+ // no plugin data and nothing has changed, bail.
+ if (!mCachedPluginData.Length() && !lts.mPluginData.Length()) {
+ PLUGINS_LOG("[%" PRIu64 "] no data, no changes", aId);
+ return false;
+ }
+
+ if (mCachedPluginData.Length() == lts.mPluginData.Length()) {
+ // check for plugin data changes
+ for (uint32_t idx = 0; idx < lts.mPluginData.Length(); idx++) {
+ if (!(mCachedPluginData[idx] == lts.mPluginData[idx])) {
+ pluginMetricsChanged = true;
+ break;
+ }
+ }
+ } else {
+ // array lengths don't match, need to update
+ pluginMetricsChanged = true;
+ }
+ } else {
+ // exchanging layer trees, we need to update
+ pluginMetricsChanged = true;
+ }
+
+ // Check if plugin windows are currently hidden due to scrolling
+ if (mDeferPluginWindows) {
+ PLUGINS_LOG("[%" PRIu64 "] suppressing", aId);
+ return false;
+ }
+
+ // If the plugin windows were hidden but now are not, we need to force
+ // update the metrics to make sure they are visible again.
+ if (mPluginWindowsHidden) {
+ PLUGINS_LOG("[%" PRIu64 "] re-showing", aId);
+ mPluginWindowsHidden = false;
+ pluginMetricsChanged = true;
+ }
+
+ if (!lts.mPluginData.Length()) {
+ // Don't hide plugins if the previous remote layer tree didn't contain any.
+ if (!mCachedPluginData.Length()) {
+ PLUGINS_LOG("[%" PRIu64 "] nothing to hide", aId);
+ return false;
+ }
+
+ uintptr_t parentWidget = GetWidget()->GetWidgetKey();
+
+ // We will pass through here in cases where the previous shadow layer
+ // tree contained visible plugins and the new tree does not. All we need
+ // to do here is hide the plugins for the old tree, so don't waste time
+ // calculating clipping.
+ mPluginsLayerOffset = nsIntPoint(0, 0);
+ mPluginsLayerVisibleRegion.SetEmpty();
+ Unused << lts.mParent->SendHideAllPlugins(parentWidget);
+ lts.mUpdatedPluginDataAvailable = false;
+ PLUGINS_LOG("[%" PRIu64 "] hide all", aId);
+ } else {
+ // Retrieve the offset and visible region of the layer that hosts
+ // the plugins, CompositorBridgeChild needs these in calculating proper
+ // plugin clipping.
+ LayerTransactionParent* layerTree = lts.mLayerTree;
+ Layer* contentRoot = layerTree->GetRoot();
+ if (contentRoot) {
+ nsIntPoint offset;
+ nsIntRegion visibleRegion;
+ if (contentRoot->GetVisibleRegionRelativeToRootLayer(visibleRegion,
+ &offset)) {
+ // Check to see if these values have changed, if so we need to
+ // update plugin window position within the window.
+ if (!pluginMetricsChanged &&
+ mPluginsLayerVisibleRegion == visibleRegion &&
+ mPluginsLayerOffset == offset) {
+ PLUGINS_LOG("[%" PRIu64 "] no change", aId);
+ return false;
+ }
+ mPluginsLayerOffset = offset;
+ mPluginsLayerVisibleRegion = visibleRegion;
+ Unused << lts.mParent->SendUpdatePluginConfigurations(
+ LayoutDeviceIntPoint::FromUnknownPoint(offset),
+ LayoutDeviceIntRegion::FromUnknownRegion(visibleRegion),
+ lts.mPluginData);
+ lts.mUpdatedPluginDataAvailable = false;
+ PLUGINS_LOG("[%" PRIu64 "] updated", aId);
+ } else {
+ PLUGINS_LOG("[%" PRIu64 "] no visibility data", aId);
+ return false;
+ }
+ } else {
+ PLUGINS_LOG("[%" PRIu64 "] no content root", aId);
+ return false;
+ }
+ }
+
+ mLastPluginUpdateLayerTreeId = aId;
+ mCachedPluginData = lts.mPluginData.Clone();
+ return true;
+}
+
+void CompositorBridgeParent::ScheduleShowAllPluginWindows() {
+ MOZ_ASSERT(CompositorThread());
+ CompositorThread()->Dispatch(
+ NewRunnableMethod("layers::CompositorBridgeParent::ShowAllPluginWindows",
+ this, &CompositorBridgeParent::ShowAllPluginWindows));
+}
+
+void CompositorBridgeParent::ShowAllPluginWindows() {
+ MOZ_ASSERT(!NS_IsMainThread());
+ mDeferPluginWindows = false;
+ ScheduleComposition();
+}
+
+void CompositorBridgeParent::ScheduleHideAllPluginWindows() {
+ MOZ_ASSERT(CompositorThread());
+ CompositorThread()->Dispatch(
+ NewRunnableMethod("layers::CompositorBridgeParent::HideAllPluginWindows",
+ this, &CompositorBridgeParent::HideAllPluginWindows));
+}
+
+void CompositorBridgeParent::HideAllPluginWindows() {
+ MOZ_ASSERT(!NS_IsMainThread());
+ // No plugins in the cache implies no plugins to manage
+ // in this content.
+ if (!mCachedPluginData.Length() || mDeferPluginWindows) {
+ return;
+ }
+
+ uintptr_t parentWidget = GetWidget()->GetWidgetKey();
+
+ mDeferPluginWindows = true;
+ mPluginWindowsHidden = true;
+
+# if defined(XP_WIN)
+ // We will get an async reply that this has happened and then send hide.
+ mWaitForPluginsUntil = TimeStamp::Now() + mVsyncRate;
+ Unused << SendCaptureAllPlugins(parentWidget);
+# else
+ Unused << SendHideAllPlugins(parentWidget);
+ ScheduleComposition();
+# endif
+}
+#endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvAllPluginsCaptured() {
+#if defined(XP_WIN)
+ mWaitForPluginsUntil = TimeStamp();
+ mHaveBlockedForPlugins = false;
+ ForceComposeToTarget(nullptr);
+ Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey());
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "CompositorBridgeParent::RecvAllPluginsCaptured calls unexpected.");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+int32_t RecordContentFrameTime(
+ const VsyncId& aTxnId, const TimeStamp& aVsyncStart,
+ const TimeStamp& aTxnStart, const VsyncId& aCompositeId,
+ const TimeStamp& aCompositeEnd, const TimeDuration& aFullPaintTime,
+ const TimeDuration& aVsyncRate, bool aContainsSVGGroup,
+ bool aRecordUploadStats, wr::RendererStats* aStats /* = nullptr */) {
+ double latencyMs = (aCompositeEnd - aTxnStart).ToMilliseconds();
+ double latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
+ int32_t fracLatencyNorm = lround(latencyNorm * 100.0);
+
+#ifdef MOZ_GECKO_PROFILER
+ if (profiler_can_accept_markers()) {
+ struct ContentFrameMarker {
+ static constexpr Span<const char> MarkerTypeName() {
+ return MakeStringSpan("CONTENT_FRAME_TIME");
+ }
+ static void StreamJSONMarkerData(
+ baseprofiler::SpliceableJSONWriter& aWriter) {}
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::markerChart, MS::Location::markerTable};
+ // Nothing outside the defaults.
+ return schema;
+ }
+ };
+
+ profiler_add_marker("CONTENT_FRAME_TIME", geckoprofiler::category::GRAPHICS,
+ MarkerTiming::Interval(aTxnStart, aCompositeEnd),
+ ContentFrameMarker{});
+ }
+#endif
+
+ Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm);
+
+ if (!(aTxnId == VsyncId()) && aVsyncStart) {
+ latencyMs = (aCompositeEnd - aVsyncStart).ToMilliseconds();
+ latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
+ fracLatencyNorm = lround(latencyNorm * 100.0);
+ int32_t result = fracLatencyNorm;
+ Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME_VSYNC, fracLatencyNorm);
+
+ if (aContainsSVGGroup) {
+ Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME_WITH_SVG,
+ fracLatencyNorm);
+ }
+
+ // Record CONTENT_FRAME_TIME_REASON.
+ //
+ // Note that deseralizing a layers update (RecvUpdate) can delay the receipt
+ // of the composite vsync message
+ // (CompositorBridgeParent::CompositeToTarget), since they're using the same
+ // thread. This can mean that compositing might start significantly late,
+ // but this code will still detect it as having successfully started on the
+ // right vsync (which is somewhat correct). We'd now have reduced time left
+ // in the vsync interval to finish compositing, so the chances of a missed
+ // frame increases. This is effectively including the RecvUpdate work as
+ // part of the 'compositing' phase for this metric, but it isn't included in
+ // COMPOSITE_TIME, and *is* included in CONTENT_FULL_PAINT_TIME.
+ //
+ // Also of note is that when the root WebRenderBridgeParent decides to
+ // skip a composite (due to the Renderer being busy), that won't notify
+ // child WebRenderBridgeParents. That failure will show up as the
+ // composite starting late (since it did), but it's really a fault of a
+ // slow composite on the previous frame, not a slow
+ // CONTENT_FULL_PAINT_TIME. It would be nice to have a separate bucket for
+ // this category (scene was ready on the next vsync, but we chose not to
+ // composite), but I can't find a way to locate the right child
+ // WebRenderBridgeParents from the root. WebRender notifies us of the
+ // child pipelines contained within a render, after it finishes, but I
+ // can't see how to query what child pipeline would have been rendered,
+ // when we choose to not do it.
+ if (fracLatencyNorm < 200) {
+ // Success
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::OnTime);
+ } else {
+ if (aCompositeId == VsyncId()) {
+ // aCompositeId is 0, possibly something got trigged from
+ // outside vsync?
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::NoVsyncNoId);
+ } else if (aTxnId >= aCompositeId) {
+ // Vsync ids are nonsensical, maybe we're trying to catch up?
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::NoVsync);
+ } else if (aCompositeId - aTxnId > 1) {
+ // Composite started late (and maybe took too long as well)
+ if (aFullPaintTime >= TimeDuration::FromMilliseconds(20)) {
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::MissedCompositeLong);
+ } else if (aFullPaintTime >= TimeDuration::FromMilliseconds(10)) {
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::MissedCompositeMid);
+ } else if (aFullPaintTime >= TimeDuration::FromMilliseconds(5)) {
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::MissedCompositeLow);
+ } else {
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::MissedComposite);
+ }
+ } else {
+ // Composite started on time, but must have taken too long.
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::SlowComposite);
+ }
+ }
+
+ if (aRecordUploadStats) {
+ if (aStats) {
+ latencyMs -= (double(aStats->resource_upload_time) / 1000000.0);
+ latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
+ fracLatencyNorm = lround(latencyNorm * 100.0);
+ }
+ Telemetry::Accumulate(
+ Telemetry::CONTENT_FRAME_TIME_WITHOUT_RESOURCE_UPLOAD,
+ fracLatencyNorm);
+
+ if (aStats) {
+ latencyMs -= (double(aStats->gpu_cache_upload_time) / 1000000.0);
+ latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
+ fracLatencyNorm = lround(latencyNorm * 100.0);
+ }
+ Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME_WITHOUT_UPLOAD,
+ fracLatencyNorm);
+ }
+ return result;
+ }
+
+ return 0;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvBeginRecording(
+ const TimeStamp& aRecordingStart, BeginRecordingResolver&& aResolve) {
+ if (mHaveCompositionRecorder) {
+ aResolve(false);
+ return IPC_OK();
+ }
+
+ if (mLayerManager) {
+ mLayerManager->SetCompositionRecorder(
+ MakeUnique<CompositionRecorder>(aRecordingStart));
+ } else if (mWrBridge) {
+ mWrBridge->BeginRecording(aRecordingStart);
+ }
+
+ mHaveCompositionRecorder = true;
+ aResolve(true);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvEndRecordingToDisk(
+ EndRecordingToDiskResolver&& aResolve) {
+ if (!mHaveCompositionRecorder) {
+ aResolve(false);
+ return IPC_OK();
+ }
+
+ if (mLayerManager) {
+ mLayerManager->WriteCollectedFrames();
+ aResolve(true);
+ } else if (mWrBridge) {
+ mWrBridge->WriteCollectedFrames()->Then(
+ NS_GetCurrentThread(), __func__,
+ [resolve{aResolve}](const bool success) { resolve(success); },
+ [resolve{aResolve}]() { resolve(false); });
+ } else {
+ aResolve(false);
+ }
+
+ mHaveCompositionRecorder = false;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvEndRecordingToMemory(
+ EndRecordingToMemoryResolver&& aResolve) {
+ if (!mHaveCompositionRecorder) {
+ aResolve(Nothing());
+ return IPC_OK();
+ }
+
+ if (mLayerManager) {
+ Maybe<CollectedFrames> frames = mLayerManager->GetCollectedFrames();
+ if (frames) {
+ aResolve(WrapCollectedFrames(std::move(*frames)));
+ } else {
+ aResolve(Nothing());
+ }
+ } else if (mWrBridge) {
+ RefPtr<CompositorBridgeParent> self = this;
+ mWrBridge->GetCollectedFrames()->Then(
+ NS_GetCurrentThread(), __func__,
+ [self, resolve{aResolve}](CollectedFrames&& frames) {
+ resolve(self->WrapCollectedFrames(std::move(frames)));
+ },
+ [resolve{aResolve}]() { resolve(Nothing()); });
+ }
+
+ mHaveCompositionRecorder = false;
+
+ return IPC_OK();
+}
+
+Maybe<CollectedFramesParams> CompositorBridgeParent::WrapCollectedFrames(
+ CollectedFrames&& aFrames) {
+ CollectedFramesParams ipcFrames;
+ ipcFrames.recordingStart() = aFrames.mRecordingStart;
+
+ size_t totalLength = 0;
+ for (const CollectedFrame& frame : aFrames.mFrames) {
+ totalLength += frame.mDataUri.Length();
+ }
+
+ Shmem shmem;
+ if (!AllocShmem(totalLength, SharedMemory::TYPE_BASIC, &shmem)) {
+ return Nothing();
+ }
+
+ {
+ char* raw = shmem.get<char>();
+ for (CollectedFrame& frame : aFrames.mFrames) {
+ size_t length = frame.mDataUri.Length();
+
+ PodCopy(raw, frame.mDataUri.get(), length);
+ raw += length;
+
+ ipcFrames.frames().EmplaceBack(frame.mTimeOffset, length);
+ }
+ }
+ ipcFrames.buffer() = std::move(shmem);
+
+ return Some(std::move(ipcFrames));
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h
new file mode 100644
index 0000000000..9d518749a2
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -0,0 +1,904 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBridgeParent_h
+#define mozilla_layers_CompositorBridgeParent_h
+
+// Enable this pref to turn on compositor performance warning.
+// This will print warnings if the compositor isn't meeting
+// its responsiveness objectives:
+// 1) Compose a frame within 15ms of receiving a ScheduleCompositeCall
+// 2) Unless a frame was composited within the throttle threshold in
+// which the deadline will be 15ms + throttle threshold
+//#define COMPOSITOR_PERFORMANCE_WARNING
+
+#include <stdint.h> // for uint64_t
+#include <unordered_map>
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Maybe.h"
+#include "mozilla/Monitor.h" // for Monitor
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/layers/CompositorController.h"
+#include "mozilla/layers/CompositorVsyncSchedulerOwner.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for IShmemAllocator
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/MetricsSharingController.h"
+#include "mozilla/layers/PCompositorBridgeParent.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+struct DxgiAdapterDesc;
+
+namespace mozilla {
+
+class CancelableRunnable;
+
+namespace dom {
+class WebGLParent;
+} // namespace dom
+
+namespace gfx {
+class DrawTarget;
+class GPUProcessManager;
+class GPUParent;
+} // namespace gfx
+
+namespace ipc {
+class Shmem;
+#ifdef FUZZING
+class ProtocolFuzzerHelper;
+#endif
+} // namespace ipc
+
+namespace webgpu {
+class PWebGPUParent;
+class WebGPUParent;
+} // namespace webgpu
+
+namespace widget {
+class CompositorWidget;
+}
+
+namespace wr {
+class WebRenderPipelineInfo;
+struct Epoch;
+struct MemoryReport;
+struct PipelineId;
+struct RendererStats;
+} // namespace wr
+
+namespace layers {
+
+class APZCTreeManager;
+class APZCTreeManagerParent;
+class APZSampler;
+class APZTestData;
+class APZUpdater;
+class AsyncCompositionManager;
+class AsyncImagePipelineManager;
+class Compositor;
+class CompositorAnimationStorage;
+class CompositorBridgeParent;
+class CompositorManagerParent;
+class CompositorVsyncScheduler;
+class FrameUniformityData;
+class GeckoContentController;
+class HostLayerManager;
+class IAPZCTreeManager;
+class Layer;
+class LayerTransactionParent;
+class OMTASampler;
+class ContentCompositorBridgeParent;
+class CompositorThreadHolder;
+class InProcessCompositorSession;
+class UiCompositorControllerParent;
+class WebRenderBridgeParent;
+struct CollectedFrames;
+
+struct ScopedLayerTreeRegistration {
+ ScopedLayerTreeRegistration(APZCTreeManager* aApzctm, LayersId aLayersId,
+ Layer* aRoot,
+ GeckoContentController* aController);
+ ~ScopedLayerTreeRegistration();
+
+ private:
+ LayersId mLayersId;
+};
+
+class CompositorBridgeParentBase : public PCompositorBridgeParent,
+ public HostIPCAllocator,
+ public mozilla::ipc::IShmemAllocator,
+ public MetricsSharingController {
+ friend class PCompositorBridgeParent;
+
+ public:
+ explicit CompositorBridgeParentBase(CompositorManagerParent* aManager);
+
+ virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const TransactionInfo& aInfo,
+ bool aHitTestUpdate) = 0;
+
+ virtual AsyncCompositionManager* GetCompositionManager(
+ LayerTransactionParent* aLayerTree) {
+ return nullptr;
+ }
+
+ virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) {}
+
+ virtual void ScheduleComposite(LayerTransactionParent* aLayerTree) {}
+ virtual bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) {
+ return true;
+ }
+ virtual void LeaveTestMode(const LayersId& aId) {}
+ enum class TransformsToSkip : uint8_t { NoneOfThem = 0, APZ = 1 };
+ virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree,
+ TransformsToSkip aSkip) = 0;
+ virtual void SetTestAsyncScrollOffset(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) = 0;
+ virtual void SetTestAsyncZoom(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) = 0;
+ virtual void FlushApzRepaints(const LayersId& aLayersId) = 0;
+ virtual void GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) {}
+ virtual void GetFrameUniformity(const LayersId& aLayersId,
+ FrameUniformityData* data) = 0;
+ virtual void SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) = 0;
+ virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree,
+ const TimeDuration& aPaintTime) {}
+ virtual void RegisterPayloads(LayerTransactionParent* aLayerTree,
+ const nsTArray<CompositionPayload>& aPayload) {}
+
+ IShmemAllocator* AsShmemAllocator() override { return this; }
+
+ CompositorBridgeParentBase* AsCompositorBridgeParentBase() override {
+ return this;
+ }
+
+ mozilla::ipc::IPCResult RecvSyncWithCompositor() { return IPC_OK(); }
+
+ mozilla::ipc::IPCResult Recv__delete__() override { return IPC_OK(); }
+
+ virtual void ObserveLayersUpdate(LayersId aLayersId,
+ LayersObserverEpoch aEpoch,
+ bool aActive) = 0;
+
+ // HostIPCAllocator
+ base::ProcessId GetChildProcessId() override;
+ void NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) override;
+ void SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) override;
+
+ // IShmemAllocator
+ bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ // MetricsSharingController
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override {
+ return HostIPCAllocator::AddRef();
+ }
+ NS_IMETHOD_(MozExternalRefCountType) Release() override {
+ return HostIPCAllocator::Release();
+ }
+ base::ProcessId RemotePid() override;
+ bool StartSharingMetrics(mozilla::ipc::SharedMemoryBasic::Handle aHandle,
+ CrossProcessMutexHandle aMutexHandle,
+ LayersId aLayersId, uint32_t aApzcId) override;
+ bool StopSharingMetrics(ScrollableLayerGuid::ViewID aScrollId,
+ uint32_t aApzcId) override;
+
+ virtual bool IsRemote() const { return false; }
+
+ virtual UniquePtr<SurfaceDescriptor> LookupSurfaceDescriptorForClientTexture(
+ const int64_t aTextureId) {
+ MOZ_CRASH("Should only be called on ContentCompositorBridgeParent.");
+ }
+
+ virtual void ForceComposeToTarget(gfx::DrawTarget* aTarget,
+ const gfx::IntRect* aRect = nullptr) {
+ MOZ_CRASH();
+ }
+
+ virtual void NotifyMemoryPressure() {}
+ virtual void AccumulateMemoryReport(wr::MemoryReport*) {}
+
+ protected:
+ virtual ~CompositorBridgeParentBase();
+
+ virtual PAPZParent* AllocPAPZParent(const LayersId& layersId) = 0;
+ virtual bool DeallocPAPZParent(PAPZParent* aActor) = 0;
+
+ virtual PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(
+ const LayersId& layersId) = 0;
+ virtual bool DeallocPAPZCTreeManagerParent(
+ PAPZCTreeManagerParent* aActor) = 0;
+
+ virtual PLayerTransactionParent* AllocPLayerTransactionParent(
+ const nsTArray<LayersBackend>& layersBackendHints,
+ const LayersId& id) = 0;
+ virtual bool DeallocPLayerTransactionParent(
+ PLayerTransactionParent* aActor) = 0;
+
+ virtual PTextureParent* AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aBackend, const TextureFlags& aTextureFlags,
+ const LayersId& id, const uint64_t& aSerial,
+ const MaybeExternalImageId& aExternalImageId) = 0;
+ virtual bool DeallocPTextureParent(PTextureParent* aActor) = 0;
+
+ virtual PWebRenderBridgeParent* AllocPWebRenderBridgeParent(
+ const PipelineId& pipelineId, const LayoutDeviceIntSize& aSize,
+ const WindowKind& aWindowKind) = 0;
+ virtual bool DeallocPWebRenderBridgeParent(
+ PWebRenderBridgeParent* aActor) = 0;
+
+ virtual webgpu::PWebGPUParent* AllocPWebGPUParent() = 0;
+ virtual bool DeallocPWebGPUParent(webgpu::PWebGPUParent* aActor) = 0;
+
+ virtual PCompositorWidgetParent* AllocPCompositorWidgetParent(
+ const CompositorWidgetInitData& aInitData) = 0;
+ virtual bool DeallocPCompositorWidgetParent(
+ PCompositorWidgetParent* aActor) = 0;
+
+ virtual mozilla::ipc::IPCResult RecvRemotePluginsReady() = 0;
+ virtual mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& id) = 0;
+ virtual mozilla::ipc::IPCResult RecvFlushRenderingAsync() = 0;
+ virtual mozilla::ipc::IPCResult RecvForcePresent() = 0;
+ virtual mozilla::ipc::IPCResult RecvNotifyRegionInvalidated(
+ const nsIntRegion& region) = 0;
+ virtual mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() = 0;
+ virtual mozilla::ipc::IPCResult RecvAllPluginsCaptured() = 0;
+ virtual mozilla::ipc::IPCResult RecvBeginRecording(
+ const TimeStamp& aRecordingStart, BeginRecordingResolver&& aResolve) = 0;
+ virtual mozilla::ipc::IPCResult RecvEndRecordingToDisk(
+ EndRecordingToDiskResolver&& aResolve) = 0;
+ virtual mozilla::ipc::IPCResult RecvEndRecordingToMemory(
+ EndRecordingToMemoryResolver&& aResolve) = 0;
+ virtual mozilla::ipc::IPCResult RecvInitialize(
+ const LayersId& rootLayerTreeId) = 0;
+ virtual mozilla::ipc::IPCResult RecvWillClose() = 0;
+ virtual mozilla::ipc::IPCResult RecvPause() = 0;
+ virtual mozilla::ipc::IPCResult RecvRequestFxrOutput() = 0;
+ virtual mozilla::ipc::IPCResult RecvResume() = 0;
+ virtual mozilla::ipc::IPCResult RecvResumeAsync() = 0;
+ virtual mozilla::ipc::IPCResult RecvNotifyChildCreated(
+ const LayersId& id, CompositorOptions* compositorOptions) = 0;
+ virtual mozilla::ipc::IPCResult RecvMapAndNotifyChildCreated(
+ const LayersId& id, const ProcessId& owner,
+ CompositorOptions* compositorOptions) = 0;
+ virtual mozilla::ipc::IPCResult RecvNotifyChildRecreated(
+ const LayersId& id, CompositorOptions* compositorOptions) = 0;
+ virtual mozilla::ipc::IPCResult RecvMakeSnapshot(
+ const SurfaceDescriptor& inSnapshot, const IntRect& dirtyRect) = 0;
+ virtual mozilla::ipc::IPCResult RecvFlushRendering() = 0;
+ virtual mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() = 0;
+ virtual mozilla::ipc::IPCResult RecvStartFrameTimeRecording(
+ const int32_t& bufferSize, uint32_t* startIndex) = 0;
+ virtual mozilla::ipc::IPCResult RecvStopFrameTimeRecording(
+ const uint32_t& startIndex, nsTArray<float>* intervals) = 0;
+ virtual mozilla::ipc::IPCResult RecvCheckContentOnlyTDR(
+ const uint32_t& sequenceNum, bool* isContentOnlyTDR) = 0;
+ virtual mozilla::ipc::IPCResult RecvInitPCanvasParent(
+ Endpoint<PCanvasParent>&& aEndpoint) = 0;
+ virtual mozilla::ipc::IPCResult RecvReleasePCanvasParent() = 0;
+
+ virtual mozilla::ipc::IPCResult RecvSupportsAsyncDXGISurface(bool* value) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ virtual mozilla::ipc::IPCResult RecvPreferredDXGIAdapter(
+ DxgiAdapterDesc* desc) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ virtual already_AddRefed<PWebGLParent> AllocPWebGLParent() = 0;
+
+ bool mCanSend;
+
+ private:
+ RefPtr<CompositorManagerParent> mCompositorManager;
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(
+ CompositorBridgeParentBase::TransformsToSkip)
+
+class CompositorBridgeParent final : public CompositorBridgeParentBase,
+ public CompositorController,
+ public CompositorVsyncSchedulerOwner {
+ friend class CompositorThreadHolder;
+ friend class InProcessCompositorSession;
+ friend class gfx::GPUProcessManager;
+ friend class gfx::GPUParent;
+ friend class PCompositorBridgeParent;
+#ifdef FUZZING
+ friend class mozilla::ipc::ProtocolFuzzerHelper;
+#endif
+
+ public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override {
+ return CompositorBridgeParentBase::AddRef();
+ }
+ NS_IMETHOD_(MozExternalRefCountType) Release() override {
+ return CompositorBridgeParentBase::Release();
+ }
+
+ explicit CompositorBridgeParent(CompositorManagerParent* aManager,
+ CSSToLayoutDeviceScale aScale,
+ const TimeDuration& aVsyncRate,
+ const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize);
+
+ void InitSameProcess(widget::CompositorWidget* aWidget,
+ const LayersId& aLayerTreeId);
+
+ mozilla::ipc::IPCResult RecvInitialize(
+ const LayersId& aRootLayerTreeId) override;
+ mozilla::ipc::IPCResult RecvWillClose() override;
+ mozilla::ipc::IPCResult RecvPause() override;
+ mozilla::ipc::IPCResult RecvRequestFxrOutput() override;
+ mozilla::ipc::IPCResult RecvResume() override;
+ mozilla::ipc::IPCResult RecvResumeAsync() override;
+ mozilla::ipc::IPCResult RecvNotifyChildCreated(
+ const LayersId& child, CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvMapAndNotifyChildCreated(
+ const LayersId& child, const base::ProcessId& pid,
+ CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvNotifyChildRecreated(
+ const LayersId& child, CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& child) override;
+ mozilla::ipc::IPCResult RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
+ const gfx::IntRect& aRect) override;
+ mozilla::ipc::IPCResult RecvFlushRendering() override;
+ mozilla::ipc::IPCResult RecvFlushRenderingAsync() override;
+ mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override;
+ mozilla::ipc::IPCResult RecvForcePresent() override;
+
+ mozilla::ipc::IPCResult RecvNotifyRegionInvalidated(
+ const nsIntRegion& aRegion) override;
+ mozilla::ipc::IPCResult RecvStartFrameTimeRecording(
+ const int32_t& aBufferSize, uint32_t* aOutStartIndex) override;
+ mozilla::ipc::IPCResult RecvStopFrameTimeRecording(
+ const uint32_t& aStartIndex, nsTArray<float>* intervals) override;
+
+ mozilla::ipc::IPCResult RecvCheckContentOnlyTDR(
+ const uint32_t& sequenceNum, bool* isContentOnlyTDR) override {
+ return IPC_OK();
+ }
+
+ // Unused for chrome <-> compositor communication (which this class does).
+ // @see ContentCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint
+ mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() override {
+ return IPC_OK();
+ };
+
+ mozilla::ipc::IPCResult RecvAllPluginsCaptured() override;
+ mozilla::ipc::IPCResult RecvBeginRecording(
+ const TimeStamp& aRecordingStart,
+ BeginRecordingResolver&& aResolve) override;
+ mozilla::ipc::IPCResult RecvEndRecordingToDisk(
+ EndRecordingToDiskResolver&& aResolve) override;
+ mozilla::ipc::IPCResult RecvEndRecordingToMemory(
+ EndRecordingToMemoryResolver&& aResolve) override;
+
+ void NotifyMemoryPressure() override;
+ void AccumulateMemoryReport(wr::MemoryReport*) override;
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const TransactionInfo& aInfo,
+ bool aHitTestUpdate) override;
+ void ScheduleComposite(LayerTransactionParent* aLayerTree) override;
+ bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override;
+ void LeaveTestMode(const LayersId& aId) override;
+ void ApplyAsyncProperties(LayerTransactionParent* aLayerTree,
+ TransformsToSkip aSkip) override;
+ CompositorAnimationStorage* GetAnimationStorage();
+ using JankedAnimations =
+ std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn>;
+ void NotifyJankedAnimations(const JankedAnimations& aJankedAnimations);
+ void SetTestAsyncScrollOffset(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) override;
+ void SetTestAsyncZoom(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) override;
+ void FlushApzRepaints(const LayersId& aLayersId) override;
+ void GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) override;
+ void GetFrameUniformity(const LayersId& aLayersId,
+ FrameUniformityData* data) override;
+ void SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) override;
+ AsyncCompositionManager* GetCompositionManager(
+ LayerTransactionParent* aLayerTree) override {
+ return mCompositionManager;
+ }
+ void SetFixedLayerMargins(ScreenIntCoord aTop, ScreenIntCoord aBottom);
+
+ PTextureParent* AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) override;
+ bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ mozilla::ipc::IPCResult RecvInitPCanvasParent(
+ Endpoint<PCanvasParent>&& aEndpoint) final;
+
+ mozilla::ipc::IPCResult RecvReleasePCanvasParent() final;
+
+ bool IsSameProcess() const override;
+
+ void NotifyWebRenderDisableNativeCompositor();
+
+ void NotifyDidRender(const VsyncId& aCompositeStartId,
+ TimeStamp& aCompositeStart, TimeStamp& aRenderStart,
+ TimeStamp& aCompositeEnd,
+ wr::RendererStats* aStats = nullptr);
+ void NotifyPipelineRendered(const wr::PipelineId& aPipelineId,
+ const wr::Epoch& aEpoch,
+ const VsyncId& aCompositeStartId,
+ TimeStamp& aCompositeStart,
+ TimeStamp& aRenderStart, TimeStamp& aCompositeEnd,
+ wr::RendererStats* aStats = nullptr);
+ void NotifyDidSceneBuild(RefPtr<const wr::WebRenderPipelineInfo> aInfo);
+ RefPtr<AsyncImagePipelineManager> GetAsyncImagePipelineManager() const;
+
+ PCompositorWidgetParent* AllocPCompositorWidgetParent(
+ const CompositorWidgetInitData& aInitData) override;
+ bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
+
+ void ObserveLayersUpdate(LayersId aLayersId, LayersObserverEpoch aEpoch,
+ bool aActive) override {}
+
+ /**
+ * This forces the is-first-paint flag to true. This is intended to
+ * be called by the widget code when it loses its viewport information
+ * (or for whatever reason wants to refresh the viewport information).
+ * The information refresh happens because the compositor will call
+ * SetFirstPaintViewport on the next frame of composition.
+ */
+ void ForceIsFirstPaint();
+
+ static void SetShadowProperties(Layer* aLayer);
+
+ void NotifyChildCreated(LayersId aChild);
+
+ void AsyncRender();
+
+ // Can be called from any thread
+ void ScheduleRenderOnCompositorThread() override;
+
+ void ScheduleComposition();
+
+ void NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstPaint,
+ const FocusTarget& aFocusTarget,
+ bool aScheduleComposite,
+ uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ bool aHitTestUpdate);
+
+ void UpdatePaintTime(LayerTransactionParent* aLayerTree,
+ const TimeDuration& aPaintTime) override;
+ void RegisterPayloads(LayerTransactionParent* aLayerTree,
+ const nsTArray<CompositionPayload>& aPayload) override;
+
+ /**
+ * Check rotation info and schedule a rendering task if needed.
+ * Only can be called from compositor thread.
+ */
+ void ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
+ bool aIsFirstPaint);
+
+ static void ScheduleForcedComposition(const LayersId& aLayersId);
+
+ /**
+ * Returns the unique layer tree identifier that corresponds to the root
+ * tree of this compositor.
+ */
+ LayersId RootLayerTreeId();
+
+ /**
+ * Notify local and remote layer trees connected to this compositor that
+ * the compositor's local device is being reset. All layers must be
+ * invalidated to clear any cached TextureSources.
+ *
+ * This must be called on the compositor thread.
+ */
+ void InvalidateRemoteLayers();
+
+ /**
+ * Initialize statics.
+ */
+ static void InitializeStatics();
+
+ /**
+ * Returns a pointer to the CompositorBridgeParent corresponding to the given
+ * ID.
+ */
+ static CompositorBridgeParent* GetCompositorBridgeParent(uint64_t id);
+
+ /**
+ * Notify the compositor for the given layer tree that vsync has occurred.
+ */
+ static void NotifyVsync(const VsyncEvent& aVsync, const LayersId& aLayersId);
+
+ /**
+ * Set aController as the pan/zoom callback for the subtree referred
+ * to by aLayersId.
+ *
+ * Must run on content main thread.
+ */
+ static void SetControllerForLayerTree(LayersId aLayersId,
+ GeckoContentController* aController);
+
+ struct LayerTreeState {
+ LayerTreeState();
+ ~LayerTreeState();
+ RefPtr<Layer> mRoot;
+ RefPtr<GeckoContentController> mController;
+ APZCTreeManagerParent* mApzcTreeManagerParent;
+ RefPtr<CompositorBridgeParent> mParent;
+ HostLayerManager* mLayerManager;
+ RefPtr<WebRenderBridgeParent> mWrBridge;
+ // Pointer to the ContentCompositorBridgeParent. Used by APZCs to share
+ // their FrameMetrics with the corresponding child process that holds
+ // the PCompositorBridgeChild
+ ContentCompositorBridgeParent* mContentCompositorBridgeParent;
+ TargetConfig mTargetConfig;
+ LayerTransactionParent* mLayerTree;
+ nsTArray<PluginWindowData> mPluginData;
+ bool mUpdatedPluginDataAvailable;
+
+ CompositorController* GetCompositorController() const;
+ MetricsSharingController* CrossProcessSharingController() const;
+ MetricsSharingController* InProcessSharingController() const;
+ RefPtr<UiCompositorControllerParent> mUiControllerParent;
+ };
+
+ /**
+ * Lookup the indirect shadow tree for |aId| and return it if it
+ * exists. Otherwise null is returned. This must only be called on
+ * the compositor thread.
+ */
+ static LayerTreeState* GetIndirectShadowTree(LayersId aId);
+
+ /**
+ * Lookup the indirect shadow tree for |aId|, call the function object and
+ * return true if found. If not found, return false.
+ */
+ static bool CallWithIndirectShadowTree(
+ LayersId aId, const std::function<void(LayerTreeState&)>& aFunc);
+
+ /**
+ * Given the layers id for a content process, get the APZCTreeManagerParent
+ * for the corresponding *root* layers id. That is, the APZCTreeManagerParent,
+ * if one is found, will always be connected to the parent process rather
+ * than a content process. Note that unless the compositor process is
+ * separated this is expected to return null, because if the compositor is
+ * living in the gecko parent process then there is no APZCTreeManagerParent
+ * for the parent process.
+ */
+ static APZCTreeManagerParent* GetApzcTreeManagerParentForRoot(
+ LayersId aContentLayersId);
+ /**
+ * Same as the GetApzcTreeManagerParentForRoot function, but returns
+ * the GeckoContentController for the parent process.
+ */
+ static GeckoContentController* GetGeckoContentControllerForRoot(
+ LayersId aContentLayersId);
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ /**
+ * Calculates and requests the main thread update plugin positioning, clip,
+ * and visibility via ipc.
+ */
+ bool UpdatePluginWindowState(LayersId aId);
+
+ /**
+ * Plugin visibility helpers for the apz (main thread) and compositor
+ * thread.
+ */
+ void ScheduleShowAllPluginWindows() override;
+ void ScheduleHideAllPluginWindows() override;
+ void ShowAllPluginWindows();
+ void HideAllPluginWindows();
+#else
+ void ScheduleShowAllPluginWindows() override {}
+ void ScheduleHideAllPluginWindows() override {}
+#endif
+
+ /**
+ * Main thread response for a plugin visibility request made by the
+ * compositor thread.
+ */
+ mozilla::ipc::IPCResult RecvRemotePluginsReady() override;
+
+ /**
+ * Used by the profiler to denote when a vsync occured
+ */
+ static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp);
+
+ widget::CompositorWidget* GetWidget() { return mWidget; }
+
+ virtual void ForceComposeToTarget(
+ gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) override;
+
+ PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(
+ const LayersId& aLayersId) override;
+ bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
+
+ // Helper method so that we don't have to expose mApzcTreeManager to
+ // ContentCompositorBridgeParent.
+ void AllocateAPZCTreeManagerParent(
+ const MonitorAutoLock& aProofOfLayerTreeStateLock,
+ const LayersId& aLayersId, LayerTreeState& aLayerTreeStateToUpdate);
+
+ PAPZParent* AllocPAPZParent(const LayersId& aLayersId) override;
+ bool DeallocPAPZParent(PAPZParent* aActor) override;
+
+ RefPtr<APZSampler> GetAPZSampler() const;
+ RefPtr<APZUpdater> GetAPZUpdater() const;
+ RefPtr<OMTASampler> GetOMTASampler() const;
+
+ CompositorOptions GetOptions() const { return mOptions; }
+
+ TimeDuration GetVsyncInterval() const override {
+ // the variable is called "rate" but really it's an interval
+ return mVsyncRate;
+ }
+
+ PWebRenderBridgeParent* AllocPWebRenderBridgeParent(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize,
+ const WindowKind& aWindowKind) override;
+ bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
+ RefPtr<WebRenderBridgeParent> GetWebRenderBridgeParent() const;
+ Maybe<TimeStamp> GetTestingTimeStamp() const;
+
+ webgpu::PWebGPUParent* AllocPWebGPUParent() override;
+ bool DeallocPWebGPUParent(webgpu::PWebGPUParent* aActor) override;
+
+ static CompositorBridgeParent* GetCompositorBridgeParentFromLayersId(
+ const LayersId& aLayersId);
+ static RefPtr<CompositorBridgeParent> GetCompositorBridgeParentFromWindowId(
+ const wr::WindowId& aWindowId);
+
+ /**
+ * This returns a reference to the IAPZCTreeManager "controller subinterface"
+ * to which pan/zoom-related events can be sent. The controller subinterface
+ * doesn't expose any sampler-thread APZCTreeManager methods.
+ */
+ static already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager(
+ LayersId aLayersId);
+
+ WebRenderBridgeParent* GetWrBridge() { return mWrBridge; }
+ webgpu::WebGPUParent* GetWebGPUBridge() { return mWebGPUBridge; }
+
+ already_AddRefed<PWebGLParent> AllocPWebGLParent() override {
+ MOZ_ASSERT_UNREACHABLE(
+ "This message is CrossProcessCompositorBridgeParent only");
+ return nullptr;
+ }
+
+ private:
+ void Initialize();
+
+ /**
+ * Called during destruction in order to release resources as early as
+ * possible.
+ */
+ void StopAndClearResources();
+
+ /**
+ * Release compositor-thread resources referred to by |aID|.
+ *
+ * Must run on the content main thread.
+ */
+ static void DeallocateLayerTreeId(LayersId aId);
+
+ /**
+ * Notify the compositor the quality settings have been updated.
+ */
+ static void UpdateQualitySettings();
+
+ /**
+ * Notify the compositor the debug flags have been updated.
+ */
+ static void UpdateDebugFlags();
+
+ /**
+ * Notify the compositor the debug flags have been updated.
+ */
+ static void UpdateWebRenderMultithreading();
+
+ /**
+ * Notify the compositor webrender batching parameters have been updated.
+ */
+ static void UpdateWebRenderBatchingParameters();
+
+ /**
+ * Notify the compositor webrender profiler UI string has been updated.
+ */
+ static void UpdateWebRenderProfilerUI();
+
+ /**
+ * Wrap the data structure to be sent over IPC.
+ */
+ Maybe<CollectedFramesParams> WrapCollectedFrames(CollectedFrames&& aFrames);
+
+ protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~CompositorBridgeParent();
+
+ void DeferredDestroy();
+
+ PLayerTransactionParent* AllocPLayerTransactionParent(
+ const nsTArray<LayersBackend>& aBackendHints,
+ const LayersId& aId) override;
+ bool DeallocPLayerTransactionParent(
+ PLayerTransactionParent* aLayers) override;
+
+ void SetEGLSurfaceRect(int x, int y, int width, int height);
+
+ void InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints);
+
+ public:
+ void PauseComposition();
+ void ResumeComposition();
+ void ResumeCompositionAndResize(int x, int y, int width, int height);
+ void Invalidate();
+ bool IsPaused() { return mPaused; }
+
+ protected:
+ void ForceComposition();
+ void CancelCurrentCompositeTask();
+
+ // CompositorVsyncSchedulerOwner
+ bool IsPendingComposite() override;
+ void FinishPendingComposite() override;
+ void CompositeToTarget(VsyncId aId, gfx::DrawTarget* aTarget,
+ const gfx::IntRect* aRect = nullptr) override;
+
+ bool InitializeAdvancedLayers(const nsTArray<LayersBackend>& aBackendHints,
+ TextureFactoryIdentifier* aOutIdentifier);
+ RefPtr<Compositor> NewCompositor(
+ const nsTArray<LayersBackend>& aBackendHints);
+
+ /**
+ * Add a compositor to the global compositor map.
+ */
+ static void AddCompositor(CompositorBridgeParent* compositor, uint64_t* id);
+ /**
+ * Remove a compositor from the global compositor map.
+ */
+ static CompositorBridgeParent* RemoveCompositor(uint64_t id);
+
+ /**
+ * Creates the global compositor map.
+ */
+ static void Setup();
+
+ /**
+ * Remaning cleanups after the compositore thread is gone.
+ */
+ static void FinishShutdown();
+
+ /**
+ * Return true if current state allows compositing, that is
+ * finishing a layers transaction.
+ */
+ bool CanComposite();
+
+ void DidComposite(const VsyncId& aId, TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd);
+
+ void NotifyDidComposite(TransactionId aTransactionId, VsyncId aId,
+ TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
+
+ // The indirect layer tree lock must be held before calling this function.
+ // Callback should take (LayerTreeState* aState, const LayersId& aLayersId)
+ template <typename Lambda>
+ inline void ForEachIndirectLayerTree(const Lambda& aCallback);
+
+ // The indirect layer tree lock must be held before calling this function.
+ // Callback should take (LayerTreeState* aState, const LayersId& aLayersId)
+ template <typename Lambda>
+ static inline void ForEachWebRenderBridgeParent(const Lambda& aCallback);
+
+ RefPtr<HostLayerManager> mLayerManager;
+ RefPtr<Compositor> mCompositor;
+ RefPtr<AsyncCompositionManager> mCompositionManager;
+ RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
+ RefPtr<WebRenderBridgeParent> mWrBridge;
+ RefPtr<webgpu::WebGPUParent> mWebGPUBridge;
+ widget::CompositorWidget* mWidget;
+ Maybe<TimeStamp> mTestTime;
+ CSSToLayoutDeviceScale mScale;
+ TimeDuration mVsyncRate;
+
+ TransactionId mPendingTransaction;
+ TimeStamp mRefreshStartTime;
+ TimeStamp mTxnStartTime;
+ TimeStamp mFwdTime;
+
+ bool mPaused;
+ bool mHaveCompositionRecorder;
+ bool mIsForcedFirstPaint;
+
+ bool mUseExternalSurfaceSize;
+ gfx::IntSize mEGLSurfaceSize;
+
+ CompositorOptions mOptions;
+
+ mozilla::Monitor mPauseCompositionMonitor;
+ mozilla::Monitor mResumeCompositionMonitor;
+
+ uint64_t mCompositorBridgeID;
+ LayersId mRootLayerTreeID;
+
+ bool mOverrideComposeReadiness;
+ RefPtr<CancelableRunnable> mForceCompositionTask;
+
+ RefPtr<APZCTreeManager> mApzcTreeManager;
+ RefPtr<APZSampler> mApzSampler;
+ RefPtr<APZUpdater> mApzUpdater;
+ RefPtr<OMTASampler> mOMTASampler;
+
+ RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
+ // This makes sure the compositorParent is not destroyed before receiving
+ // confirmation that the channel is closed.
+ // mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy.
+ RefPtr<CompositorBridgeParent> mSelfRef;
+ RefPtr<CompositorAnimationStorage> mAnimationStorage;
+
+ TimeDuration mPaintTime;
+
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ // cached plugin data used to reduce the number of updates we request.
+ LayersId mLastPluginUpdateLayerTreeId;
+ nsIntPoint mPluginsLayerOffset;
+ nsIntRegion mPluginsLayerVisibleRegion;
+ nsTArray<PluginWindowData> mCachedPluginData;
+ // Time until which we will block composition to wait for plugin updates.
+ TimeStamp mWaitForPluginsUntil;
+ // Indicates that we have actually blocked a composition waiting for plugins.
+ bool mHaveBlockedForPlugins = false;
+ // indicates if plugin window visibility and metric updates are currently
+ // being defered due to a scroll operation.
+ bool mDeferPluginWindows;
+ // indicates if the plugin windows were hidden, and need to be made
+ // visible again even if their geometry has not changed.
+ bool mPluginWindowsHidden;
+#endif
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeParent);
+};
+
+int32_t RecordContentFrameTime(
+ const VsyncId& aTxnId, const TimeStamp& aVsyncStart,
+ const TimeStamp& aTxnStart, const VsyncId& aCompositeId,
+ const TimeStamp& aCompositeEnd, const TimeDuration& aFullPaintTime,
+ const TimeDuration& aVsyncRate, bool aContainsSVGGroup,
+ bool aRecordUploadStats, wr::RendererStats* aStats = nullptr);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorBridgeParent_h
diff --git a/gfx/layers/ipc/CompositorManagerChild.cpp b/gfx/layers/ipc/CompositorManagerChild.cpp
new file mode 100644
index 0000000000..b629d28229
--- /dev/null
+++ b/gfx/layers/ipc/CompositorManagerChild.cpp
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorManagerChild.h"
+
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/dom/ContentChild.h" // for ContentChild
+#include "mozilla/dom/BrowserChild.h" // for BrowserChild
+#include "mozilla/ipc/Endpoint.h"
+#include "VsyncSource.h"
+
+namespace mozilla {
+namespace layers {
+
+using gfx::GPUProcessManager;
+
+StaticRefPtr<CompositorManagerChild> CompositorManagerChild::sInstance;
+
+/* static */
+bool CompositorManagerChild::IsInitialized(uint64_t aProcessToken) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return sInstance && sInstance->CanSend() &&
+ sInstance->mProcessToken == aProcessToken;
+}
+
+/* static */
+void CompositorManagerChild::InitSameProcess(uint32_t aNamespace,
+ uint64_t aProcessToken) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_WARN_IF(IsInitialized(aProcessToken))) {
+ MOZ_ASSERT_UNREACHABLE("Already initialized same process");
+ return;
+ }
+
+ RefPtr<CompositorManagerParent> parent =
+ CompositorManagerParent::CreateSameProcess();
+ RefPtr<CompositorManagerChild> child =
+ new CompositorManagerChild(parent, aProcessToken, aNamespace);
+ if (NS_WARN_IF(!child->CanSend())) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Failed to open same process protocol");
+ return;
+ }
+
+ parent->BindComplete(/* aIsRoot */ true);
+ sInstance = std::move(child);
+}
+
+/* static */
+bool CompositorManagerChild::Init(Endpoint<PCompositorManagerChild>&& aEndpoint,
+ uint32_t aNamespace,
+ uint64_t aProcessToken /* = 0 */) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (sInstance) {
+ MOZ_ASSERT(sInstance->mNamespace != aNamespace);
+ }
+
+ sInstance = new CompositorManagerChild(std::move(aEndpoint), aProcessToken,
+ aNamespace);
+ return sInstance->CanSend();
+}
+
+/* static */
+void CompositorManagerChild::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ CompositorBridgeChild::ShutDown();
+
+ if (!sInstance) {
+ return;
+ }
+
+ sInstance->Close();
+ sInstance = nullptr;
+}
+
+/* static */
+void CompositorManagerChild::OnGPUProcessLost(uint64_t aProcessToken) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Since GPUChild and CompositorManagerChild will race on ActorDestroy, we
+ // cannot know if the CompositorManagerChild is about to be released but has
+ // yet to be. As such, we want to pre-emptively set mCanSend to false.
+ if (sInstance && sInstance->mProcessToken == aProcessToken) {
+ sInstance->mCanSend = false;
+ }
+}
+
+/* static */
+bool CompositorManagerChild::CreateContentCompositorBridge(
+ uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
+ return false;
+ }
+
+ CompositorBridgeOptions options = ContentCompositorOptions();
+
+ RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance);
+ if (NS_WARN_IF(
+ !sInstance->SendPCompositorBridgeConstructor(bridge, options))) {
+ return false;
+ }
+
+ bridge->InitForContent(aNamespace);
+ return true;
+}
+
+/* static */
+already_AddRefed<CompositorBridgeChild>
+CompositorManagerChild::CreateWidgetCompositorBridge(
+ uint64_t aProcessToken, LayerManager* aLayerManager, uint32_t aNamespace,
+ CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
+ return nullptr;
+ }
+
+ TimeDuration vsyncRate = gfxPlatform::GetPlatform()
+ ->GetHardwareVsync()
+ ->GetGlobalDisplay()
+ .GetVsyncRate();
+
+ CompositorBridgeOptions options = WidgetCompositorOptions(
+ aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, aSurfaceSize);
+
+ RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance);
+ if (NS_WARN_IF(
+ !sInstance->SendPCompositorBridgeConstructor(bridge, options))) {
+ return nullptr;
+ }
+
+ bridge->InitForWidget(aProcessToken, aLayerManager, aNamespace);
+ return bridge.forget();
+}
+
+/* static */
+already_AddRefed<CompositorBridgeChild>
+CompositorManagerChild::CreateSameProcessWidgetCompositorBridge(
+ LayerManager* aLayerManager, uint32_t aNamespace) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
+ return nullptr;
+ }
+
+ CompositorBridgeOptions options = SameProcessWidgetCompositorOptions();
+
+ RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance);
+ if (NS_WARN_IF(
+ !sInstance->SendPCompositorBridgeConstructor(bridge, options))) {
+ return nullptr;
+ }
+
+ bridge->InitForWidget(1, aLayerManager, aNamespace);
+ return bridge.forget();
+}
+
+CompositorManagerChild::CompositorManagerChild(CompositorManagerParent* aParent,
+ uint64_t aProcessToken,
+ uint32_t aNamespace)
+ : mProcessToken(aProcessToken),
+ mNamespace(aNamespace),
+ mResourceId(0),
+ mCanSend(false) {
+ MOZ_ASSERT(aParent);
+
+ SetOtherProcessId(base::GetCurrentProcId());
+ ipc::MessageChannel* channel = aParent->GetIPCChannel();
+ if (NS_WARN_IF(!Open(channel, CompositorThread(), ipc::ChildSide))) {
+ return;
+ }
+
+ mCanSend = true;
+ AddRef();
+ SetReplyTimeout();
+}
+
+CompositorManagerChild::CompositorManagerChild(
+ Endpoint<PCompositorManagerChild>&& aEndpoint, uint64_t aProcessToken,
+ uint32_t aNamespace)
+ : mProcessToken(aProcessToken),
+ mNamespace(aNamespace),
+ mResourceId(0),
+ mCanSend(false) {
+ if (NS_WARN_IF(!aEndpoint.Bind(this))) {
+ return;
+ }
+
+ mCanSend = true;
+ AddRef();
+ SetReplyTimeout();
+}
+
+void CompositorManagerChild::ActorDealloc() {
+ MOZ_ASSERT(!mCanSend);
+ Release();
+}
+
+void CompositorManagerChild::ActorDestroy(ActorDestroyReason aReason) {
+ mCanSend = false;
+ if (sInstance == this) {
+ sInstance = nullptr;
+ }
+}
+
+void CompositorManagerChild::HandleFatalError(const char* aMsg) const {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+void CompositorManagerChild::ProcessingError(Result aCode,
+ const char* aReason) {
+ if (aCode != MsgDropped) {
+ gfxDevCrash(gfx::LogReason::ProcessingError)
+ << "Processing error in CompositorBridgeChild: " << int(aCode);
+ }
+}
+
+void CompositorManagerChild::SetReplyTimeout() {
+#ifndef DEBUG
+ // Add a timeout for release builds to kill GPU process when it hangs.
+ if (XRE_IsParentProcess() && GPUProcessManager::Get()->GetGPUChild()) {
+ int32_t timeout =
+ StaticPrefs::layers_gpu_process_ipc_reply_timeout_ms_AtStartup();
+ SetReplyTimeoutMs(timeout);
+ }
+#endif
+}
+
+bool CompositorManagerChild::ShouldContinueFromReplyTimeout() {
+ if (XRE_IsParentProcess()) {
+ gfxCriticalNote << "Killing GPU process due to IPC reply timeout";
+ MOZ_DIAGNOSTIC_ASSERT(GPUProcessManager::Get()->GetGPUChild());
+ GPUProcessManager::Get()->KillProcess();
+ }
+ return false;
+}
+
+mozilla::ipc::IPCResult CompositorManagerChild::RecvNotifyWebRenderError(
+ const WebRenderError&& aError) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ GPUProcessManager::Get()->NotifyWebRenderError(aError);
+ return IPC_OK();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorManagerChild.h b/gfx/layers/ipc/CompositorManagerChild.h
new file mode 100644
index 0000000000..50adedbb63
--- /dev/null
+++ b/gfx/layers/ipc/CompositorManagerChild.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITORMANAGERCHILD_H
+#define MOZILLA_GFX_COMPOSITORMANAGERCHILD_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/layers/PCompositorManagerChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositorBridgeChild;
+class CompositorManagerParent;
+class LayerManager;
+
+class CompositorManagerChild : public PCompositorManagerChild {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerChild)
+
+ public:
+ static bool IsInitialized(uint64_t aProcessToken);
+ static void InitSameProcess(uint32_t aNamespace, uint64_t aProcessToken);
+ static bool Init(Endpoint<PCompositorManagerChild>&& aEndpoint,
+ uint32_t aNamespace, uint64_t aProcessToken = 0);
+ static void Shutdown();
+ static void OnGPUProcessLost(uint64_t aProcessToken);
+
+ static bool CreateContentCompositorBridge(uint32_t aNamespace);
+
+ static already_AddRefed<CompositorBridgeChild> CreateWidgetCompositorBridge(
+ uint64_t aProcessToken, LayerManager* aLayerManager, uint32_t aNamespace,
+ CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize);
+
+ static already_AddRefed<CompositorBridgeChild>
+ CreateSameProcessWidgetCompositorBridge(LayerManager* aLayerManager,
+ uint32_t aNamespace);
+
+ static CompositorManagerChild* GetInstance() {
+ MOZ_ASSERT(NS_IsMainThread());
+ return sInstance;
+ }
+
+ bool CanSend() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mCanSend;
+ }
+
+ uint32_t GetNextResourceId() {
+ MOZ_ASSERT(NS_IsMainThread());
+ return ++mResourceId;
+ }
+
+ uint32_t GetNamespace() const { return mNamespace; }
+
+ bool OwnsExternalImageId(const wr::ExternalImageId& aId) const {
+ return mNamespace == static_cast<uint32_t>(wr::AsUint64(aId) >> 32);
+ }
+
+ wr::ExternalImageId GetNextExternalImageId() {
+ uint64_t id = GetNextResourceId();
+ MOZ_RELEASE_ASSERT(id != 0);
+ id |= (static_cast<uint64_t>(mNamespace) << 32);
+ return wr::ToExternalImageId(id);
+ }
+
+ void ActorDestroy(ActorDestroyReason aReason) override;
+
+ void HandleFatalError(const char* aMsg) const override;
+
+ void ProcessingError(Result aCode, const char* aReason) override;
+
+ bool ShouldContinueFromReplyTimeout() override;
+
+ mozilla::ipc::IPCResult RecvNotifyWebRenderError(
+ const WebRenderError&& aError);
+
+ private:
+ static StaticRefPtr<CompositorManagerChild> sInstance;
+
+ CompositorManagerChild(CompositorManagerParent* aParent,
+ uint64_t aProcessToken, uint32_t aNamespace);
+
+ CompositorManagerChild(Endpoint<PCompositorManagerChild>&& aEndpoint,
+ uint64_t aProcessToken, uint32_t aNamespace);
+
+ virtual ~CompositorManagerChild() = default;
+
+ void ActorDealloc() override;
+
+ void SetReplyTimeout();
+
+ uint64_t mProcessToken;
+ uint32_t mNamespace;
+ uint32_t mResourceId;
+ bool mCanSend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositorManagerParent.cpp b/gfx/layers/ipc/CompositorManagerParent.cpp
new file mode 100644
index 0000000000..8b6ee34080
--- /dev/null
+++ b/gfx/layers/ipc/CompositorManagerParent.cpp
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/ContentCompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/SharedSurfacesParent.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "gfxPlatform.h"
+#include "VsyncSource.h"
+
+namespace mozilla {
+namespace layers {
+
+StaticRefPtr<CompositorManagerParent> CompositorManagerParent::sInstance;
+StaticMutex CompositorManagerParent::sMutex;
+
+#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
+StaticAutoPtr<nsTArray<CompositorManagerParent*>>
+ CompositorManagerParent::sActiveActors;
+#endif
+
+/* static */
+already_AddRefed<CompositorManagerParent>
+CompositorManagerParent::CreateSameProcess() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sMutex);
+
+ // We are creating a manager for the UI process, inside the combined GPU/UI
+ // process. It is created more-or-less the same but we retain a reference to
+ // the parent to access state.
+ if (NS_WARN_IF(sInstance)) {
+ MOZ_ASSERT_UNREACHABLE("Already initialized");
+ return nullptr;
+ }
+
+ // The child is responsible for setting up the IPC channel in the same
+ // process case because if we open from the child perspective, we can do it
+ // on the main thread and complete before we return the manager handles.
+ RefPtr<CompositorManagerParent> parent = new CompositorManagerParent();
+ parent->SetOtherProcessId(base::GetCurrentProcId());
+ return parent.forget();
+}
+
+/* static */
+bool CompositorManagerParent::Create(
+ Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We are creating a manager for the another process, inside the GPU process
+ // (or UI process if it subsumbed the GPU process).
+ MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId());
+
+ if (!CompositorThreadHolder::IsActive()) {
+ return false;
+ }
+
+ RefPtr<CompositorManagerParent> bridge = new CompositorManagerParent();
+
+ RefPtr<Runnable> runnable =
+ NewRunnableMethod<Endpoint<PCompositorManagerParent>&&, bool>(
+ "CompositorManagerParent::Bind", bridge,
+ &CompositorManagerParent::Bind, std::move(aEndpoint), aIsRoot);
+ CompositorThread()->Dispatch(runnable.forget());
+ return true;
+}
+
+/* static */
+already_AddRefed<CompositorBridgeParent>
+CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(
+ CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // When we are in a combined UI / GPU process, InProcessCompositorSession
+ // requires both the parent and child PCompositorBridge actors for its own
+ // construction, which is done on the main thread. Normally
+ // CompositorBridgeParent is created on the compositor thread via the IPDL
+ // plumbing (CompositorManagerParent::AllocPCompositorBridgeParent). Thus to
+ // actually get a reference to the parent, we would need to block on the
+ // compositor thread until it handles our constructor message. Because only
+ // one one IPDL constructor is permitted per parent and child protocol, we
+ // cannot make the normal case async and this case sync. Instead what we do
+ // is leave the constructor async (a boon to the content process setup) and
+ // create the parent ahead of time. It will pull the preinitialized parent
+ // from the queue when it receives the message and give that to IPDL.
+
+ // Note that the static mutex not only is used to protect sInstance, but also
+ // mPendingCompositorBridges.
+ StaticMutexAutoLock lock(sMutex);
+ if (NS_WARN_IF(!sInstance)) {
+ return nullptr;
+ }
+
+ TimeDuration vsyncRate = gfxPlatform::GetPlatform()
+ ->GetHardwareVsync()
+ ->GetGlobalDisplay()
+ .GetVsyncRate();
+
+ RefPtr<CompositorBridgeParent> bridge =
+ new CompositorBridgeParent(sInstance, aScale, vsyncRate, aOptions,
+ aUseExternalSurfaceSize, aSurfaceSize);
+
+ sInstance->mPendingCompositorBridges.AppendElement(bridge);
+ return bridge.forget();
+}
+
+CompositorManagerParent::CompositorManagerParent()
+ : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) {}
+
+CompositorManagerParent::~CompositorManagerParent() = default;
+
+void CompositorManagerParent::Bind(
+ Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (NS_WARN_IF(!aEndpoint.Bind(this))) {
+ return;
+ }
+
+ BindComplete(aIsRoot);
+}
+
+void CompositorManagerParent::BindComplete(bool aIsRoot) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
+ NS_IsMainThread());
+
+ // Add the IPDL reference to ourself, so we can't get freed until IPDL is
+ // done with us.
+ AddRef();
+
+ StaticMutexAutoLock lock(sMutex);
+ if (aIsRoot) {
+ sInstance = this;
+ }
+
+#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
+ if (!sActiveActors) {
+ sActiveActors = new nsTArray<CompositorManagerParent*>();
+ }
+ sActiveActors->AppendElement(this);
+#endif
+}
+
+void CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason) {
+ SharedSurfacesParent::DestroyProcess(OtherPid());
+
+ StaticMutexAutoLock lock(sMutex);
+ if (sInstance == this) {
+ sInstance = nullptr;
+ }
+}
+
+void CompositorManagerParent::ActorDealloc() {
+ GetCurrentSerialEventTarget()->Dispatch(
+ NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy",
+ this, &CompositorManagerParent::DeferredDestroy));
+
+#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
+ StaticMutexAutoLock lock(sMutex);
+ if (sActiveActors) {
+ sActiveActors->RemoveElement(this);
+ }
+#endif
+ Release();
+}
+
+void CompositorManagerParent::DeferredDestroy() {
+ mCompositorThreadHolder = nullptr;
+}
+
+#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
+/* static */
+void CompositorManagerParent::ShutdownInternal() {
+ UniquePtr<nsTArray<CompositorManagerParent*>> actors;
+
+ // We move here because we may attempt to acquire the same lock during the
+ // destroy to remove the reference in sActiveActors.
+ {
+ StaticMutexAutoLock lock(sMutex);
+ actors = WrapUnique(sActiveActors.forget());
+ }
+
+ if (actors) {
+ for (auto& actor : *actors) {
+ actor->Close();
+ }
+ }
+}
+#endif // COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
+
+/* static */
+void CompositorManagerParent::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "layers::CompositorManagerParent::Shutdown",
+ []() -> void { CompositorManagerParent::ShutdownInternal(); }));
+#endif
+}
+
+already_AddRefed<PCompositorBridgeParent>
+CompositorManagerParent::AllocPCompositorBridgeParent(
+ const CompositorBridgeOptions& aOpt) {
+ switch (aOpt.type()) {
+ case CompositorBridgeOptions::TContentCompositorOptions: {
+ RefPtr<ContentCompositorBridgeParent> bridge =
+ new ContentCompositorBridgeParent(this);
+ return bridge.forget();
+ }
+ case CompositorBridgeOptions::TWidgetCompositorOptions: {
+ // Only the UI process is allowed to create widget compositors in the
+ // compositor process.
+ gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton();
+ if (NS_WARN_IF(!gpu || OtherPid() != gpu->OtherPid())) {
+ MOZ_ASSERT_UNREACHABLE("Child cannot create widget compositor!");
+ break;
+ }
+
+ const WidgetCompositorOptions& opt = aOpt.get_WidgetCompositorOptions();
+ RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent(
+ this, opt.scale(), opt.vsyncRate(), opt.options(),
+ opt.useExternalSurfaceSize(), opt.surfaceSize());
+ return bridge.forget();
+ }
+ case CompositorBridgeOptions::TSameProcessWidgetCompositorOptions: {
+ // If the GPU and UI process are combined, we actually already created the
+ // CompositorBridgeParent, so we need to reuse that to inject it into the
+ // IPDL framework.
+ if (NS_WARN_IF(OtherPid() != base::GetCurrentProcId())) {
+ MOZ_ASSERT_UNREACHABLE("Child cannot create same process compositor!");
+ break;
+ }
+
+ // Note that the static mutex not only is used to protect sInstance, but
+ // also mPendingCompositorBridges.
+ StaticMutexAutoLock lock(sMutex);
+ if (mPendingCompositorBridges.IsEmpty()) {
+ break;
+ }
+
+ RefPtr<CompositorBridgeParent> bridge = mPendingCompositorBridges[0];
+ mPendingCompositorBridges.RemoveElementAt(0);
+ return bridge.forget();
+ }
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvAddSharedSurface(
+ const wr::ExternalImageId& aId, const SurfaceDescriptorShared& aDesc) {
+ SharedSurfacesParent::Add(aId, aDesc, OtherPid());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvRemoveSharedSurface(
+ const wr::ExternalImageId& aId) {
+ SharedSurfacesParent::Remove(aId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvReportSharedSurfacesMemory(
+ ReportSharedSurfacesMemoryResolver&& aResolver) {
+ SharedSurfacesMemoryReport report;
+ SharedSurfacesParent::AccumulateMemoryReport(OtherPid(), report);
+ aResolver(std::move(report));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvNotifyMemoryPressure() {
+ nsTArray<PCompositorBridgeParent*> compositorBridges;
+ ManagedPCompositorBridgeParent(compositorBridges);
+ for (auto bridge : compositorBridges) {
+ static_cast<CompositorBridgeParentBase*>(bridge)->NotifyMemoryPressure();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvReportMemory(
+ ReportMemoryResolver&& aResolver) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MemoryReport aggregate;
+ PodZero(&aggregate);
+
+ // Accumulate RenderBackend usage.
+ nsTArray<PCompositorBridgeParent*> compositorBridges;
+ ManagedPCompositorBridgeParent(compositorBridges);
+ for (auto bridge : compositorBridges) {
+ static_cast<CompositorBridgeParentBase*>(bridge)->AccumulateMemoryReport(
+ &aggregate);
+ }
+
+ // Accumulate Renderer usage asynchronously, and resolve.
+ //
+ // Note that the IPDL machinery requires aResolver to be called on this
+ // thread, so we can't just pass it over to the renderer thread. We use
+ // an intermediate MozPromise instead.
+ wr::RenderThread::AccumulateMemoryReport(aggregate)->Then(
+ CompositorThread(), __func__,
+ [resolver = std::move(aResolver)](MemoryReport aReport) {
+ resolver(aReport);
+ },
+ [](bool) {
+ MOZ_ASSERT_UNREACHABLE("MemoryReport promises are never rejected");
+ });
+
+ return IPC_OK();
+}
+
+/* static */
+void CompositorManagerParent::NotifyWebRenderError(wr::WebRenderError aError) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ StaticMutexAutoLock lock(sMutex);
+ if (NS_WARN_IF(!sInstance)) {
+ return;
+ }
+ Unused << sInstance->SendNotifyWebRenderError(aError);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorManagerParent.h b/gfx/layers/ipc/CompositorManagerParent.h
new file mode 100644
index 0000000000..f16a85aca4
--- /dev/null
+++ b/gfx/layers/ipc/CompositorManagerParent.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_COMPOSITORMANAGERPARENT_H
+#define MOZILLA_GFX_COMPOSITORMANAGERPARENT_H
+
+#include <stdint.h> // for uint32_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/StaticMutex.h" // for StaticMutex
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/layers/PCompositorManagerParent.h"
+#include "nsTArray.h" // for AutoTArray
+
+namespace mozilla {
+namespace layers {
+
+class CompositorBridgeParent;
+class CompositorThreadHolder;
+
+#ifndef DEBUG
+# define COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
+#endif
+
+class CompositorManagerParent final : public PCompositorManagerParent {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerParent)
+
+ public:
+ static already_AddRefed<CompositorManagerParent> CreateSameProcess();
+ static bool Create(Endpoint<PCompositorManagerParent>&& aEndpoint,
+ bool aIsRoot);
+ static void Shutdown();
+
+ static already_AddRefed<CompositorBridgeParent>
+ CreateSameProcessWidgetCompositorBridge(CSSToLayoutDeviceScale aScale,
+ const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize);
+
+ mozilla::ipc::IPCResult RecvAddSharedSurface(
+ const wr::ExternalImageId& aId, const SurfaceDescriptorShared& aDesc);
+ mozilla::ipc::IPCResult RecvRemoveSharedSurface(
+ const wr::ExternalImageId& aId);
+ mozilla::ipc::IPCResult RecvReportSharedSurfacesMemory(
+ ReportSharedSurfacesMemoryResolver&&);
+
+ mozilla::ipc::IPCResult RecvNotifyMemoryPressure();
+
+ mozilla::ipc::IPCResult RecvReportMemory(ReportMemoryResolver&&);
+
+ void BindComplete(bool aIsRoot);
+ void ActorDestroy(ActorDestroyReason aReason) override;
+
+ already_AddRefed<PCompositorBridgeParent> AllocPCompositorBridgeParent(
+ const CompositorBridgeOptions& aOpt);
+
+ static void NotifyWebRenderError(wr::WebRenderError aError);
+
+ private:
+ static StaticRefPtr<CompositorManagerParent> sInstance;
+ static StaticMutex sMutex;
+
+#ifdef COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
+ static StaticAutoPtr<nsTArray<CompositorManagerParent*>> sActiveActors;
+ static void ShutdownInternal();
+#endif
+
+ CompositorManagerParent();
+ virtual ~CompositorManagerParent();
+
+ void Bind(Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot);
+
+ void ActorDealloc() override;
+
+ void DeferredDestroy();
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+
+ AutoTArray<RefPtr<CompositorBridgeParent>, 1> mPendingCompositorBridges;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositorThread.cpp b/gfx/layers/ipc/CompositorThread.cpp
new file mode 100644
index 0000000000..53a23cbc6f
--- /dev/null
+++ b/gfx/layers/ipc/CompositorThread.cpp
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "CompositorThread.h"
+
+#include "CompositorBridgeParent.h"
+#include "MainThreadUtils.h"
+#include "VRManagerParent.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/layers/CanvasTranslator.h"
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/media/MediaSystemResourceService.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
+static Atomic<bool> sFinishedCompositorShutDown(false);
+static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor;
+
+nsISerialEventTarget* CompositorThread() {
+ return sCompositorThreadHolder
+ ? sCompositorThreadHolder->GetCompositorThread()
+ : nullptr;
+}
+
+CompositorThreadHolder* CompositorThreadHolder::GetSingleton() {
+ return sCompositorThreadHolder;
+}
+
+CompositorThreadHolder::CompositorThreadHolder()
+ : mCompositorThread(CreateCompositorThread()) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+CompositorThreadHolder::~CompositorThreadHolder() {
+ sFinishedCompositorShutDown = true;
+}
+
+/* static */ already_AddRefed<nsIThread>
+CompositorThreadHolder::CreateCompositorThread() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(!sCompositorThreadHolder,
+ "The compositor thread has already been started!");
+
+ nsCOMPtr<nsIThread> compositorThread;
+ nsresult rv = NS_NewNamedThread(
+ "Compositor", getter_AddRefs(compositorThread),
+ NS_NewRunnableFunction(
+ "CompositorThreadHolder::CompositorThreadHolderSetup", []() {
+ sBackgroundHangMonitor = new mozilla::BackgroundHangMonitor(
+ "Compositor",
+ /* Timeout values are powers-of-two to enable us get better
+ data. 128ms is chosen for transient hangs because 8Hz should
+ be the minimally acceptable goal for Compositor
+ responsiveness (normal goal is 60Hz). */
+ 128,
+ /* 2048ms is chosen for permanent hangs because it's longer than
+ * most Compositor hangs seen in the wild, but is short enough
+ * to not miss getting native hang stacks. */
+ 2048);
+ nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
+ static_cast<nsThread*>(thread.get())->SetUseHangMonitor(true);
+ }));
+
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ CompositorBridgeParent::Setup();
+ ImageBridgeParent::Setup();
+
+ return compositorThread.forget();
+}
+
+void CompositorThreadHolder::Start() {
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+ MOZ_ASSERT(!sCompositorThreadHolder,
+ "The compositor thread has already been started!");
+
+ // We unset the holder instead of asserting because failing to start the
+ // compositor thread may not be a fatal error. As long as this succeeds in
+ // either the GPU process or the UI process, the user will have a usable
+ // browser. If we get neither, it will crash as soon as we try to post to the
+ // compositor thread for the first time.
+ sCompositorThreadHolder = new CompositorThreadHolder();
+ if (!sCompositorThreadHolder->GetCompositorThread()) {
+ gfxCriticalNote << "Compositor thread not started ("
+ << XRE_IsParentProcess() << ")";
+ sCompositorThreadHolder = nullptr;
+ }
+}
+
+void CompositorThreadHolder::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+ if (!sCompositorThreadHolder) {
+ // We've already shutdown or never started.
+ return;
+ }
+
+ ImageBridgeParent::Shutdown();
+ gfx::VRManagerParent::Shutdown();
+ MediaSystemResourceService::Shutdown();
+ CompositorManagerParent::Shutdown();
+ CanvasTranslator::Shutdown();
+
+ // Ensure there are no pending tasks that would cause an access to the
+ // thread's HangMonitor. APZ and Canvas can keep a reference to the compositor
+ // thread and may continue to dispatch tasks on it as the system shuts down.
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "CompositorThreadHolder::Shutdown",
+ [compositorThreadHolder =
+ RefPtr<CompositorThreadHolder>(sCompositorThreadHolder),
+ backgroundHangMonitor = UniquePtr<mozilla::BackgroundHangMonitor>(
+ sBackgroundHangMonitor)]() {
+ nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
+ static_cast<nsThread*>(thread.get())->SetUseHangMonitor(false);
+ }));
+
+ sCompositorThreadHolder = nullptr;
+ sBackgroundHangMonitor = nullptr;
+
+ SpinEventLoopUntil([&]() {
+ bool finished = sFinishedCompositorShutDown;
+ return finished;
+ });
+
+ // At this point, the CompositorThreadHolder instance will have been
+ // destroyed, but the compositor thread itself may still be running due to
+ // APZ/Canvas code holding a reference to the underlying
+ // nsIThread/nsISerialEventTarget. Any tasks scheduled to run on the
+ // compositor thread earlier in this function will have been run to
+ // completion.
+ CompositorBridgeParent::FinishShutdown();
+}
+
+/* static */
+bool CompositorThreadHolder::IsInCompositorThread() {
+ if (!CompositorThread()) {
+ return false;
+ }
+ bool in = false;
+ MOZ_ALWAYS_SUCCEEDS(CompositorThread()->IsOnCurrentThread(&in));
+ return in;
+}
+
+} // namespace layers
+} // namespace mozilla
+
+bool NS_IsInCompositorThread() {
+ return mozilla::layers::CompositorThreadHolder::IsInCompositorThread();
+}
diff --git a/gfx/layers/ipc/CompositorThread.h b/gfx/layers/ipc/CompositorThread.h
new file mode 100644
index 0000000000..2dea93e9d0
--- /dev/null
+++ b/gfx/layers/ipc/CompositorThread.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_layers_CompositorThread_h
+#define mozilla_layers_CompositorThread_h
+
+#include "nsISupportsImpl.h"
+#include "nsIThread.h"
+
+class nsISerialEventTarget;
+class nsIThread;
+
+namespace mozilla {
+namespace layers {
+
+class CompositorThreadHolder final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
+ CompositorThreadHolder)
+
+ public:
+ CompositorThreadHolder();
+
+ nsISerialEventTarget* GetCompositorThread() const {
+ return mCompositorThread;
+ }
+
+ static CompositorThreadHolder* GetSingleton();
+
+ static bool IsActive() { return !!GetSingleton(); }
+
+ /**
+ * Creates the compositor thread and the global compositor map.
+ */
+ static void Start();
+
+ /*
+ * Waits for all [CrossProcess]CompositorBridgeParents to shutdown and
+ * releases compositor-thread owned resources.
+ */
+ static void Shutdown();
+
+ // Returns true if the calling thread is the compositor thread.
+ static bool IsInCompositorThread();
+
+ private:
+ ~CompositorThreadHolder();
+
+ nsCOMPtr<nsIThread> mCompositorThread;
+
+ static already_AddRefed<nsIThread> CreateCompositorThread();
+
+ friend class CompositorBridgeParent;
+};
+
+nsISerialEventTarget* CompositorThread();
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorThread_h
diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.cpp b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
new file mode 100644
index 0000000000..4662a1e3ae
--- /dev/null
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorVsyncScheduler.h"
+
+#include <stdio.h> // for fprintf, stdout
+#include <stdint.h> // for uint64_t
+#include "base/task.h" // for CancelableTask, etc
+#include "base/thread.h" // for Thread
+#include "gfxPlatform.h" // for gfxPlatform
+#ifdef MOZ_WIDGET_GTK
+# include "gfxPlatformGtk.h" // for gfxPlatform
+#endif
+#include "mozilla/AutoRestore.h" // for AutoRestore
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for IntSize
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorVsyncSchedulerOwner.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/Telemetry.h"
+#include "mozilla/VsyncDispatcher.h"
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+# include "VsyncSource.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#include "VRManager.h"
+
+namespace mozilla {
+
+namespace layers {
+
+using namespace mozilla::gfx;
+
+CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
+ : mMutex("CompositorVsyncScheduler.Observer.Mutex"), mOwner(aOwner) {}
+
+CompositorVsyncScheduler::Observer::~Observer() { MOZ_ASSERT(!mOwner); }
+
+bool CompositorVsyncScheduler::Observer::NotifyVsync(const VsyncEvent& aVsync) {
+ MutexAutoLock lock(mMutex);
+ if (!mOwner) {
+ return false;
+ }
+ return mOwner->NotifyVsync(aVsync);
+}
+
+void CompositorVsyncScheduler::Observer::Destroy() {
+ MutexAutoLock lock(mMutex);
+ mOwner = nullptr;
+}
+
+CompositorVsyncScheduler::CompositorVsyncScheduler(
+ CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner,
+ widget::CompositorWidget* aWidget)
+ : mVsyncSchedulerOwner(aVsyncSchedulerOwner),
+ mLastComposeTime(SampleTime::FromNow()),
+ mLastVsyncTime(TimeStamp::Now()),
+ mLastVsyncOutputTime(TimeStamp::Now()),
+ mIsObservingVsync(false),
+ mVsyncNotificationsSkipped(0),
+ mWidget(aWidget),
+ mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor"),
+ mCurrentCompositeTask(nullptr),
+ mCurrentVRTaskMonitor("CurrentVRTaskMonitor"),
+ mCurrentVRTask(nullptr) {
+ mVsyncObserver = new Observer(this);
+
+ // mAsapScheduling is set on the main thread during init,
+ // but is only accessed after on the compositor thread.
+ mAsapScheduling =
+ StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
+ gfxPlatform::IsInLayoutAsapMode();
+}
+
+CompositorVsyncScheduler::~CompositorVsyncScheduler() {
+ MOZ_ASSERT(!mIsObservingVsync);
+ MOZ_ASSERT(!mVsyncObserver);
+ // The CompositorVsyncDispatcher is cleaned up before this in the
+ // nsBaseWidget, which stops vsync listeners
+ mVsyncSchedulerOwner = nullptr;
+}
+
+void CompositorVsyncScheduler::Destroy() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ if (!mVsyncObserver) {
+ // Destroy was already called on this object.
+ return;
+ }
+ UnobserveVsync();
+ mVsyncObserver->Destroy();
+ mVsyncObserver = nullptr;
+
+ mCompositeRequestedAt = TimeStamp();
+ CancelCurrentCompositeTask();
+ CancelCurrentVRTask();
+}
+
+void CompositorVsyncScheduler::PostCompositeTask(
+ const VsyncEvent& aVsyncEvent) {
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ if (mCurrentCompositeTask == nullptr && CompositorThread()) {
+ RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<VsyncEvent>(
+ "layers::CompositorVsyncScheduler::Composite", this,
+ &CompositorVsyncScheduler::Composite, aVsyncEvent);
+ mCurrentCompositeTask = task;
+ CompositorThread()->Dispatch(task.forget());
+ }
+}
+
+void CompositorVsyncScheduler::PostVRTask(TimeStamp aTimestamp) {
+ MonitorAutoLock lockVR(mCurrentVRTaskMonitor);
+ if (mCurrentVRTask == nullptr && CompositorThread()) {
+ RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>(
+ "layers::CompositorVsyncScheduler::DispatchVREvents", this,
+ &CompositorVsyncScheduler::DispatchVREvents, aTimestamp);
+ mCurrentVRTask = task;
+ CompositorThread()->Dispatch(task.forget());
+ }
+}
+
+void CompositorVsyncScheduler::ScheduleComposition() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mVsyncObserver) {
+ // Destroy was already called on this object.
+ return;
+ }
+
+ // Make a synthetic vsync event for the calls to PostCompositeTask below.
+ TimeStamp vsyncTime = TimeStamp::Now();
+ TimeStamp outputTime = vsyncTime + mVsyncSchedulerOwner->GetVsyncInterval();
+ VsyncEvent vsyncEvent(VsyncId(), vsyncTime, outputTime);
+
+ if (mAsapScheduling) {
+ // Used only for performance testing purposes, and when recording/replaying
+ // to ensure that graphics are up to date.
+ PostCompositeTask(vsyncEvent);
+ } else {
+ if (!mCompositeRequestedAt) {
+ mCompositeRequestedAt = TimeStamp::Now();
+ }
+ if (!mIsObservingVsync && mCompositeRequestedAt) {
+ ObserveVsync();
+ // Starting to observe vsync is an async operation that goes
+ // through the main thread of the UI process. It's possible that
+ // we're blocking there waiting on a composite, so schedule an initial
+ // one now to get things started.
+ PostCompositeTask(vsyncEvent);
+ }
+ }
+}
+
+bool CompositorVsyncScheduler::NotifyVsync(const VsyncEvent& aVsync) {
+ // Called from the vsync dispatch thread. When in the GPU Process, that's
+ // the same as the compositor thread.
+#ifdef DEBUG
+# ifdef MOZ_WAYLAND
+ // On Wayland, we dispatch vsync from the main thread, without a GPU process.
+ // To allow this, we skip the following asserts if we're currently utilizing
+ // the Wayland backend. The IsParentProcess guard is needed to ensure that
+ // we don't accidentally attempt to initialize the gfxPlatform in the GPU
+ // process on X11.
+ if (!XRE_IsParentProcess() ||
+ !gfxPlatformGtk::GetPlatform()->IsWaylandDisplay())
+# endif // MOZ_WAYLAND
+ {
+ MOZ_ASSERT_IF(XRE_IsParentProcess(),
+ !CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(!NS_IsMainThread());
+ }
+
+ MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU,
+ CompositorThreadHolder::IsInCompositorThread());
+#endif // DEBUG
+
+#if defined(MOZ_WIDGET_ANDROID)
+ gfx::VRManager* vm = gfx::VRManager::Get();
+ if (!vm->IsPresenting()) {
+ PostCompositeTask(aVsync);
+ }
+#else
+ PostCompositeTask(aVsync);
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ PostVRTask(aVsync.mTime);
+ return true;
+}
+
+void CompositorVsyncScheduler::CancelCurrentVRTask() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
+ NS_IsMainThread());
+ MonitorAutoLock lock(mCurrentVRTaskMonitor);
+ if (mCurrentVRTask) {
+ mCurrentVRTask->Cancel();
+ mCurrentVRTask = nullptr;
+ }
+}
+
+void CompositorVsyncScheduler::CancelCurrentCompositeTask() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
+ NS_IsMainThread());
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ if (mCurrentCompositeTask) {
+ mCurrentCompositeTask->Cancel();
+ mCurrentCompositeTask = nullptr;
+ }
+}
+
+void CompositorVsyncScheduler::Composite(const VsyncEvent& aVsyncEvent) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(mVsyncSchedulerOwner);
+
+ { // scope lock
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ mCurrentCompositeTask = nullptr;
+ }
+
+ mLastVsyncTime = aVsyncEvent.mTime;
+ mLastVsyncOutputTime = aVsyncEvent.mOutputTime;
+ mLastVsyncId = aVsyncEvent.mId;
+
+ if (!mAsapScheduling) {
+ // Some early exit conditions if we're not in ASAP mode
+ if (aVsyncEvent.mTime < mLastComposeTime.Time()) {
+ // We can sometimes get vsync timestamps that are in the past
+ // compared to the last compose with force composites.
+ // In those cases, wait until the next vsync;
+ return;
+ }
+
+ if (mVsyncSchedulerOwner->IsPendingComposite()) {
+ // If previous composite is still on going, finish it and wait for the
+ // next vsync.
+ mVsyncSchedulerOwner->FinishPendingComposite();
+ return;
+ }
+ }
+
+ if (mCompositeRequestedAt || mAsapScheduling) {
+ mCompositeRequestedAt = TimeStamp();
+ mLastComposeTime = SampleTime::FromVsync(aVsyncEvent.mTime);
+
+ // Tell the owner to do a composite
+ mVsyncSchedulerOwner->CompositeToTarget(aVsyncEvent.mId, nullptr, nullptr);
+
+ mVsyncNotificationsSkipped = 0;
+
+ TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncEvent.mTime;
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME,
+ compositeFrameTotal.ToMilliseconds());
+ } else if (mVsyncNotificationsSkipped++ >
+ StaticPrefs::gfx_vsync_compositor_unobserve_count_AtStartup()) {
+ UnobserveVsync();
+ }
+}
+
+void CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget,
+ const IntRect* aRect) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ /**
+ * bug 1138502 - There are cases such as during long-running window resizing
+ * events where we receive many force-composites. We also continue to get
+ * vsync notifications. Because the force-composites trigger compositing and
+ * clear the mCompositeRequestedAt timestamp, the vsync notifications will not
+ * need to do anything and so will increment the mVsyncNotificationsSkipped
+ * counter to indicate the vsync was ignored. If this happens enough times, we
+ * will disable listening for vsync entirely. On the next force-composite we
+ * will enable listening for vsync again, and continued force-composites and
+ * vsyncs will cause oscillation between observing vsync and not. On some
+ * platforms, enabling/disabling vsync is not free and this oscillating
+ * behavior causes a performance hit. In order to avoid this problem, we reset
+ * the mVsyncNotificationsSkipped counter to keep vsync enabled.
+ */
+ mVsyncNotificationsSkipped = 0;
+
+ mLastComposeTime = SampleTime::FromNow();
+ MOZ_ASSERT(mVsyncSchedulerOwner);
+ mVsyncSchedulerOwner->CompositeToTarget(VsyncId(), aTarget, aRect);
+}
+
+bool CompositorVsyncScheduler::NeedsComposite() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return (bool)mCompositeRequestedAt;
+}
+
+bool CompositorVsyncScheduler::FlushPendingComposite() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mCompositeRequestedAt) {
+ CancelCurrentCompositeTask();
+ ForceComposeToTarget(nullptr, nullptr);
+ return true;
+ }
+ return false;
+}
+
+void CompositorVsyncScheduler::ObserveVsync() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mWidget->ObserveVsync(mVsyncObserver);
+ mIsObservingVsync = true;
+}
+
+void CompositorVsyncScheduler::UnobserveVsync() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mWidget->ObserveVsync(nullptr);
+ mIsObservingVsync = false;
+}
+
+void CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp) {
+ {
+ MonitorAutoLock lock(mCurrentVRTaskMonitor);
+ mCurrentVRTask = nullptr;
+ }
+ // This only allows to be called by CompositorVsyncScheduler::PostVRTask()
+ // When the process is going to shutdown, the runnable has chance to be
+ // executed by other threads, we only want it to be run in the compositor
+ // thread.
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ return;
+ }
+
+ VRManager* vm = VRManager::Get();
+ vm->NotifyVsync(aVsyncTimestamp);
+}
+
+const SampleTime& CompositorVsyncScheduler::GetLastComposeTime() const {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mLastComposeTime;
+}
+
+const TimeStamp& CompositorVsyncScheduler::GetLastVsyncTime() const {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mLastVsyncTime;
+}
+
+const TimeStamp& CompositorVsyncScheduler::GetLastVsyncOutputTime() const {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mLastVsyncOutputTime;
+}
+
+const VsyncId& CompositorVsyncScheduler::GetLastVsyncId() const {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mLastVsyncId;
+}
+
+void CompositorVsyncScheduler::UpdateLastComposeTime() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mLastComposeTime = SampleTime::FromNow();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.h b/gfx/layers/ipc/CompositorVsyncScheduler.h
new file mode 100644
index 0000000000..67e3d73b03
--- /dev/null
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.h
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorVsyncScheduler_h
+#define mozilla_layers_CompositorVsyncScheduler_h
+
+#include <stdint.h> // for uint64_t
+
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Monitor.h" // for Monitor
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/SampleTime.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+class CancelableRunnable;
+class Runnable;
+
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class CompositorVsyncSchedulerOwner;
+
+/**
+ * Manages the vsync (de)registration and tracking on behalf of the
+ * compositor when it need to paint.
+ * Turns vsync notifications into scheduled composites.
+ **/
+class CompositorVsyncScheduler {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler)
+
+ public:
+ CompositorVsyncScheduler(CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner,
+ widget::CompositorWidget* aWidget);
+
+ /**
+ * Notify this class of a vsync. This will trigger a composite if one is
+ * needed. This must be called from the vsync dispatch thread.
+ */
+ bool NotifyVsync(const VsyncEvent& aVsync);
+
+ /**
+ * Do cleanup. This must be called on the compositor thread.
+ */
+ void Destroy();
+
+ /**
+ * Notify this class that a composition is needed. This will trigger a
+ * composition soon (likely at the next vsync). This must be called on the
+ * compositor thread.
+ */
+ void ScheduleComposition();
+
+ /**
+ * Cancel any composite task that has been scheduled but hasn't run yet.
+ */
+ void CancelCurrentCompositeTask();
+
+ /**
+ * Check if a composite is pending. This is generally true between a call
+ * to ScheduleComposition() and the time the composite happens.
+ */
+ bool NeedsComposite();
+
+ /**
+ * Force a composite to happen right away, without waiting for the next vsync.
+ * This must be called on the compositor thread.
+ */
+ void ForceComposeToTarget(gfx::DrawTarget* aTarget,
+ const gfx::IntRect* aRect);
+
+ /**
+ * If a composite is pending, force it to trigger right away. This must be
+ * called on the compositor thread. Returns true if there was a composite
+ * flushed.
+ */
+ bool FlushPendingComposite();
+
+ /**
+ * Return the vsync timestamp of the last or ongoing composite. Must be called
+ * on the compositor thread.
+ */
+ const SampleTime& GetLastComposeTime() const;
+
+ /**
+ * Return the vsync timestamp and id of the most recently received
+ * vsync event. Must be called on the compositor thread.
+ */
+ const TimeStamp& GetLastVsyncTime() const;
+ const TimeStamp& GetLastVsyncOutputTime() const;
+ const VsyncId& GetLastVsyncId() const;
+
+ /**
+ * Update LastCompose TimeStamp to current timestamp.
+ * The function is typically used when composition is handled outside the
+ * CompositorVsyncScheduler.
+ */
+ void UpdateLastComposeTime();
+
+ private:
+ virtual ~CompositorVsyncScheduler();
+
+ // Post a task to run Composite() on the compositor thread, if there isn't
+ // such a task already queued. Can be called from any thread.
+ void PostCompositeTask(const VsyncEvent& aVsyncEvent);
+
+ // Post a task to run DispatchVREvents() on the VR thread, if there isn't
+ // such a task already queued. Can be called from any thread.
+ void PostVRTask(TimeStamp aTimestamp);
+
+ /**
+ * Cancel any VR task that has been scheduled but hasn't run yet.
+ */
+ void CancelCurrentVRTask();
+
+ // This gets run at vsync time and "does" a composite (which really means
+ // update internal state and call the owner to do the composite).
+ void Composite(const VsyncEvent& aVsyncEvent);
+
+ void ObserveVsync();
+ void UnobserveVsync();
+
+ void DispatchVREvents(TimeStamp aVsyncTimestamp);
+
+ class Observer final : public VsyncObserver {
+ public:
+ explicit Observer(CompositorVsyncScheduler* aOwner);
+ bool NotifyVsync(const VsyncEvent& aVsync) override;
+ void Destroy();
+
+ private:
+ virtual ~Observer();
+
+ Mutex mMutex;
+ // Hold raw pointer to avoid mutual reference.
+ CompositorVsyncScheduler* mOwner;
+ };
+
+ CompositorVsyncSchedulerOwner* mVsyncSchedulerOwner;
+ SampleTime mLastComposeTime;
+ TimeStamp mLastVsyncTime;
+ TimeStamp mLastVsyncOutputTime;
+ VsyncId mLastVsyncId;
+
+ bool mAsapScheduling;
+ bool mIsObservingVsync;
+ TimeStamp mCompositeRequestedAt;
+ int32_t mVsyncNotificationsSkipped;
+ widget::CompositorWidget* mWidget;
+ RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver;
+
+ mozilla::Monitor mCurrentCompositeTaskMonitor;
+ RefPtr<CancelableRunnable> mCurrentCompositeTask;
+
+ mozilla::Monitor mCurrentVRTaskMonitor;
+ RefPtr<CancelableRunnable> mCurrentVRTask;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorVsyncScheduler_h
diff --git a/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h b/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h
new file mode 100644
index 0000000000..1691f6535a
--- /dev/null
+++ b/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorVsyncSchedulerOwner_h
+#define mozilla_layers_CompositorVsyncSchedulerOwner_h
+
+#include "mozilla/VsyncDispatcher.h"
+
+namespace mozilla {
+
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class CompositorVsyncSchedulerOwner {
+ public:
+ virtual bool IsPendingComposite() = 0;
+ virtual void FinishPendingComposite() = 0;
+ virtual void CompositeToTarget(VsyncId aId, gfx::DrawTarget* aTarget,
+ const gfx::IntRect* aRect = nullptr) = 0;
+ virtual TimeDuration GetVsyncInterval() const = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorVsyncSchedulerOwner_h
diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
new file mode 100644
index 0000000000..793919e7f3
--- /dev/null
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
@@ -0,0 +1,753 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/ContentCompositorBridgeParent.h"
+
+#include <stdint.h> // for uint64_t
+
+#include "LayerTransactionParent.h" // for LayerTransactionParent
+#include "apz/src/APZCTreeManager.h" // for APZCTreeManager
+#include "gfxUtils.h"
+#ifdef XP_WIN
+# include "mozilla/gfx/DeviceManagerDx.h" // for DeviceManagerDx
+# include "mozilla/layers/ImageDataSerializer.h"
+#endif
+#include "mozilla/D3DMessageUtils.h" // for DxgiAdapterDesc
+#include "mozilla/dom/WebGLParent.h"
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/layers/AnimationHelper.h" // for CompositorAnimationStorage
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZUpdater.h" // for APZUpdater
+#include "mozilla/layers/AsyncCompositionManager.h"
+#include "mozilla/layers/CompositorOptions.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
+#include "mozilla/webgpu/WebGPUParent.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsTArray.h" // for nsTArray
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#include "mozilla/Unused.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#ifdef MOZ_GECKO_PROFILER
+# include "mozilla/BaseProfilerMarkerTypes.h"
+#endif
+
+namespace mozilla {
+
+namespace layers {
+
+// defined in CompositorBridgeParent.cpp
+typedef std::map<LayersId, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
+extern LayerTreeMap sIndirectLayerTrees;
+extern StaticAutoPtr<mozilla::Monitor> sIndirectLayerTreesLock;
+void UpdateIndirectTree(LayersId aId, Layer* aRoot,
+ const TargetConfig& aTargetConfig);
+void EraseLayerState(LayersId aId);
+
+mozilla::ipc::IPCResult
+ContentCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint() {
+ mNotifyAfterRemotePaint = true;
+ return IPC_OK();
+}
+
+void ContentCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+ mCanSend = false;
+
+ // We must keep this object alive untill the code handling message
+ // reception is finished on this thread.
+ GetCurrentSerialEventTarget()->Dispatch(NewRunnableMethod(
+ "layers::ContentCompositorBridgeParent::DeferredDestroy", this,
+ &ContentCompositorBridgeParent::DeferredDestroy));
+}
+
+PLayerTransactionParent*
+ContentCompositorBridgeParent::AllocPLayerTransactionParent(
+ const nsTArray<LayersBackend>&, const LayersId& aId) {
+ MOZ_ASSERT(aId.IsValid());
+
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aId, OtherPid())) {
+ NS_ERROR(
+ "Unexpected layers id in AllocPLayerTransactionParent; dropping "
+ "message...");
+ return nullptr;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+ LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ if (state && state->mLayerManager) {
+ state->mContentCompositorBridgeParent = this;
+ HostLayerManager* lm = state->mLayerManager;
+ CompositorAnimationStorage* animStorage =
+ state->mParent ? state->mParent->GetAnimationStorage() : nullptr;
+ TimeDuration vsyncRate =
+ state->mParent ? state->mParent->GetVsyncInterval() : TimeDuration();
+ LayerTransactionParent* p =
+ new LayerTransactionParent(lm, this, animStorage, aId, vsyncRate);
+ p->AddIPDLReference();
+ sIndirectLayerTrees[aId].mLayerTree = p;
+ return p;
+ }
+
+ NS_WARNING("Created child without a matching parent?");
+ LayerTransactionParent* p = new LayerTransactionParent(
+ /* aManager */ nullptr, this, /* aAnimStorage */ nullptr, aId,
+ TimeDuration());
+ p->AddIPDLReference();
+ return p;
+}
+
+bool ContentCompositorBridgeParent::DeallocPLayerTransactionParent(
+ PLayerTransactionParent* aLayers) {
+ LayerTransactionParent* slp = static_cast<LayerTransactionParent*>(aLayers);
+ EraseLayerState(slp->GetId());
+ static_cast<LayerTransactionParent*>(aLayers)->ReleaseIPDLReference();
+ return true;
+}
+
+PAPZCTreeManagerParent*
+ContentCompositorBridgeParent::AllocPAPZCTreeManagerParent(
+ const LayersId& aLayersId) {
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) {
+ NS_ERROR(
+ "Unexpected layers id in AllocPAPZCTreeManagerParent; dropping "
+ "message...");
+ return nullptr;
+ }
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state =
+ sIndirectLayerTrees[aLayersId];
+
+ // If the widget has shutdown its compositor, we may not have had a chance yet
+ // to unmap our layers id, and we could get here without a parent compositor.
+ // In this case return an empty APZCTM.
+ if (!state.mParent) {
+ // Note: we immediately call ClearTree since otherwise the APZCTM will
+ // retain a reference to itself, through the checkerboard observer.
+ LayersId dummyId{0};
+ const bool useWebRender = false;
+ RefPtr<APZCTreeManager> temp = new APZCTreeManager(dummyId, useWebRender);
+ RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp, useWebRender);
+ tempUpdater->ClearTree(dummyId);
+ return new APZCTreeManagerParent(aLayersId, temp, tempUpdater);
+ }
+
+ state.mParent->AllocateAPZCTreeManagerParent(lock, aLayersId, state);
+ return state.mApzcTreeManagerParent;
+}
+bool ContentCompositorBridgeParent::DeallocPAPZCTreeManagerParent(
+ PAPZCTreeManagerParent* aActor) {
+ APZCTreeManagerParent* parent = static_cast<APZCTreeManagerParent*>(aActor);
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ auto iter = sIndirectLayerTrees.find(parent->GetLayersId());
+ if (iter != sIndirectLayerTrees.end()) {
+ CompositorBridgeParent::LayerTreeState& state = iter->second;
+ MOZ_ASSERT(state.mApzcTreeManagerParent == parent);
+ state.mApzcTreeManagerParent = nullptr;
+ }
+
+ delete parent;
+
+ return true;
+}
+
+PAPZParent* ContentCompositorBridgeParent::AllocPAPZParent(
+ const LayersId& aLayersId) {
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) {
+ NS_ERROR("Unexpected layers id in AllocPAPZParent; dropping message...");
+ return nullptr;
+ }
+
+ RemoteContentController* controller = new RemoteContentController();
+
+ // Increment the controller's refcount before we return it. This will keep the
+ // controller alive until it is released by IPDL in DeallocPAPZParent.
+ controller->AddRef();
+
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state =
+ sIndirectLayerTrees[aLayersId];
+ MOZ_ASSERT(!state.mController);
+ state.mController = controller;
+
+ return controller;
+}
+
+bool ContentCompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor) {
+ RemoteContentController* controller =
+ static_cast<RemoteContentController*>(aActor);
+ controller->Release();
+ return true;
+}
+
+PWebRenderBridgeParent*
+ContentCompositorBridgeParent::AllocPWebRenderBridgeParent(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize,
+ const WindowKind& aWindowKind) {
+ LayersId layersId = wr::AsLayersId(aPipelineId);
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(layersId, OtherPid())) {
+ NS_ERROR(
+ "Unexpected layers id in AllocPWebRenderBridgeParent; dropping "
+ "message...");
+ return nullptr;
+ }
+
+ RefPtr<CompositorBridgeParent> cbp = nullptr;
+ RefPtr<WebRenderBridgeParent> root = nullptr;
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ MOZ_ASSERT(sIndirectLayerTrees.find(layersId) != sIndirectLayerTrees.end());
+ MOZ_ASSERT(sIndirectLayerTrees[layersId].mWrBridge == nullptr);
+ cbp = sIndirectLayerTrees[layersId].mParent;
+ if (cbp) {
+ root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge;
+ }
+ }
+
+ RefPtr<wr::WebRenderAPI> api;
+ if (root) {
+ api = root->GetWebRenderAPI();
+ }
+
+ if (!root || !api) {
+ // This could happen when this function is called after
+ // CompositorBridgeParent destruction. This was observed during Tab move
+ // between different windows.
+ NS_WARNING(
+ nsPrintfCString("Created child without a matching parent? root %p",
+ root.get())
+ .get());
+ nsCString error("NO_PARENT");
+ WebRenderBridgeParent* parent =
+ WebRenderBridgeParent::CreateDestroyed(aPipelineId, std::move(error));
+ parent->AddRef(); // IPDL reference
+ return parent;
+ }
+
+ api = api->Clone();
+ RefPtr<AsyncImagePipelineManager> holder = root->AsyncImageManager();
+ WebRenderBridgeParent* parent = new WebRenderBridgeParent(
+ this, aPipelineId, nullptr, root->CompositorScheduler(), std::move(api),
+ std::move(holder), cbp->GetVsyncInterval());
+ parent->AddRef(); // IPDL reference
+
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ sIndirectLayerTrees[layersId].mContentCompositorBridgeParent = this;
+ sIndirectLayerTrees[layersId].mWrBridge = parent;
+ }
+
+ return parent;
+}
+
+bool ContentCompositorBridgeParent::DeallocPWebRenderBridgeParent(
+ PWebRenderBridgeParent* aActor) {
+ WebRenderBridgeParent* parent = static_cast<WebRenderBridgeParent*>(aActor);
+ EraseLayerState(wr::AsLayersId(parent->PipelineId()));
+ parent->Release(); // IPDL reference
+ return true;
+}
+
+webgpu::PWebGPUParent* ContentCompositorBridgeParent::AllocPWebGPUParent() {
+ webgpu::WebGPUParent* parent = new webgpu::WebGPUParent();
+ parent->AddRef(); // IPDL reference
+ return parent;
+}
+
+bool ContentCompositorBridgeParent::DeallocPWebGPUParent(
+ webgpu::PWebGPUParent* aActor) {
+ webgpu::WebGPUParent* parent = static_cast<webgpu::WebGPUParent*>(aActor);
+ parent->Release(); // IPDL reference
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvNotifyChildCreated(
+ const LayersId& child, CompositorOptions* aOptions) {
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
+ it != sIndirectLayerTrees.end(); it++) {
+ CompositorBridgeParent::LayerTreeState* lts = &it->second;
+ if (lts->mParent && lts->mContentCompositorBridgeParent == this) {
+ lts->mParent->NotifyChildCreated(child);
+ *aOptions = lts->mParent->GetOptions();
+ return IPC_OK();
+ }
+ }
+ return IPC_FAIL_NO_REASON(this);
+}
+
+mozilla::ipc::IPCResult
+ContentCompositorBridgeParent::RecvMapAndNotifyChildCreated(
+ const LayersId& child, const base::ProcessId& pid,
+ CompositorOptions* aOptions) {
+ // This can only be called from the browser process, as the mapping
+ // ensures proper window ownership of layer trees.
+ return IPC_FAIL_NO_REASON(this);
+}
+
+mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvCheckContentOnlyTDR(
+ const uint32_t& sequenceNum, bool* isContentOnlyTDR) {
+ *isContentOnlyTDR = false;
+#ifdef XP_WIN
+ gfx::ContentDeviceData compositor;
+
+ gfx::DeviceManagerDx* dm = gfx::DeviceManagerDx::Get();
+
+ // Check that the D3D11 device sequence numbers match.
+ gfx::D3D11DeviceStatus status;
+ dm->ExportDeviceInfo(&status);
+
+ if (sequenceNum == status.sequenceNumber() && !dm->HasDeviceReset()) {
+ *isContentOnlyTDR = true;
+ }
+
+#endif
+ return IPC_OK();
+};
+
+void ContentCompositorBridgeParent::ShadowLayersUpdated(
+ LayerTransactionParent* aLayerTree, const TransactionInfo& aInfo,
+ bool aHitTestUpdate) {
+ LayersId id = aLayerTree->GetId();
+
+ MOZ_ASSERT(id.IsValid());
+
+ CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+ MOZ_ASSERT(state->mParent);
+ state->mParent->ScheduleRotationOnCompositorThread(aInfo.targetConfig(),
+ aInfo.isFirstPaint());
+
+ Layer* shadowRoot = aLayerTree->GetRoot();
+ if (shadowRoot) {
+ CompositorBridgeParent::SetShadowProperties(shadowRoot);
+ }
+ UpdateIndirectTree(id, shadowRoot, aInfo.targetConfig());
+
+ // Cache the plugin data for this remote layer tree
+ state->mPluginData = aInfo.plugins().Clone();
+ state->mUpdatedPluginDataAvailable = true;
+
+ state->mParent->NotifyShadowTreeTransaction(
+ id, aInfo.isFirstPaint(), aInfo.focusTarget(), aInfo.scheduleComposite(),
+ aInfo.paintSequenceNumber(), aInfo.isRepeatTransaction(), aHitTestUpdate);
+
+ // Send the 'remote paint ready' message to the content thread if it has
+ // already asked.
+ if (mNotifyAfterRemotePaint) {
+ Unused << SendRemotePaintIsReady();
+ mNotifyAfterRemotePaint = false;
+ }
+
+ if (aLayerTree->ShouldParentObserveEpoch()) {
+ // Note that we send this through the window compositor, since this needs
+ // to reach the widget owning the tab.
+ Unused << state->mParent->SendObserveLayersUpdate(
+ id, aLayerTree->GetChildEpoch(), true);
+ }
+
+ auto endTime = TimeStamp::Now();
+#ifdef MOZ_GECKO_PROFILER
+ if (profiler_can_accept_markers()) {
+ profiler_add_marker(
+ "CONTENT_FULL_PAINT_TIME", geckoprofiler::category::GRAPHICS,
+ MarkerTiming::Interval(aInfo.transactionStart(), endTime),
+ baseprofiler::markers::ContentBuildMarker{});
+ }
+#endif
+ Telemetry::Accumulate(
+ Telemetry::CONTENT_FULL_PAINT_TIME,
+ static_cast<uint32_t>(
+ (endTime - aInfo.transactionStart()).ToMilliseconds()));
+
+ RegisterPayloads(aLayerTree, aInfo.payload());
+
+ aLayerTree->SetPendingTransactionId(
+ aInfo.id(), aInfo.vsyncId(), aInfo.vsyncStart(), aInfo.refreshStart(),
+ aInfo.transactionStart(), endTime, aInfo.containsSVG(), aInfo.url(),
+ aInfo.fwdTime());
+}
+
+void ContentCompositorBridgeParent::DidCompositeLocked(
+ LayersId aId, const VsyncId& aVsyncId, TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd) {
+ sIndirectLayerTreesLock->AssertCurrentThreadOwns();
+ if (LayerTransactionParent* layerTree = sIndirectLayerTrees[aId].mLayerTree) {
+ TransactionId transactionId =
+ layerTree->FlushTransactionId(aVsyncId, aCompositeEnd);
+ if (transactionId.IsValid()) {
+ Unused << SendDidComposite(aId, transactionId, aCompositeStart,
+ aCompositeEnd);
+ }
+ } else if (sIndirectLayerTrees[aId].mWrBridge) {
+ MOZ_ASSERT(false); // this should never get called for a WR compositor
+ }
+}
+
+void ContentCompositorBridgeParent::ScheduleComposite(
+ LayerTransactionParent* aLayerTree) {
+ LayersId id = aLayerTree->GetId();
+ MOZ_ASSERT(id.IsValid());
+ CompositorBridgeParent* parent;
+ { // scope lock
+ MonitorAutoLock lock(*sIndirectLayerTreesLock);
+ parent = sIndirectLayerTrees[id].mParent;
+ }
+ if (parent) {
+ parent->ScheduleComposite(aLayerTree);
+ }
+}
+
+void ContentCompositorBridgeParent::NotifyClearCachedResources(
+ LayerTransactionParent* aLayerTree) {
+ LayersId id = aLayerTree->GetId();
+ MOZ_ASSERT(id.IsValid());
+
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (state && state->mParent) {
+ // Note that we send this through the window compositor, since this needs
+ // to reach the widget owning the tab.
+ Unused << state->mParent->SendObserveLayersUpdate(
+ id, aLayerTree->GetChildEpoch(), false);
+ }
+}
+
+bool ContentCompositorBridgeParent::SetTestSampleTime(const LayersId& aId,
+ const TimeStamp& aTime) {
+ MOZ_ASSERT(aId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aId);
+ if (!state) {
+ return false;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ return state->mParent->SetTestSampleTime(aId, aTime);
+}
+
+void ContentCompositorBridgeParent::LeaveTestMode(const LayersId& aId) {
+ MOZ_ASSERT(aId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aId);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->LeaveTestMode(aId);
+}
+
+void ContentCompositorBridgeParent::ApplyAsyncProperties(
+ LayerTransactionParent* aLayerTree, TransformsToSkip aSkip) {
+ LayersId id = aLayerTree->GetId();
+ MOZ_ASSERT(id.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->ApplyAsyncProperties(aLayerTree, aSkip);
+}
+
+void ContentCompositorBridgeParent::SetTestAsyncScrollOffset(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
+}
+
+void ContentCompositorBridgeParent::SetTestAsyncZoom(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
+}
+
+void ContentCompositorBridgeParent::FlushApzRepaints(
+ const LayersId& aLayersId) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->FlushApzRepaints(aLayersId);
+}
+
+void ContentCompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->GetAPZTestData(aLayersId, aOutData);
+}
+
+void ContentCompositorBridgeParent::GetFrameUniformity(
+ const LayersId& aLayersId, FrameUniformityData* aOutData) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->GetFrameUniformity(aLayersId, aOutData);
+}
+
+void ContentCompositorBridgeParent::SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->SetConfirmedTargetAPZC(aLayersId, aInputBlockId,
+ std::move(aTargets));
+}
+
+AsyncCompositionManager* ContentCompositorBridgeParent::GetCompositionManager(
+ LayerTransactionParent* aLayerTree) {
+ LayersId id = aLayerTree->GetId();
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ return state->mParent->GetCompositionManager(aLayerTree);
+}
+
+void ContentCompositorBridgeParent::DeferredDestroy() { mSelfRef = nullptr; }
+
+ContentCompositorBridgeParent::~ContentCompositorBridgeParent() {
+ MOZ_ASSERT(XRE_GetIOMessageLoop());
+}
+
+PTextureParent* ContentCompositorBridgeParent::AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+
+ LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
+ if (sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ TextureFlags flags = aFlags;
+
+ LayersBackend actualBackend = LayersBackend::LAYERS_NONE;
+ if (state && state->mLayerManager) {
+ actualBackend = state->mLayerManager->GetBackendType();
+ }
+
+ if (!state) {
+ // The compositor was recreated, and we're receiving layers updates for a
+ // a layer manager that will soon be discarded or invalidated. We can't
+ // return null because this will mess up deserialization later and we'll
+ // kill the content process. Instead, we signal that the underlying
+ // TextureHost should not attempt to access the compositor.
+ flags |= TextureFlags::INVALID_COMPOSITOR;
+ } else if (actualBackend != LayersBackend::LAYERS_NONE &&
+ aLayersBackend != actualBackend) {
+ gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch)
+ << "Texture backend is wrong";
+ }
+
+ return TextureHost::CreateIPDLActor(this, aSharedData, aReadLock,
+ aLayersBackend, aFlags, aSerial,
+ aExternalImageId);
+}
+
+bool ContentCompositorBridgeParent::DeallocPTextureParent(
+ PTextureParent* actor) {
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvInitPCanvasParent(
+ Endpoint<PCanvasParent>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(!mCanvasTranslator,
+ "mCanvasTranslator must be released before recreating.");
+
+ mCanvasTranslator = CanvasTranslator::Create(std::move(aEndpoint));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentCompositorBridgeParent::RecvReleasePCanvasParent() {
+ MOZ_RELEASE_ASSERT(mCanvasTranslator,
+ "mCanvasTranslator hasn't been created.");
+
+ mCanvasTranslator = nullptr;
+ return IPC_OK();
+}
+
+UniquePtr<SurfaceDescriptor>
+ContentCompositorBridgeParent::LookupSurfaceDescriptorForClientTexture(
+ const int64_t aTextureId) {
+ return mCanvasTranslator->WaitForSurfaceDescriptor(aTextureId);
+}
+
+bool ContentCompositorBridgeParent::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void ContentCompositorBridgeParent::UpdatePaintTime(
+ LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) {
+ LayersId id = aLayerTree->GetId();
+ MOZ_ASSERT(id.IsValid());
+
+ CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->UpdatePaintTime(aLayerTree, aPaintTime);
+}
+
+void ContentCompositorBridgeParent::RegisterPayloads(
+ LayerTransactionParent* aLayerTree,
+ const nsTArray<CompositionPayload>& aPayload) {
+ LayersId id = aLayerTree->GetId();
+ MOZ_ASSERT(id.IsValid());
+
+ CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(id);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->RegisterPayloads(aLayerTree, aPayload);
+}
+
+void ContentCompositorBridgeParent::ObserveLayersUpdate(
+ LayersId aLayersId, LayersObserverEpoch aEpoch, bool aActive) {
+ MOZ_ASSERT(aLayersId.IsValid());
+
+ CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ Unused << state->mParent->SendObserveLayersUpdate(aLayersId, aEpoch, aActive);
+}
+
+static inline bool AllowDirectDXGISurfaceDrawing() {
+ if (!StaticPrefs::dom_ipc_plugins_asyncdrawing_enabled()) {
+ return false;
+ }
+#if defined(XP_WIN)
+ gfx::DeviceManagerDx* dm = gfx::DeviceManagerDx::Get();
+ MOZ_ASSERT(dm);
+ if (!dm || !dm->GetCompositorDevice() || !dm->TextureSharingWorks()) {
+ return false;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
+mozilla::ipc::IPCResult
+ContentCompositorBridgeParent::RecvSupportsAsyncDXGISurface(bool* value) {
+ *value = AllowDirectDXGISurfaceDrawing();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvPreferredDXGIAdapter(
+ DxgiAdapterDesc* aOutDesc) {
+ PodZero(aOutDesc);
+#ifdef XP_WIN
+ if (!AllowDirectDXGISurfaceDrawing()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ RefPtr<ID3D11Device> device =
+ gfx::DeviceManagerDx::Get()->GetCompositorDevice();
+ if (!device) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ RefPtr<IDXGIDevice> dxgi;
+ if (FAILED(device->QueryInterface(__uuidof(IDXGIDevice),
+ getter_AddRefs(dxgi))) ||
+ !dxgi) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ RefPtr<IDXGIAdapter> adapter;
+ if (FAILED(dxgi->GetAdapter(getter_AddRefs(adapter))) || !adapter) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ DXGI_ADAPTER_DESC desc;
+ if (FAILED(adapter->GetDesc(&desc))) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ *aOutDesc = DxgiAdapterDesc::From(desc);
+#endif
+ return IPC_OK();
+}
+
+already_AddRefed<dom::PWebGLParent>
+ContentCompositorBridgeParent::AllocPWebGLParent() {
+ RefPtr<dom::PWebGLParent> parent = new dom::WebGLParent();
+ return parent.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.h b/gfx/layers/ipc/ContentCompositorBridgeParent.h
new file mode 100644
index 0000000000..2d50894eb3
--- /dev/null
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.h
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ContentCompositorBridgeParent_h
+#define mozilla_layers_ContentCompositorBridgeParent_h
+
+#include "mozilla/layers/CanvasTranslator.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace webgpu {
+class PWebGPUParent;
+} // namespace webgpu
+
+namespace layers {
+
+class CompositorOptions;
+
+/**
+ * This class handles layer updates pushed directly from child processes to
+ * the compositor thread. It's associated with a CompositorBridgeParent on the
+ * compositor thread. While it uses the PCompositorBridge protocol to manage
+ * these updates, it doesn't actually drive compositing itself. For that it
+ * hands off work to the CompositorBridgeParent it's associated with.
+ */
+class ContentCompositorBridgeParent final : public CompositorBridgeParentBase {
+ friend class CompositorBridgeParent;
+
+ public:
+ explicit ContentCompositorBridgeParent(CompositorManagerParent* aManager)
+ : CompositorBridgeParentBase(aManager),
+ mNotifyAfterRemotePaint(false),
+ mDestroyCalled(false) {}
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // FIXME/bug 774388: work out what shutdown protocol we need.
+ mozilla::ipc::IPCResult RecvInitialize(
+ const LayersId& aRootLayerTreeId) override {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mozilla::ipc::IPCResult RecvWillClose() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvPause() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvRequestFxrOutput() override {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mozilla::ipc::IPCResult RecvResume() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvResumeAsync() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvNotifyChildCreated(
+ const LayersId& child, CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvMapAndNotifyChildCreated(
+ const LayersId& child, const base::ProcessId& pid,
+ CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvNotifyChildRecreated(
+ const LayersId& child, CompositorOptions* aOptions) override {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& child) override {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mozilla::ipc::IPCResult RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
+ const gfx::IntRect& aRect) override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvFlushRendering() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvFlushRenderingAsync() override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvForcePresent() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvNotifyRegionInvalidated(
+ const nsIntRegion& aRegion) override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvStartFrameTimeRecording(
+ const int32_t& aBufferSize, uint32_t* aOutStartIndex) override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvStopFrameTimeRecording(
+ const uint32_t& aStartIndex, nsTArray<float>* intervals) override {
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult RecvCheckContentOnlyTDR(
+ const uint32_t& sequenceNum, bool* isContentOnlyTDR) override;
+
+ mozilla::ipc::IPCResult RecvAllPluginsCaptured() override { return IPC_OK(); }
+
+ mozilla::ipc::IPCResult RecvBeginRecording(
+ const TimeStamp& aRecordingStart,
+ BeginRecordingResolver&& aResolve) override {
+ aResolve(false);
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult RecvEndRecordingToDisk(
+ EndRecordingToDiskResolver&& aResolve) override {
+ aResolve(false);
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult RecvEndRecordingToMemory(
+ EndRecordingToMemoryResolver&& aResolve) override {
+ aResolve(Nothing());
+ return IPC_OK();
+ }
+
+ /**
+ * Tells this CompositorBridgeParent to send a message when the compositor has
+ * received the transaction.
+ */
+ mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() override;
+
+ PLayerTransactionParent* AllocPLayerTransactionParent(
+ const nsTArray<LayersBackend>& aBackendHints,
+ const LayersId& aId) override;
+
+ bool DeallocPLayerTransactionParent(
+ PLayerTransactionParent* aLayers) override;
+
+ void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
+ const TransactionInfo& aInfo,
+ bool aHitTestUpdate) override;
+ void ScheduleComposite(LayerTransactionParent* aLayerTree) override;
+ void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) override;
+ bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override;
+ void LeaveTestMode(const LayersId& aId) override;
+ void ApplyAsyncProperties(LayerTransactionParent* aLayerTree,
+ TransformsToSkip aSkip) override;
+ void SetTestAsyncScrollOffset(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) override;
+ void SetTestAsyncZoom(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) override;
+ void FlushApzRepaints(const LayersId& aLayersId) override;
+ void GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) override;
+ void GetFrameUniformity(const LayersId& aLayersId,
+ FrameUniformityData* aOutData) override;
+ void SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) override;
+
+ AsyncCompositionManager* GetCompositionManager(
+ LayerTransactionParent* aParent) override;
+ mozilla::ipc::IPCResult RecvRemotePluginsReady() override {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ already_AddRefed<dom::PWebGLParent> AllocPWebGLParent() override;
+
+ // Use DidCompositeLocked if you already hold a lock on
+ // sIndirectLayerTreesLock; Otherwise use DidComposite, which would request
+ // the lock automatically.
+ void DidCompositeLocked(LayersId aId, const VsyncId& aVsyncId,
+ TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
+
+ PTextureParent* AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) override;
+
+ bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ mozilla::ipc::IPCResult RecvInitPCanvasParent(
+ Endpoint<PCanvasParent>&& aEndpoint) final;
+
+ mozilla::ipc::IPCResult RecvReleasePCanvasParent() final;
+
+ bool IsSameProcess() const override;
+
+ PCompositorWidgetParent* AllocPCompositorWidgetParent(
+ const CompositorWidgetInitData& aInitData) override {
+ // Not allowed.
+ return nullptr;
+ }
+ bool DeallocPCompositorWidgetParent(
+ PCompositorWidgetParent* aActor) override {
+ // Not allowed.
+ return false;
+ }
+
+ PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(
+ const LayersId& aLayersId) override;
+ bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
+
+ PAPZParent* AllocPAPZParent(const LayersId& aLayersId) override;
+ bool DeallocPAPZParent(PAPZParent* aActor) override;
+
+ void UpdatePaintTime(LayerTransactionParent* aLayerTree,
+ const TimeDuration& aPaintTime) override;
+ void RegisterPayloads(LayerTransactionParent* aLayerTree,
+ const nsTArray<CompositionPayload>& aPayload) override;
+
+ PWebRenderBridgeParent* AllocPWebRenderBridgeParent(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize,
+ const WindowKind& aWindowKind) override;
+ bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
+
+ webgpu::PWebGPUParent* AllocPWebGPUParent() override;
+ bool DeallocPWebGPUParent(webgpu::PWebGPUParent* aActor) override;
+
+ void ObserveLayersUpdate(LayersId aLayersId, LayersObserverEpoch aEpoch,
+ bool aActive) override;
+
+ bool IsRemote() const override { return true; }
+
+ UniquePtr<SurfaceDescriptor> LookupSurfaceDescriptorForClientTexture(
+ const int64_t aTextureId) final;
+
+ mozilla::ipc::IPCResult RecvSupportsAsyncDXGISurface(bool* value) override;
+ mozilla::ipc::IPCResult RecvPreferredDXGIAdapter(
+ DxgiAdapterDesc* desc) override;
+
+ private:
+ // Private destructor, to discourage deletion outside of Release():
+ virtual ~ContentCompositorBridgeParent();
+
+ void DeferredDestroy();
+
+ // There can be many CPCPs, and IPDL-generated code doesn't hold a
+ // reference to top-level actors. So we hold a reference to
+ // ourself. This is released (deferred) in ActorDestroy().
+ RefPtr<ContentCompositorBridgeParent> mSelfRef;
+
+ // If true, we should send a RemotePaintIsReady message when the layer
+ // transaction is received
+ bool mNotifyAfterRemotePaint;
+ bool mDestroyCalled;
+
+ RefPtr<CanvasTranslator> mCanvasTranslator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_ContentCompositorBridgeParent_h
diff --git a/gfx/layers/ipc/ISurfaceAllocator.cpp b/gfx/layers/ipc/ISurfaceAllocator.cpp
new file mode 100644
index 0000000000..bf38b22226
--- /dev/null
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ISurfaceAllocator.h"
+
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/CompositableForwarder.h"
+
+namespace mozilla {
+namespace layers {
+
+NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter)
+
+mozilla::Atomic<ptrdiff_t> GfxMemoryImageReporter::sAmount(0);
+
+/* static */
+uint32_t CompositableForwarder::GetMaxFileDescriptorsPerMessage() {
+#if defined(OS_POSIX)
+ static const uint32_t kMaxFileDescriptors =
+ FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE;
+#else
+ // default number that works everywhere else
+ static const uint32_t kMaxFileDescriptors = 250;
+#endif
+ return kMaxFileDescriptors;
+}
+
+mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType() {
+ return ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
+}
+
+void HostIPCAllocator::SendPendingAsyncMessages() {
+ if (mPendingAsyncMessage.empty()) {
+ return;
+ }
+
+ // Some type of AsyncParentMessageData message could have
+ // one file descriptor (e.g. OpDeliverFence).
+ // A number of file descriptors per gecko ipc message have a limitation
+ // on OS_POSIX (MACOSX or LINUX).
+#if defined(OS_POSIX)
+ static const uint32_t kMaxMessageNumber =
+ FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE;
+#else
+ // default number that works everywhere else
+ static const uint32_t kMaxMessageNumber = 250;
+#endif
+
+ nsTArray<AsyncParentMessageData> messages;
+ messages.SetCapacity(mPendingAsyncMessage.size());
+ for (size_t i = 0; i < mPendingAsyncMessage.size(); i++) {
+ messages.AppendElement(mPendingAsyncMessage[i]);
+ // Limit maximum number of messages.
+ if (messages.Length() >= kMaxMessageNumber) {
+ SendAsyncMessage(messages);
+ // Initialize Messages.
+ messages.Clear();
+ }
+ }
+
+ if (messages.Length() > 0) {
+ SendAsyncMessage(messages);
+ }
+ mPendingAsyncMessage.clear();
+}
+
+// XXX - We should actually figure out the minimum shmem allocation size on
+// a certain platform and use that.
+const uint32_t sShmemPageSize = 4096;
+
+#ifdef DEBUG
+const uint32_t sSupportedBlockSize = 4;
+#endif
+
+FixedSizeSmallShmemSectionAllocator::FixedSizeSmallShmemSectionAllocator(
+ LayersIPCChannel* aShmProvider)
+ : mShmProvider(aShmProvider) {
+ MOZ_ASSERT(mShmProvider);
+}
+
+FixedSizeSmallShmemSectionAllocator::~FixedSizeSmallShmemSectionAllocator() {
+ ShrinkShmemSectionHeap();
+}
+
+bool FixedSizeSmallShmemSectionAllocator::IPCOpen() const {
+ return mShmProvider->IPCOpen();
+}
+
+bool FixedSizeSmallShmemSectionAllocator::AllocShmemSection(
+ uint32_t aSize, ShmemSection* aShmemSection) {
+ // For now we only support sizes of 4. If we want to support different sizes
+ // some more complicated bookkeeping should be added.
+ MOZ_ASSERT(aSize == sSupportedBlockSize);
+ MOZ_ASSERT(aShmemSection);
+
+ if (!IPCOpen()) {
+ gfxCriticalError() << "Attempt to allocate a ShmemSection after shutdown.";
+ return false;
+ }
+
+ uint32_t allocationSize = (aSize + sizeof(ShmemSectionHeapAllocation));
+
+ for (size_t i = 0; i < mUsedShmems.size(); i++) {
+ ShmemSectionHeapHeader* header =
+ mUsedShmems[i].get<ShmemSectionHeapHeader>();
+ if ((header->mAllocatedBlocks + 1) * allocationSize +
+ sizeof(ShmemSectionHeapHeader) <
+ sShmemPageSize) {
+ aShmemSection->shmem() = mUsedShmems[i];
+ MOZ_ASSERT(mUsedShmems[i].IsWritable());
+ break;
+ }
+ }
+
+ if (!aShmemSection->shmem().IsWritable()) {
+ ipc::Shmem tmp;
+ if (!mShmProvider->AllocUnsafeShmem(sShmemPageSize, OptimalShmemType(),
+ &tmp)) {
+ return false;
+ }
+
+ ShmemSectionHeapHeader* header = tmp.get<ShmemSectionHeapHeader>();
+ header->mTotalBlocks = 0;
+ header->mAllocatedBlocks = 0;
+
+ mUsedShmems.push_back(tmp);
+ aShmemSection->shmem() = tmp;
+ }
+
+ MOZ_ASSERT(aShmemSection->shmem().IsWritable());
+
+ ShmemSectionHeapHeader* header =
+ aShmemSection->shmem().get<ShmemSectionHeapHeader>();
+ uint8_t* heap =
+ aShmemSection->shmem().get<uint8_t>() + sizeof(ShmemSectionHeapHeader);
+
+ ShmemSectionHeapAllocation* allocHeader = nullptr;
+
+ if (header->mTotalBlocks > header->mAllocatedBlocks) {
+ // Search for the first available block.
+ for (size_t i = 0; i < header->mTotalBlocks; i++) {
+ allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+
+ if (allocHeader->mStatus == STATUS_FREED) {
+ break;
+ }
+ heap += allocationSize;
+ }
+ MOZ_ASSERT(allocHeader && allocHeader->mStatus == STATUS_FREED);
+ MOZ_ASSERT(allocHeader->mSize == sSupportedBlockSize);
+ } else {
+ heap += header->mTotalBlocks * allocationSize;
+
+ header->mTotalBlocks++;
+ allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+ allocHeader->mSize = aSize;
+ }
+
+ MOZ_ASSERT(allocHeader);
+ header->mAllocatedBlocks++;
+ allocHeader->mStatus = STATUS_ALLOCATED;
+
+ aShmemSection->size() = aSize;
+ aShmemSection->offset() = (heap + sizeof(ShmemSectionHeapAllocation)) -
+ aShmemSection->shmem().get<uint8_t>();
+ ShrinkShmemSectionHeap();
+ return true;
+}
+
+void FixedSizeSmallShmemSectionAllocator::FreeShmemSection(
+ mozilla::layers::ShmemSection& aShmemSection) {
+ MOZ_ASSERT(aShmemSection.size() == sSupportedBlockSize);
+ MOZ_ASSERT(aShmemSection.offset() < sShmemPageSize - sSupportedBlockSize);
+
+ if (!aShmemSection.shmem().IsWritable()) {
+ return;
+ }
+
+ ShmemSectionHeapAllocation* allocHeader =
+ reinterpret_cast<ShmemSectionHeapAllocation*>(
+ aShmemSection.shmem().get<char>() + aShmemSection.offset() -
+ sizeof(ShmemSectionHeapAllocation));
+
+ MOZ_ASSERT(allocHeader->mSize == aShmemSection.size());
+
+ DebugOnly<bool> success =
+ allocHeader->mStatus.compareExchange(STATUS_ALLOCATED, STATUS_FREED);
+ // If this fails something really weird is going on.
+ MOZ_ASSERT(success);
+
+ ShmemSectionHeapHeader* header =
+ aShmemSection.shmem().get<ShmemSectionHeapHeader>();
+ header->mAllocatedBlocks--;
+}
+
+void FixedSizeSmallShmemSectionAllocator::DeallocShmemSection(
+ mozilla::layers::ShmemSection& aShmemSection) {
+ if (!IPCOpen()) {
+ gfxCriticalNote << "Attempt to dealloc a ShmemSections after shutdown.";
+ return;
+ }
+
+ FreeShmemSection(aShmemSection);
+ ShrinkShmemSectionHeap();
+}
+
+void FixedSizeSmallShmemSectionAllocator::ShrinkShmemSectionHeap() {
+ if (!IPCOpen()) {
+ mUsedShmems.clear();
+ return;
+ }
+
+ // The loop will terminate as we either increase i, or decrease size
+ // every time through.
+ size_t i = 0;
+ while (i < mUsedShmems.size()) {
+ ShmemSectionHeapHeader* header =
+ mUsedShmems[i].get<ShmemSectionHeapHeader>();
+ if (header->mAllocatedBlocks == 0) {
+ mShmProvider->DeallocShmem(mUsedShmems[i]);
+ // We don't particularly care about order, move the last one in the array
+ // to this position.
+ if (i < mUsedShmems.size() - 1) {
+ mUsedShmems[i] = mUsedShmems[mUsedShmems.size() - 1];
+ }
+ mUsedShmems.pop_back();
+ } else {
+ i++;
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ISurfaceAllocator.h b/gfx/layers/ipc/ISurfaceAllocator.h
new file mode 100644
index 0000000000..740c3b2b3e
--- /dev/null
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_LAYERS_ISURFACEDEALLOCATOR
+#define GFX_LAYERS_ISURFACEDEALLOCATOR
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t
+#include "gfxTypes.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/RefPtr.h"
+#include "nsIMemoryReporter.h" // for nsIMemoryReporter
+#include "mozilla/Atomics.h" // for Atomic
+#include "mozilla/layers/LayersMessages.h" // for ShmemSection
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+class IShmemAllocator;
+} // namespace ipc
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class CompositableForwarder;
+class CompositorBridgeParentBase;
+class TextureForwarder;
+
+class ShmemSectionAllocator;
+class LegacySurfaceDescriptorAllocator;
+class ClientIPCAllocator;
+class HostIPCAllocator;
+class LayersIPCChannel;
+
+enum BufferCapabilities {
+ DEFAULT_BUFFER_CAPS = 0,
+ /**
+ * The allocated buffer must be efficiently mappable as a DataSourceSurface.
+ */
+ MAP_AS_IMAGE_SURFACE = 1 << 0,
+ /**
+ * The allocated buffer will be used for GL rendering only
+ */
+ USING_GL_RENDERING_ONLY = 1 << 1
+};
+
+class SurfaceDescriptor;
+
+mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType();
+
+/**
+ * An interface used to create and destroy surfaces that are shared with the
+ * Compositor process (using shmem, or other platform specific memory)
+ *
+ * Most of the methods here correspond to methods that are implemented by IPDL
+ * actors without a common polymorphic interface.
+ * These methods should be only called in the ipdl implementor's thread, unless
+ * specified otherwise in the implementing class.
+ */
+class ISurfaceAllocator {
+ public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(ISurfaceAllocator)
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ISurfaceAllocator)
+
+ ISurfaceAllocator() = default;
+
+ // down-casting
+
+ virtual mozilla::ipc::IShmemAllocator* AsShmemAllocator() { return nullptr; }
+
+ virtual ShmemSectionAllocator* AsShmemSectionAllocator() { return nullptr; }
+
+ virtual CompositableForwarder* AsCompositableForwarder() { return nullptr; }
+
+ virtual TextureForwarder* GetTextureForwarder() { return nullptr; }
+
+ virtual ClientIPCAllocator* AsClientAllocator() { return nullptr; }
+
+ virtual HostIPCAllocator* AsHostIPCAllocator() { return nullptr; }
+
+ virtual LegacySurfaceDescriptorAllocator*
+ AsLegacySurfaceDescriptorAllocator() {
+ return nullptr;
+ }
+
+ virtual CompositorBridgeParentBase* AsCompositorBridgeParentBase() {
+ return nullptr;
+ }
+
+ // ipc info
+
+ virtual bool IPCOpen() const { return true; }
+
+ virtual bool IsSameProcess() const = 0;
+
+ virtual bool UsesImageBridge() const { return false; }
+
+ virtual bool UsesWebRenderBridge() const { return false; }
+
+ protected:
+ void Finalize() {}
+
+ virtual ~ISurfaceAllocator() = default;
+};
+
+/// Methods that are specific to the client/child side.
+class ClientIPCAllocator : public ISurfaceAllocator {
+ public:
+ ClientIPCAllocator() = default;
+
+ ClientIPCAllocator* AsClientAllocator() override { return this; }
+
+ virtual base::ProcessId GetParentPid() const = 0;
+
+ virtual MessageLoop* GetMessageLoop() const = 0;
+
+ virtual void CancelWaitForNotifyNotUsed(uint64_t aTextureId) = 0;
+};
+
+/// Methods that are specific to the host/parent side.
+class HostIPCAllocator : public ISurfaceAllocator {
+ public:
+ HostIPCAllocator() = default;
+
+ HostIPCAllocator* AsHostIPCAllocator() override { return this; }
+
+ /**
+ * Get child side's process Id.
+ */
+ virtual base::ProcessId GetChildProcessId() = 0;
+
+ virtual void NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) = 0;
+
+ virtual void SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) = 0;
+
+ virtual void SendPendingAsyncMessages();
+
+ virtual void SetAboutToSendAsyncMessages() {
+ mAboutToSendAsyncMessages = true;
+ }
+
+ bool IsAboutToSendAsyncMessages() { return mAboutToSendAsyncMessages; }
+
+ protected:
+ std::vector<AsyncParentMessageData> mPendingAsyncMessage;
+ bool mAboutToSendAsyncMessages = false;
+};
+
+/// An allocator that can group allocations in bigger chunks of shared memory.
+///
+/// The allocated shmem sections can only be deallocated by the same allocator
+/// instance (and only in the child process).
+class ShmemSectionAllocator {
+ public:
+ virtual bool AllocShmemSection(uint32_t aSize,
+ ShmemSection* aShmemSection) = 0;
+
+ virtual void DeallocShmemSection(ShmemSection& aShmemSection) = 0;
+
+ virtual void MemoryPressure() {}
+};
+
+/// Some old stuff that's still around and used for screenshots.
+///
+/// New code should not need this (see TextureClient).
+class LegacySurfaceDescriptorAllocator {
+ public:
+ virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer) = 0;
+
+ virtual bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ uint32_t aCaps,
+ SurfaceDescriptor* aBuffer) = 0;
+
+ virtual void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) = 0;
+};
+
+bool IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface);
+
+already_AddRefed<gfx::DrawTarget> GetDrawTargetForDescriptor(
+ const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend);
+
+already_AddRefed<gfx::DataSourceSurface> GetSurfaceForDescriptor(
+ const SurfaceDescriptor& aDescriptor);
+
+uint8_t* GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor);
+
+void DestroySurfaceDescriptor(mozilla::ipc::IShmemAllocator* aAllocator,
+ SurfaceDescriptor* aSurface);
+
+class GfxMemoryImageReporter final : public nsIMemoryReporter {
+ ~GfxMemoryImageReporter() = default;
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ GfxMemoryImageReporter() {
+#ifdef DEBUG
+ // There must be only one instance of this class, due to |sAmount|
+ // being static.
+ static bool hasRun = false;
+ MOZ_ASSERT(!hasRun);
+ hasRun = true;
+#endif
+ }
+
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
+
+ static void DidAlloc(void* aPointer) {
+ sAmount += MallocSizeOfOnAlloc(aPointer);
+ }
+
+ static void WillFree(void* aPointer) {
+ sAmount -= MallocSizeOfOnFree(aPointer);
+ }
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override {
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/heap-textures", KIND_HEAP, UNITS_BYTES, sAmount,
+ "Heap memory shared between threads by texture clients and hosts.");
+
+ return NS_OK;
+ }
+
+ private:
+ // Typically we use |size_t| in memory reporters, but in the past this
+ // variable has sometimes gone negative due to missing DidAlloc() calls.
+ // Therefore, we use a signed type so that any such negative values show up
+ // as negative in about:memory, rather than as enormous positive numbers.
+ static mozilla::Atomic<ptrdiff_t> sAmount;
+};
+
+/// A simple shmem section allocator that can only allocate small
+/// fixed size elements (only intended to be used to store tile
+/// copy-on-write locks for now).
+class FixedSizeSmallShmemSectionAllocator final : public ShmemSectionAllocator {
+ public:
+ enum AllocationStatus { STATUS_ALLOCATED, STATUS_FREED };
+
+ struct ShmemSectionHeapHeader {
+ Atomic<uint32_t> mTotalBlocks;
+ Atomic<uint32_t> mAllocatedBlocks;
+ };
+
+ struct ShmemSectionHeapAllocation {
+ Atomic<uint32_t> mStatus;
+ uint32_t mSize;
+ };
+
+ explicit FixedSizeSmallShmemSectionAllocator(LayersIPCChannel* aShmProvider);
+
+ ~FixedSizeSmallShmemSectionAllocator();
+
+ bool AllocShmemSection(uint32_t aSize, ShmemSection* aShmemSection) override;
+
+ void DeallocShmemSection(ShmemSection& aShmemSection) override;
+
+ void MemoryPressure() override { ShrinkShmemSectionHeap(); }
+
+ // can be called on the compositor process.
+ static void FreeShmemSection(ShmemSection& aShmemSection);
+
+ void ShrinkShmemSectionHeap();
+
+ bool IPCOpen() const;
+
+ protected:
+ std::vector<mozilla::ipc::Shmem> mUsedShmems;
+ LayersIPCChannel* mShmProvider;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp
new file mode 100644
index 0000000000..0c3024426f
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -0,0 +1,995 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageBridgeChild.h"
+
+#include <vector> // for vector
+
+#include "ImageBridgeParent.h" // for ImageBridgeParent
+#include "ImageContainer.h" // for ImageContainer
+#include "Layers.h" // for Layer, etc
+#include "ShadowLayers.h" // for ShadowLayerForwarder
+#include "SynchronousTask.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/LayersMessages.h" // for CompositableOperation
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
+#include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "transport/runnable_utils.h"
+#include "nsContentUtils.h"
+#include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsTArrayForwardDeclare.h" // for AutoTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+
+#if defined(XP_WIN)
+# include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/layers/AndroidHardwareBuffer.h"
+#endif
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::media;
+
+typedef std::vector<CompositableOperation> OpVector;
+typedef nsTArray<OpDestroy> OpDestroyVector;
+
+struct CompositableTransaction {
+ CompositableTransaction() : mFinished(true) {}
+ ~CompositableTransaction() { End(); }
+ bool Finished() const { return mFinished; }
+ void Begin() {
+ MOZ_ASSERT(mFinished);
+ mFinished = false;
+ }
+ void End() {
+ mFinished = true;
+ mOperations.clear();
+ mDestroyedActors.Clear();
+ }
+ bool IsEmpty() const {
+ return mOperations.empty() && mDestroyedActors.IsEmpty();
+ }
+ void AddNoSwapEdit(const CompositableOperation& op) {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mOperations.push_back(op);
+ }
+
+ OpVector mOperations;
+ OpDestroyVector mDestroyedActors;
+
+ bool mFinished;
+};
+
+struct AutoEndTransaction final {
+ explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
+ ~AutoEndTransaction() { mTxn->End(); }
+ CompositableTransaction* mTxn;
+};
+
+void ImageBridgeChild::UseTextures(
+ CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) {
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aCompositable->GetIPCHandle());
+ MOZ_ASSERT(aCompositable->IsConnected());
+
+ AutoTArray<TimedTexture, 4> textures;
+
+ for (auto& t : aTextures) {
+ MOZ_ASSERT(t.mTextureClient);
+ MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
+
+ if (!t.mTextureClient->IsSharedWithCompositor()) {
+ return;
+ }
+
+ bool readLocked = t.mTextureClient->OnForwardedToHost();
+
+ auto fenceFd = t.mTextureClient->GetInternalData()->GetAcquireFence();
+ if (fenceFd.IsValid()) {
+ mTxn->AddNoSwapEdit(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpDeliverAcquireFence(nullptr, t.mTextureClient->GetIPDLActor(),
+ fenceFd)));
+ }
+
+ textures.AppendElement(
+ TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), t.mTimeStamp,
+ t.mPictureRect, t.mFrameID, t.mProducerID, readLocked));
+
+ // Wait end of usage on host side if TextureFlags::RECYCLE is set
+ HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
+ }
+ mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(),
+ OpUseTexture(textures)));
+}
+
+void ImageBridgeChild::UseComponentAlphaTextures(
+ CompositableClient* aCompositable, TextureClient* aTextureOnBlack,
+ TextureClient* aTextureOnWhite) {
+ MOZ_CRASH("should not be called");
+}
+
+void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(
+ TextureClient* aClient) {
+ if (!aClient) {
+ return;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ auto bufferId = aClient->GetInternalData()->GetBufferId();
+ if (bufferId.isSome()) {
+ MOZ_ASSERT(aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END);
+ AndroidHardwareBufferManager::Get()->HoldUntilNotifyNotUsed(
+ bufferId.ref(), GetFwdTransactionId(), /* aUsesImageBridge */ true);
+ }
+#endif
+
+ // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE or
+ // TextureFlags::WAIT_HOST_USAGE_END is set on ImageBridge.
+ bool waitNotifyNotUsed =
+ aClient->GetFlags() & TextureFlags::RECYCLE ||
+ aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END;
+ if (!waitNotifyNotUsed) {
+ return;
+ }
+
+ aClient->SetLastFwdTransactionId(GetFwdTransactionId());
+ mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient);
+}
+
+void ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId,
+ uint64_t aFwdTransactionId) {
+ auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId);
+ if (it != mTexturesWaitingNotifyNotUsed.end()) {
+ if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) {
+ // Released on host side, but client already requested newer use texture.
+ return;
+ }
+ mTexturesWaitingNotifyNotUsed.erase(it);
+ }
+}
+
+void ImageBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) {
+ MOZ_ASSERT(InImageBridgeChildThread());
+ mTexturesWaitingNotifyNotUsed.erase(aTextureId);
+}
+
+// Singleton
+static StaticMutex sImageBridgeSingletonLock;
+static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
+static StaticRefPtr<nsIThread> sImageBridgeChildThread;
+
+// dispatched function
+void ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask) {
+ AutoCompleteTask complete(aTask);
+
+ MOZ_ASSERT(InImageBridgeChildThread(),
+ "Should be in ImageBridgeChild thread.");
+
+ MediaSystemResourceManager::Shutdown();
+
+ // Force all managed protocols to shut themselves down cleanly
+ nsTArray<PTextureChild*> textures;
+ ManagedPTextureChild(textures);
+ for (int i = textures.Length() - 1; i >= 0; --i) {
+ RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
+ if (client) {
+ client->Destroy();
+ }
+ }
+
+ if (mCanSend) {
+ SendWillClose();
+ }
+ MarkShutDown();
+
+ // From now on, no message can be sent through the image bridge from the
+ // client side except the final Stop message.
+}
+
+// dispatched function
+void ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask) {
+ AutoCompleteTask complete(aTask);
+
+ MOZ_ASSERT(InImageBridgeChildThread(),
+ "Should be in ImageBridgeChild thread.");
+ if (!mDestroyed) {
+ Close();
+ }
+}
+
+void ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mCanSend = false;
+ mDestroyed = true;
+ {
+ MutexAutoLock lock(mContainerMapLock);
+ mImageContainerListeners.clear();
+ }
+}
+
+void ImageBridgeChild::ActorDealloc() { this->Release(); }
+
+void ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer) {
+ AutoCompleteTask complete(aTask);
+ *result = CreateImageClientNow(aType, aImageContainer);
+}
+
+ImageBridgeChild::ImageBridgeChild(uint32_t aNamespace)
+ : mNamespace(aNamespace),
+ mCanSend(false),
+ mDestroyed(false),
+ mFwdTransactionId(0),
+ mContainerMapLock("ImageBridgeChild.mContainerMapLock") {
+ MOZ_ASSERT(mNamespace);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mTxn = new CompositableTransaction();
+}
+
+ImageBridgeChild::~ImageBridgeChild() { delete mTxn; }
+
+void ImageBridgeChild::MarkShutDown() {
+ mTexturesWaitingNotifyNotUsed.clear();
+
+ mCanSend = false;
+}
+
+void ImageBridgeChild::Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) {
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(InImageBridgeChildThread());
+ MOZ_ASSERT(CanSend());
+
+ // Note: this is static, rather than per-IBC, so IDs are not re-used across
+ // ImageBridgeChild instances. This is relevant for the GPU process, where
+ // we don't want old IDs to potentially leak into a recreated ImageBridge.
+ static uint64_t sNextID = 1;
+ uint64_t id = sNextID++;
+
+ // ImageClient of ImageContainer provides aImageContainer.
+ // But offscreen canvas does not provide it.
+ if (aImageContainer) {
+ MutexAutoLock lock(mContainerMapLock);
+ MOZ_ASSERT(mImageContainerListeners.find(id) ==
+ mImageContainerListeners.end());
+ mImageContainerListeners.emplace(
+ id, aImageContainer->GetImageContainerListener());
+ }
+
+ CompositableHandle handle(id);
+ aCompositable->InitIPDL(handle);
+ SendNewCompositable(handle, aCompositable->GetTextureInfo(),
+ GetCompositorBackendType());
+}
+
+void ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle) {
+ MutexAutoLock lock(mContainerMapLock);
+ mImageContainerListeners.erase(aHandle.Value());
+}
+
+/* static */
+RefPtr<ImageBridgeChild> ImageBridgeChild::GetSingleton() {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ return sImageBridgeChildSingleton;
+}
+
+void ImageBridgeChild::UpdateImageClient(RefPtr<ImageContainer> aContainer) {
+ if (!aContainer) {
+ return;
+ }
+
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable =
+ WrapRunnable(RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::UpdateImageClient, aContainer);
+ GetThread()->Dispatch(runnable.forget());
+ return;
+ }
+
+ if (!CanSend()) {
+ return;
+ }
+
+ RefPtr<ImageClient> client = aContainer->GetImageClient();
+ if (NS_WARN_IF(!client)) {
+ return;
+ }
+
+ // If the client has become disconnected before this event was dispatched,
+ // early return now.
+ if (!client->IsConnected()) {
+ return;
+ }
+
+ BeginTransaction();
+ client->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
+ EndTransaction();
+}
+
+void ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
+ ImageClient* aClient,
+ ImageContainer* aContainer) {
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ MOZ_ASSERT(aClient);
+ BeginTransaction();
+ if (aContainer) {
+ aContainer->ClearImagesFromImageBridge();
+ }
+ aClient->FlushAllImages();
+ EndTransaction();
+}
+
+void ImageBridgeChild::FlushAllImages(ImageClient* aClient,
+ ImageContainer* aContainer) {
+ MOZ_ASSERT(aClient);
+ MOZ_ASSERT(!InImageBridgeChildThread());
+
+ if (InImageBridgeChildThread()) {
+ NS_ERROR(
+ "ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
+ return;
+ }
+
+ SynchronousTask task("FlushAllImages Lock");
+
+ // RefPtrs on arguments are not needed since this dispatches synchronously.
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::FlushAllImagesSync,
+ &task, aClient, aContainer);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+}
+
+void ImageBridgeChild::BeginTransaction() {
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
+ UpdateFwdTransactionId();
+ mTxn->Begin();
+}
+
+void ImageBridgeChild::EndTransaction() {
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
+
+ AutoEndTransaction _(mTxn);
+
+ if (mTxn->IsEmpty()) {
+ return;
+ }
+
+ AutoTArray<CompositableOperation, 10> cset;
+ cset.SetCapacity(mTxn->mOperations.size());
+ if (!mTxn->mOperations.empty()) {
+ cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
+ }
+
+ if (!IsSameProcess()) {
+ ShadowLayerForwarder::PlatformSyncBeforeUpdate();
+ }
+
+ if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
+ NS_WARNING("could not send async texture transaction");
+ return;
+ }
+}
+
+bool ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
+ uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ gfxPlatform::GetPlatform();
+
+ if (!sImageBridgeChildThread) {
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to start ImageBridgeChild thread!");
+ sImageBridgeChildThread = thread.forget();
+ }
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
+
+ child->GetThread()->Dispatch(NS_NewRunnableFunction(
+ "layers::ImageBridgeChild::Bind",
+ [child, endpoint = std::move(aEndpoint)]() mutable {
+ child->Bind(std::move(endpoint));
+ }));
+
+ // Assign this after so other threads can't post messages before we connect to
+ // IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+
+ return true;
+}
+
+bool ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
+ uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Note that at this point, ActorDestroy may not have been called yet,
+ // meaning mCanSend is still true. In this case we will try to send a
+ // synchronous WillClose message to the parent, and will certainly get a
+ // false result and a MsgDropped processing error. This is okay.
+ ShutdownSingleton();
+
+ return InitForContent(std::move(aEndpoint), aNamespace);
+}
+
+void ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+
+ // This reference is dropped in DeallocPImageBridgeChild.
+ this->AddRef();
+
+ mCanSend = true;
+}
+
+void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) {
+ ipc::MessageChannel* parentChannel = aParent->GetIPCChannel();
+ Open(parentChannel, aParent->GetThread(), mozilla::ipc::ChildSide);
+
+ // This reference is dropped in DeallocPImageBridgeChild.
+ this->AddRef();
+
+ mCanSend = true;
+}
+
+/* static */
+void ImageBridgeChild::ShutDown() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ShutdownSingleton();
+
+ if (sImageBridgeChildThread) {
+ sImageBridgeChildThread->Shutdown();
+ sImageBridgeChildThread = nullptr;
+ }
+}
+
+/* static */
+void ImageBridgeChild::ShutdownSingleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+ child->WillShutdown();
+
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = nullptr;
+ }
+}
+
+void ImageBridgeChild::WillShutdown() {
+ {
+ SynchronousTask task("ImageBridge ShutdownStep1 lock");
+
+ RefPtr<Runnable> runnable =
+ WrapRunnable(RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ShutdownStep1, &task);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+ }
+
+ {
+ SynchronousTask task("ImageBridge ShutdownStep2 lock");
+
+ RefPtr<Runnable> runnable =
+ WrapRunnable(RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ShutdownStep2, &task);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+ }
+}
+
+void ImageBridgeChild::InitSameProcess(uint32_t aNamespace) {
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
+
+ MOZ_ASSERT(!sImageBridgeChildSingleton);
+ MOZ_ASSERT(!sImageBridgeChildThread);
+
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to start ImageBridgeChild thread!");
+ sImageBridgeChildThread = thread.forget();
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
+ RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
+
+ RefPtr<Runnable> runnable =
+ WrapRunnable(child, &ImageBridgeChild::BindSameProcess, parent);
+ child->GetThread()->Dispatch(runnable.forget());
+
+ // Assign this after so other threads can't post messages before we connect to
+ // IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+}
+
+/* static */
+void ImageBridgeChild::InitWithGPUProcess(
+ Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sImageBridgeChildSingleton);
+ MOZ_ASSERT(!sImageBridgeChildThread);
+
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to start ImageBridgeChild thread!");
+ sImageBridgeChildThread = thread.forget();
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
+
+ child->GetThread()->Dispatch(NS_NewRunnableFunction(
+ "layers::ImageBridgeChild::Bind",
+ [child, endpoint = std::move(aEndpoint)]() mutable {
+ child->Bind(std::move(endpoint));
+ }));
+
+ // Assign this after so other threads can't post messages before we connect to
+ // IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+}
+
+bool InImageBridgeChildThread() {
+ return sImageBridgeChildThread &&
+ sImageBridgeChildThread->IsOnCurrentThread();
+}
+
+nsISerialEventTarget* ImageBridgeChild::GetThread() const {
+ return sImageBridgeChildThread;
+}
+
+/* static */
+void ImageBridgeChild::IdentifyCompositorTextureHost(
+ const TextureFactoryIdentifier& aIdentifier) {
+ if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+ child->UpdateTextureFactoryIdentifier(aIdentifier);
+ }
+}
+
+void ImageBridgeChild::UpdateTextureFactoryIdentifier(
+ const TextureFactoryIdentifier& aIdentifier) {
+ // ImageHost is incompatible between WebRender enabled and WebRender disabled.
+ // Then drop all ImageContainers' ImageClients during disabling WebRender.
+ bool disablingWebRender =
+ GetCompositorBackendType() == LayersBackend::LAYERS_WR &&
+ aIdentifier.mParentBackend != LayersBackend::LAYERS_WR;
+
+ // Do not update TextureFactoryIdentifier if aIdentifier is going to disable
+ // WebRender, but gecko is still using WebRender. Since gecko uses different
+ // incompatible ImageHost and TextureHost between WebRender and non-WebRender.
+ //
+ // Even when WebRender is still in use, if non-accelerated widget is opened,
+ // aIdentifier disables WebRender at ImageBridgeChild.
+ if (disablingWebRender && gfxVars::UseWebRender()) {
+ return;
+ }
+
+ // D3DTexture might become obsolte. To prevent to use obsoleted D3DTexture,
+ // drop all ImageContainers' ImageClients.
+
+ // During re-creating GPU process, there was a period that ImageBridgeChild
+ // was re-created, but ImageBridgeChild::UpdateTextureFactoryIdentifier() was
+ // not called yet. In the period, if ImageBridgeChild::CreateImageClient() is
+ // called, ImageBridgeParent creates incompatible ImageHost than
+ // WebRenderImageHost.
+ bool initializingWebRender =
+ GetCompositorBackendType() != LayersBackend::LAYERS_WR &&
+ aIdentifier.mParentBackend == LayersBackend::LAYERS_WR;
+
+ bool needsDrop = disablingWebRender || initializingWebRender;
+
+#if defined(XP_WIN)
+ RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
+ needsDrop |= !!mImageDevice && mImageDevice != device &&
+ GetCompositorBackendType() == LayersBackend::LAYERS_D3D11;
+ mImageDevice = device;
+#endif
+
+ IdentifyTextureHost(aIdentifier);
+ if (needsDrop) {
+ nsTArray<RefPtr<ImageContainerListener> > listeners;
+ {
+ MutexAutoLock lock(mContainerMapLock);
+ for (const auto& entry : mImageContainerListeners) {
+ listeners.AppendElement(entry.second);
+ }
+ }
+ // Drop ImageContainer's ImageClient whithout holding mContainerMapLock to
+ // avoid deadlock.
+ for (auto container : listeners) {
+ container->DropImageClient();
+ }
+ }
+}
+
+RefPtr<ImageClient> ImageBridgeChild::CreateImageClient(
+ CompositableType aType, ImageContainer* aImageContainer) {
+ if (InImageBridgeChildThread()) {
+ return CreateImageClientNow(aType, aImageContainer);
+ }
+
+ SynchronousTask task("CreateImageClient Lock");
+
+ RefPtr<ImageClient> result = nullptr;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::CreateImageClientSync,
+ &task, &result, aType, aImageContainer);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+
+ return result;
+}
+
+RefPtr<ImageClient> ImageBridgeChild::CreateImageClientNow(
+ CompositableType aType, ImageContainer* aImageContainer) {
+ MOZ_ASSERT(InImageBridgeChildThread());
+ if (!CanSend()) {
+ return nullptr;
+ }
+
+ RefPtr<ImageClient> client =
+ ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
+ MOZ_ASSERT(client, "failed to create ImageClient");
+ if (client) {
+ client->Connect(aImageContainer);
+ }
+ return client;
+}
+
+bool ImageBridgeChild::AllocUnsafeShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ if (!InImageBridgeChildThread()) {
+ return DispatchAllocShmemInternal(aSize, aType, aShmem,
+ true); // true: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool ImageBridgeChild::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ if (!InImageBridgeChildThread()) {
+ return DispatchAllocShmemInternal(aSize, aType, aShmem,
+ false); // false: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
+}
+
+void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem, bool aUnsafe,
+ bool* aSuccess) {
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ bool ok = false;
+ if (aUnsafe) {
+ ok = AllocUnsafeShmem(aSize, aType, aShmem);
+ } else {
+ ok = AllocShmem(aSize, aType, aShmem);
+ }
+ *aSuccess = ok;
+}
+
+bool ImageBridgeChild::DispatchAllocShmemInternal(
+ size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem,
+ bool aUnsafe) {
+ SynchronousTask task("AllocatorProxy alloc");
+
+ bool success = false;
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyAllocShmemNow,
+ &task, aSize, aType, aShmem, aUnsafe, &success);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+
+ return success;
+}
+
+void ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
+ ipc::Shmem* aShmem, bool* aResult) {
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+ *aResult = DeallocShmem(*aShmem);
+}
+
+bool ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem) {
+ if (InImageBridgeChildThread()) {
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::DeallocShmem(aShmem);
+ }
+
+ // If we can't post a task, then we definitely cannot send, so there's
+ // no reason to queue up this send.
+ if (!CanPostTask()) {
+ return false;
+ }
+
+ SynchronousTask task("AllocatorProxy Dealloc");
+ bool result = false;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyDeallocShmemNow,
+ &task, &aShmem, &result);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+ return result;
+}
+
+PTextureChild* ImageBridgeChild::AllocPTextureChild(
+ const SurfaceDescriptor&, const ReadLockDescriptor&, const LayersBackend&,
+ const TextureFlags&, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(CanSend());
+ return TextureClient::CreateIPDLActor();
+}
+
+bool ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+PMediaSystemResourceManagerChild*
+ImageBridgeChild::AllocPMediaSystemResourceManagerChild() {
+ MOZ_ASSERT(CanSend());
+ return new mozilla::media::MediaSystemResourceManagerChild();
+}
+
+bool ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(
+ PMediaSystemResourceManagerChild* aActor) {
+ MOZ_ASSERT(aActor);
+ delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
+ return true;
+}
+
+mozilla::ipc::IPCResult ImageBridgeChild::RecvParentAsyncMessages(
+ nsTArray<AsyncParentMessageData>&& aMessages) {
+ for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
+ const AsyncParentMessageData& message = aMessages[i];
+
+ switch (message.type()) {
+ case AsyncParentMessageData::TOpNotifyNotUsed: {
+ const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
+ NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
+ break;
+ }
+ case AsyncParentMessageData::TOpDeliverReleaseFence: {
+#ifdef MOZ_WIDGET_ANDROID
+ const OpDeliverReleaseFence& op = message.get_OpDeliverReleaseFence();
+ ipc::FileDescriptor fenceFd;
+ if (op.fenceFd().isSome()) {
+ fenceFd = *op.fenceFd();
+ }
+ AndroidHardwareBufferManager::Get()->NotifyNotUsed(
+ std::move(fenceFd), op.bufferId(), op.fwdTransactionId(),
+ op.usesImageBridge());
+#else
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+#endif
+ break;
+ }
+ default:
+ NS_ERROR("unknown AsyncParentMessageData type");
+ return IPC_FAIL_NO_REASON(this);
+ }
+ }
+ return IPC_OK();
+}
+
+RefPtr<ImageContainerListener> ImageBridgeChild::FindListener(
+ const CompositableHandle& aHandle) {
+ RefPtr<ImageContainerListener> listener;
+ MutexAutoLock lock(mContainerMapLock);
+ auto it = mImageContainerListeners.find(aHandle.Value());
+ if (it != mImageContainerListeners.end()) {
+ listener = it->second;
+ }
+ return listener;
+}
+
+mozilla::ipc::IPCResult ImageBridgeChild::RecvDidComposite(
+ nsTArray<ImageCompositeNotification>&& aNotifications) {
+ for (auto& n : aNotifications) {
+ RefPtr<ImageContainerListener> listener = FindListener(n.compositable());
+ if (listener) {
+ listener->NotifyComposite(n);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ImageBridgeChild::RecvReportFramesDropped(
+ const CompositableHandle& aHandle, const uint32_t& aFrames) {
+ RefPtr<ImageContainerListener> listener = FindListener(aHandle);
+ if (listener) {
+ listener->NotifyDropped(aFrames);
+ }
+
+ return IPC_OK();
+}
+
+PTextureChild* ImageBridgeChild::CreateTexture(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) {
+ MOZ_ASSERT(CanSend());
+ return SendPTextureConstructor(aSharedData, aReadLock, aLayersBackend, aFlags,
+ aSerial, aExternalImageId);
+}
+
+static bool IBCAddOpDestroy(CompositableTransaction* aTxn,
+ const OpDestroy& op) {
+ if (aTxn->Finished()) {
+ return false;
+ }
+
+ aTxn->mDestroyedActors.AppendElement(op);
+ return true;
+}
+
+bool ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture) {
+ return IBCAddOpDestroy(mTxn, OpDestroy(aTexture));
+}
+
+bool ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle) {
+ return IBCAddOpDestroy(mTxn, OpDestroy(aHandle));
+}
+
+void ImageBridgeChild::RemoveTextureFromCompositable(
+ CompositableClient* aCompositable, TextureClient* aTexture) {
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->IsSharedWithCompositor());
+ MOZ_ASSERT(aCompositable->IsConnected());
+ if (!aTexture || !aTexture->IsSharedWithCompositor() ||
+ !aCompositable->IsConnected()) {
+ return;
+ }
+
+ mTxn->AddNoSwapEdit(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
+}
+
+bool ImageBridgeChild::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+bool ImageBridgeChild::CanPostTask() const {
+ // During shutdown, the cycle collector may free objects that are holding a
+ // reference to ImageBridgeChild. Since this happens on the main thread,
+ // ImageBridgeChild will attempt to post a task to the ImageBridge thread.
+ // However the thread manager has already been shut down, so the task cannot
+ // post.
+ //
+ // It's okay if this races. We only care about the shutdown case where
+ // everything's happening on the main thread. Even if it races outside of
+ // shutdown, it's still harmless to post the task, since the task must
+ // check CanSend().
+ return !mDestroyed;
+}
+
+void ImageBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle) {
+ if (!InImageBridgeChildThread()) {
+ // If we can't post a task, then we definitely cannot send, so there's
+ // no reason to queue up this send.
+ if (!CanPostTask()) {
+ return;
+ }
+
+ RefPtr<Runnable> runnable =
+ WrapRunnable(RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ReleaseCompositable, aHandle);
+ GetThread()->Dispatch(runnable.forget());
+ return;
+ }
+
+ if (!CanSend()) {
+ return;
+ }
+
+ if (!DestroyInTransaction(aHandle)) {
+ SendReleaseCompositable(aHandle);
+ }
+
+ {
+ MutexAutoLock lock(mContainerMapLock);
+ mImageContainerListeners.erase(aHandle.Value());
+ }
+}
+
+bool ImageBridgeChild::CanSend() const {
+ MOZ_ASSERT(InImageBridgeChildThread());
+ return mCanSend;
+}
+
+void ImageBridgeChild::HandleFatalError(const char* aMsg) const {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+wr::MaybeExternalImageId ImageBridgeChild::GetNextExternalImageId() {
+ static uint32_t sNextID = 1;
+ ++sNextID;
+ MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
+
+ uint64_t imageId = mNamespace;
+ imageId = imageId << 32 | sNextID;
+ return Some(wr::ToExternalImageId(imageId));
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ImageBridgeChild.h b/gfx/layers/ipc/ImageBridgeChild.h
new file mode 100644
index 0000000000..76663f7ffd
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -0,0 +1,390 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_IMAGEBRIDGECHILD_H
+#define MOZILLA_GFX_IMAGEBRIDGECHILD_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include <unordered_map>
+
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Atomics.h"
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/PImageBridgeChild.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+class ImageClient;
+class ImageContainer;
+class ImageContainerListener;
+class ImageBridgeParent;
+class CompositableClient;
+struct CompositableTransaction;
+class Image;
+class TextureClient;
+class SynchronousTask;
+
+/**
+ * Returns true if the current thread is the ImageBrdigeChild's thread.
+ *
+ * Can be called from any thread.
+ */
+bool InImageBridgeChildThread();
+
+/**
+ * The ImageBridge protocol is meant to allow ImageContainers to forward images
+ * directly to the compositor thread/process without using the main thread.
+ *
+ * ImageBridgeChild is a CompositableForwarder just like ShadowLayerForwarder.
+ * This means it also does transactions with the compositor thread/process,
+ * except that the transactions are restricted to operations on the
+ * Compositables and cannot contain messages affecting layers directly.
+ *
+ * ImageBridgeChild is also a ISurfaceAllocator. It can be used to allocate or
+ * deallocate data that is shared with the compositor. The main differerence
+ * with other ISurfaceAllocators is that some of its overriden methods can be
+ * invoked from any thread.
+ *
+ * There are three important phases in the ImageBridge protocol. These three
+ * steps can do different things depending if (A) the ImageContainer uses
+ * ImageBridge or (B) it does not use ImageBridge:
+ *
+ * - When an ImageContainer calls its method SetCurrentImage:
+ * - (A) The image is sent directly to the compositor process through the
+ * ImageBridge IPDL protocol.
+ * On the compositor side the image is stored in a global table that
+ * associates the image with an ID corresponding to the ImageContainer, and a
+ * composition is triggered.
+ * - (B) Since it does not have an ImageBridge, the image is not sent yet.
+ * instead the will be sent to the compositor during the next layer
+ * transaction (on the main thread).
+ *
+ * - During a Layer transaction:
+ * - (A) The ImageContainer uses ImageBridge. The image is already available
+ * to the compositor process because it has been sent with SetCurrentImage.
+ * Yet, the CompositableHost on the compositor side will needs the ID
+ * referring to the ImageContainer to access the Image. So during the Swap
+ * operation that happens in the transaction, we swap the container ID rather
+ * than the image data.
+ * - (B) Since the ImageContainer does not use ImageBridge, the image data is
+ * swaped.
+ *
+ * - During composition:
+ * - (A) The CompositableHost has an AsyncID, it looks up the ID in the
+ * global table to see if there is an image. If there is no image, nothing is
+ * rendered.
+ * - (B) The CompositableHost has image data rather than an ID (meaning it is
+ * not using ImageBridge), then it just composites the image data normally.
+ *
+ * This means that there might be a possibility for the ImageBridge to send the
+ * first frame before the first layer transaction that will pass the container
+ * ID to the CompositableHost happens. In this (unlikely) case the layer is not
+ * composited until the layer transaction happens. This means this scenario is
+ * not harmful.
+ *
+ * Since sending an image through imageBridge triggers compositing, the main
+ * thread is not used at all (except for the very first transaction that
+ * provides the CompositableHost with an AsyncID).
+ */
+class ImageBridgeChild final : public PImageBridgeChild,
+ public CompositableForwarder,
+ public TextureForwarder {
+ friend class ImageContainer;
+
+ typedef nsTArray<AsyncParentMessageData> AsyncParentMessageArray;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageBridgeChild, override);
+
+ TextureForwarder* GetTextureForwarder() override { return this; }
+ LayersIPCActor* GetLayersIPCActor() override { return this; }
+
+ /**
+ * Creates the image bridge with a dedicated thread for ImageBridgeChild.
+ *
+ * We may want to use a specifi thread in the future. In this case, use
+ * CreateWithThread instead.
+ */
+ static void InitSameProcess(uint32_t aNamespace);
+
+ static void InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint,
+ uint32_t aNamespace);
+ static bool InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
+ uint32_t aNamespace);
+ static bool ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
+ uint32_t aNamespace);
+
+ /**
+ * Destroys the image bridge by calling DestroyBridge, and destroys the
+ * ImageBridge's thread.
+ *
+ * If you don't want to destroy the thread, call DestroyBridge directly
+ * instead.
+ */
+ static void ShutDown();
+
+ /**
+ * returns the singleton instance.
+ *
+ * can be called from any thread.
+ */
+ static RefPtr<ImageBridgeChild> GetSingleton();
+
+ static void IdentifyCompositorTextureHost(
+ const TextureFactoryIdentifier& aIdentifier);
+
+ void BeginTransaction();
+ void EndTransaction();
+
+ /**
+ * Returns the ImageBridgeChild's thread.
+ *
+ * Can be called from any thread.
+ */
+ nsISerialEventTarget* GetThread() const override;
+
+ base::ProcessId GetParentPid() const override { return OtherPid(); }
+
+ PTextureChild* AllocPTextureChild(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+
+ bool DeallocPTextureChild(PTextureChild* actor);
+
+ PMediaSystemResourceManagerChild* AllocPMediaSystemResourceManagerChild();
+ bool DeallocPMediaSystemResourceManagerChild(
+ PMediaSystemResourceManagerChild* aActor);
+
+ mozilla::ipc::IPCResult RecvParentAsyncMessages(
+ nsTArray<AsyncParentMessageData>&& aMessages);
+
+ mozilla::ipc::IPCResult RecvDidComposite(
+ nsTArray<ImageCompositeNotification>&& aNotifications);
+
+ mozilla::ipc::IPCResult RecvReportFramesDropped(
+ const CompositableHandle& aHandle, const uint32_t& aFrames);
+
+ // Create an ImageClient from any thread.
+ RefPtr<ImageClient> CreateImageClient(CompositableType aType,
+ ImageContainer* aImageContainer);
+
+ // Create an ImageClient from the ImageBridge thread.
+ RefPtr<ImageClient> CreateImageClientNow(CompositableType aType,
+ ImageContainer* aImageContainer);
+
+ void UpdateImageClient(RefPtr<ImageContainer> aContainer);
+
+ /**
+ * Flush all Images sent to CompositableHost.
+ */
+ void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
+
+ bool IPCOpen() const override { return mCanSend; }
+
+ private:
+ /**
+ * This must be called by the static function DeleteImageBridgeSync defined
+ * in ImageBridgeChild.cpp ONLY.
+ */
+ virtual ~ImageBridgeChild();
+
+ // Helpers for dispatching.
+ void CreateImageClientSync(SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer);
+
+ void FlushAllImagesSync(SynchronousTask* aTask, ImageClient* aClient,
+ ImageContainer* aContainer);
+
+ void ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ mozilla::ipc::Shmem* aShmem, bool aUnsafe,
+ bool* aSuccess);
+ void ProxyDeallocShmemNow(SynchronousTask* aTask, mozilla::ipc::Shmem* aShmem,
+ bool* aResult);
+
+ void UpdateTextureFactoryIdentifier(
+ const TextureFactoryIdentifier& aIdentifier);
+
+ public:
+ // CompositableForwarder
+
+ void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) override;
+
+ bool UsesImageBridge() const override { return true; }
+
+ /**
+ * See CompositableForwarder::UseTextures
+ */
+ void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) override;
+ void UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aClientOnBlack,
+ TextureClient* aClientOnWhite) override;
+
+ void ReleaseCompositable(const CompositableHandle& aHandle) override;
+
+ void ForgetImageContainer(const CompositableHandle& aHandle);
+
+ /**
+ * Hold TextureClient ref until end of usage on host side if
+ * TextureFlags::RECYCLE is set. Host side's usage is checked via
+ * CompositableRef.
+ */
+ void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient);
+
+ /**
+ * Notify id of Texture When host side end its use. Transaction id is used to
+ * make sure if there is no newer usage.
+ */
+ void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
+
+ void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override;
+
+ bool DestroyInTransaction(PTextureChild* aTexture) override;
+ bool DestroyInTransaction(const CompositableHandle& aHandle);
+
+ void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) override;
+
+ void UseTiledLayerBuffer(
+ CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTileLayerDescriptor) override {
+ MOZ_CRASH("should not be called");
+ }
+
+ void UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) override {
+ MOZ_CRASH("should not be called");
+ }
+
+ // ISurfaceAllocator
+
+ /**
+ * See ISurfaceAllocator.h
+ * Can be used from any thread.
+ * If used outside the ImageBridgeChild thread, it will proxy a synchronous
+ * call on the ImageBridgeChild thread.
+ */
+ bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+
+ /**
+ * See ISurfaceAllocator.h
+ * Can be used from any thread.
+ * If used outside the ImageBridgeChild thread, it will proxy a synchronous
+ * call on the ImageBridgeChild thread.
+ */
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ PTextureChild* CreateTexture(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId,
+ nsISerialEventTarget* aTarget = nullptr) override;
+
+ bool IsSameProcess() const override;
+
+ void UpdateFwdTransactionId() override { ++mFwdTransactionId; }
+ uint64_t GetFwdTransactionId() override { return mFwdTransactionId; }
+
+ bool InForwarderThread() override { return InImageBridgeChildThread(); }
+
+ void HandleFatalError(const char* aMsg) const override;
+
+ wr::MaybeExternalImageId GetNextExternalImageId() override;
+
+ protected:
+ explicit ImageBridgeChild(uint32_t aNamespace);
+ bool DispatchAllocShmemInternal(size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ Shmem* aShmem, bool aUnsafe);
+
+ void Bind(Endpoint<PImageBridgeChild>&& aEndpoint);
+ void BindSameProcess(RefPtr<ImageBridgeParent> aParent);
+
+ void SendImageBridgeThreadId();
+
+ void WillShutdown();
+ void ShutdownStep1(SynchronousTask* aTask);
+ void ShutdownStep2(SynchronousTask* aTask);
+ void MarkShutDown();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void ActorDealloc() override;
+
+ bool CanSend() const;
+ bool CanPostTask() const;
+
+ static void ShutdownSingleton();
+
+ private:
+ uint32_t mNamespace;
+
+ CompositableTransaction* mTxn;
+
+ bool mCanSend;
+ mozilla::Atomic<bool> mDestroyed;
+
+ /**
+ * Transaction id of CompositableForwarder.
+ * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction()
+ * call.
+ */
+ uint64_t mFwdTransactionId;
+
+ /**
+ * Hold TextureClients refs until end of their usages on host side.
+ * It defer calling of TextureClient recycle callback.
+ */
+ std::unordered_map<uint64_t, RefPtr<TextureClient>>
+ mTexturesWaitingNotifyNotUsed;
+
+ /**
+ * Mapping from async compositable IDs to image containers.
+ */
+ Mutex mContainerMapLock;
+ std::unordered_map<uint64_t, RefPtr<ImageContainerListener>>
+ mImageContainerListeners;
+ RefPtr<ImageContainerListener> FindListener(
+ const CompositableHandle& aHandle);
+
+#if defined(XP_WIN)
+ /**
+ * Used for checking if D3D11Device is updated.
+ */
+ RefPtr<ID3D11Device> mImageDevice;
+#endif
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp
new file mode 100644
index 0000000000..8022b76687
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -0,0 +1,796 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageBridgeParent.h"
+#include <stdint.h> // for uint64_t, uint32_t
+#include "CompositableHost.h" // for CompositableParent, Create
+#include "GeckoProfiler.h"
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, DeleteTask, etc
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Hal.h" // for hal::SetCurrentThreadPriority()
+#include "mozilla/HalTypes.h" // for hal::THREAD_PRIORITY_COMPOSITOR
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/CompositableTransactionParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessages.h" // for EditReply
+#include "mozilla/layers/PImageBridgeParent.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Unused.h"
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for ImageBridgeParent::Release, etc
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "nsTArrayForwardDeclare.h" // for nsTArray
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#include "mozilla/layers/TextureHost.h"
+#include "nsThreadUtils.h"
+
+#if defined(OS_WIN)
+# include "mozilla/layers/TextureD3D11.h"
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/layers/AndroidHardwareBuffer.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::media;
+
+ImageBridgeParent::ImageBridgeMap ImageBridgeParent::sImageBridges;
+
+StaticAutoPtr<mozilla::Monitor> sImageBridgesLock;
+
+static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton;
+
+/* static */
+void ImageBridgeParent::Setup() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sImageBridgesLock) {
+ sImageBridgesLock = new Monitor("ImageBridges");
+ mozilla::ClearOnShutdown(&sImageBridgesLock);
+ }
+}
+
+ImageBridgeParent::ImageBridgeParent(nsISerialEventTarget* aThread,
+ ProcessId aChildProcessId)
+ : mThread(aThread),
+ mClosed(false),
+ mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SetOtherProcessId(aChildProcessId);
+}
+
+ImageBridgeParent::~ImageBridgeParent() = default;
+
+/* static */
+ImageBridgeParent* ImageBridgeParent::CreateSameProcess() {
+ base::ProcessId pid = base::GetCurrentProcId();
+ RefPtr<ImageBridgeParent> parent =
+ new ImageBridgeParent(CompositorThread(), pid);
+ parent->mSelfRef = parent;
+
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ MOZ_RELEASE_ASSERT(sImageBridges.count(pid) == 0);
+ sImageBridges[pid] = parent;
+ }
+
+ sImageBridgeParentSingleton = parent;
+ return parent;
+}
+
+/* static */
+bool ImageBridgeParent::CreateForGPUProcess(
+ Endpoint<PImageBridgeParent>&& aEndpoint) {
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+
+ nsCOMPtr<nsISerialEventTarget> compositorThread = CompositorThread();
+ if (!compositorThread) {
+ return false;
+ }
+
+ RefPtr<ImageBridgeParent> parent =
+ new ImageBridgeParent(compositorThread, aEndpoint.OtherPid());
+
+ compositorThread->Dispatch(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>(
+ "layers::ImageBridgeParent::Bind", parent, &ImageBridgeParent::Bind,
+ std::move(aEndpoint)));
+
+ sImageBridgeParentSingleton = parent;
+ return true;
+}
+
+/* static */
+void ImageBridgeParent::ShutdownInternal() {
+ // We make a copy because we don't want to hold the lock while closing and we
+ // don't want the object to get freed underneath us.
+ nsTArray<RefPtr<ImageBridgeParent>> actors;
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ for (const auto& iter : sImageBridges) {
+ actors.AppendElement(iter.second);
+ }
+ }
+
+ for (auto const& actor : actors) {
+ MOZ_RELEASE_ASSERT(!actor->mClosed);
+ actor->Close();
+ }
+
+ sImageBridgeParentSingleton = nullptr;
+}
+
+/* static */
+void ImageBridgeParent::Shutdown() {
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "ImageBridgeParent::Shutdown",
+ []() -> void { ImageBridgeParent::ShutdownInternal(); }));
+}
+
+void ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+ // Can't alloc/dealloc shmems from now on.
+ mClosed = true;
+ mCompositables.clear();
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ sImageBridges.erase(OtherPid());
+ }
+ GetThread()->Dispatch(
+ NewRunnableMethod("layers::ImageBridgeParent::DeferredDestroy", this,
+ &ImageBridgeParent::DeferredDestroy));
+
+ // It is very important that this method gets called at shutdown (be it a
+ // clean or an abnormal shutdown), because DeferredDestroy is what clears
+ // mSelfRef. If mSelfRef is not null and ActorDestroy is not called, the
+ // ImageBridgeParent is leaked which causes the CompositorThreadHolder to be
+ // leaked and CompsoitorParent's shutdown ends up spinning the event loop
+ // forever, waiting for the compositor thread to terminate.
+}
+
+class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender final {
+ public:
+ explicit AutoImageBridgeParentAsyncMessageSender(
+ ImageBridgeParent* aImageBridge,
+ nsTArray<OpDestroy>* aToDestroy = nullptr)
+ : mImageBridge(aImageBridge), mToDestroy(aToDestroy) {
+ mImageBridge->SetAboutToSendAsyncMessages();
+ }
+
+ ~AutoImageBridgeParentAsyncMessageSender() {
+ mImageBridge->SendPendingAsyncMessages();
+ if (mToDestroy) {
+ for (const auto& op : *mToDestroy) {
+ mImageBridge->DestroyActor(op);
+ }
+ }
+ }
+
+ private:
+ ImageBridgeParent* mImageBridge;
+ nsTArray<OpDestroy>* mToDestroy;
+};
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvUpdate(
+ EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId) {
+ AUTO_PROFILER_TRACING_MARKER("Paint", "ImageBridgeTransaction", GRAPHICS);
+ AUTO_PROFILER_LABEL("ImageBridgeParent::RecvUpdate", GRAPHICS);
+
+ // This ensures that destroy operations are always processed. It is not safe
+ // to early-return from RecvUpdate without doing so.
+ AutoImageBridgeParentAsyncMessageSender autoAsyncMessageSender(this,
+ &aToDestroy);
+ UpdateFwdTransactionId(aFwdTransactionId);
+
+ for (const auto& edit : aEdits) {
+ RefPtr<CompositableHost> compositable =
+ FindCompositable(edit.compositable());
+ if (!compositable ||
+ !ReceiveCompositableUpdate(edit.detail(), WrapNotNull(compositable))) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ uint32_t dropped = compositable->GetDroppedFrames();
+ if (dropped) {
+ Unused << SendReportFramesDropped(edit.compositable(), dropped);
+ }
+ }
+
+ if (!IsSameProcess()) {
+ // Ensure that any pending operations involving back and front
+ // buffers have completed, so that neither process stomps on the
+ // other's buffer contents.
+ LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+ }
+
+ return IPC_OK();
+}
+
+/* static */
+bool ImageBridgeParent::CreateForContent(
+ Endpoint<PImageBridgeParent>&& aEndpoint) {
+ nsCOMPtr<nsISerialEventTarget> compositorThread = CompositorThread();
+ if (!compositorThread) {
+ return false;
+ }
+
+ RefPtr<ImageBridgeParent> bridge =
+ new ImageBridgeParent(compositorThread, aEndpoint.OtherPid());
+ compositorThread->Dispatch(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>(
+ "layers::ImageBridgeParent::Bind", bridge, &ImageBridgeParent::Bind,
+ std::move(aEndpoint)));
+
+ return true;
+}
+
+void ImageBridgeParent::Bind(Endpoint<PImageBridgeParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) return;
+ mSelfRef = this;
+
+ // If the child process ID was reused by the OS before the ImageBridgeParent
+ // object was destroyed, we need to clean it up first.
+ RefPtr<ImageBridgeParent> oldActor;
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ ImageBridgeMap::const_iterator i = sImageBridges.find(OtherPid());
+ if (i != sImageBridges.end()) {
+ oldActor = i->second;
+ }
+ }
+
+ // We can't hold the lock during Close because it erases itself from the map.
+ if (oldActor) {
+ MOZ_RELEASE_ASSERT(!oldActor->mClosed);
+ oldActor->Close();
+ }
+
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ sImageBridges[OtherPid()] = this;
+ }
+}
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvWillClose() {
+ // If there is any texture still alive we have to force it to deallocate the
+ // device data (GL textures, etc.) now because shortly after SenStop() returns
+ // on the child side the widget will be destroyed along with it's associated
+ // GL context.
+ nsTArray<PTextureParent*> textures;
+ ManagedPTextureParent(textures);
+ for (unsigned int i = 0; i < textures.Length(); ++i) {
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
+ tex->DeallocateDeviceData();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvNewCompositable(
+ const CompositableHandle& aHandle, const TextureInfo& aInfo,
+ const LayersBackend& aLayersBackend) {
+ bool useWebRender = aLayersBackend == LayersBackend::LAYERS_WR;
+ RefPtr<CompositableHost> host = AddCompositable(aHandle, aInfo, useWebRender);
+ if (!host) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ host->SetAsyncRef(AsyncCompositableRef(OtherPid(), aHandle));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvReleaseCompositable(
+ const CompositableHandle& aHandle) {
+ ReleaseCompositable(aHandle);
+ return IPC_OK();
+}
+
+PTextureParent* ImageBridgeParent::AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const uint64_t& aSerial, const wr::MaybeExternalImageId& aExternalImageId) {
+ return TextureHost::CreateIPDLActor(this, aSharedData, aReadLock,
+ aLayersBackend, aFlags, aSerial,
+ aExternalImageId);
+}
+
+bool ImageBridgeParent::DeallocPTextureParent(PTextureParent* actor) {
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+PMediaSystemResourceManagerParent*
+ImageBridgeParent::AllocPMediaSystemResourceManagerParent() {
+ return new mozilla::media::MediaSystemResourceManagerParent();
+}
+
+bool ImageBridgeParent::DeallocPMediaSystemResourceManagerParent(
+ PMediaSystemResourceManagerParent* aActor) {
+ MOZ_ASSERT(aActor);
+ delete static_cast<mozilla::media::MediaSystemResourceManagerParent*>(aActor);
+ return true;
+}
+
+void ImageBridgeParent::SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) {
+ mozilla::Unused << SendParentAsyncMessages(aMessage);
+}
+
+class ProcessIdComparator {
+ public:
+ bool Equals(const ImageCompositeNotificationInfo& aA,
+ const ImageCompositeNotificationInfo& aB) const {
+ return aA.mImageBridgeProcessId == aB.mImageBridgeProcessId;
+ }
+ bool LessThan(const ImageCompositeNotificationInfo& aA,
+ const ImageCompositeNotificationInfo& aB) const {
+ return aA.mImageBridgeProcessId < aB.mImageBridgeProcessId;
+ }
+};
+
+/* static */
+bool ImageBridgeParent::NotifyImageComposites(
+ nsTArray<ImageCompositeNotificationInfo>& aNotifications) {
+ // Group the notifications by destination process ID and then send the
+ // notifications in one message per group.
+ aNotifications.Sort(ProcessIdComparator());
+ uint32_t i = 0;
+ bool ok = true;
+ while (i < aNotifications.Length()) {
+ AutoTArray<ImageCompositeNotification, 1> notifications;
+ notifications.AppendElement(aNotifications[i].mNotification);
+ uint32_t end = i + 1;
+ MOZ_ASSERT(aNotifications[i].mNotification.compositable());
+ ProcessId pid = aNotifications[i].mImageBridgeProcessId;
+ while (end < aNotifications.Length() &&
+ aNotifications[end].mImageBridgeProcessId == pid) {
+ notifications.AppendElement(aNotifications[end].mNotification);
+ ++end;
+ }
+ RefPtr<ImageBridgeParent> bridge = GetInstance(pid);
+ if (!bridge || bridge->mClosed) {
+ i = end;
+ continue;
+ }
+ bridge->SendPendingAsyncMessages();
+ if (!bridge->SendDidComposite(notifications)) {
+ ok = false;
+ }
+ i = end;
+ }
+ return ok;
+}
+
+void ImageBridgeParent::DeferredDestroy() {
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr; // "this" ImageBridge may get deleted here.
+}
+
+already_AddRefed<ImageBridgeParent> ImageBridgeParent::GetInstance(
+ ProcessId aId) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MonitorAutoLock lock(*sImageBridgesLock);
+ ImageBridgeMap::const_iterator i = sImageBridges.find(aId);
+ if (i == sImageBridges.end()) {
+ NS_WARNING("Cannot find image bridge for process!");
+ return nullptr;
+ }
+ RefPtr<ImageBridgeParent> bridge = i->second;
+ return bridge.forget();
+}
+
+bool ImageBridgeParent::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool ImageBridgeParent::AllocUnsafeShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool ImageBridgeParent::DeallocShmem(ipc::Shmem& aShmem) {
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::DeallocShmem(aShmem);
+}
+
+bool ImageBridgeParent::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void ImageBridgeParent::NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) {
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
+ if (!texture) {
+ return;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (auto hardwareBuffer = texture->GetAndroidHardwareBuffer()) {
+ MOZ_ASSERT(texture->GetFlags() & TextureFlags::RECYCLE);
+
+ Maybe<FileDescriptor> fenceFd = Some(FileDescriptor());
+ auto* compositor = texture->GetProvider()
+ ? texture->GetProvider()->AsCompositorOGL()
+ : nullptr;
+ if (compositor) {
+ fenceFd = Some(compositor->GetReleaseFence());
+ }
+
+ auto* wrTexture = texture->AsWebRenderTextureHost();
+ if (wrTexture) {
+ MOZ_ASSERT(!fenceFd->IsValid());
+ fenceFd = Some(texture->GetAndResetReleaseFence());
+ }
+
+ // Invalid file descriptor could not be sent via IPC, but
+ // OpDeliverReleaseFence message needs to be sent to child side.
+ if (!fenceFd->IsValid()) {
+ fenceFd = Nothing();
+ }
+ mPendingAsyncMessage.push_back(OpDeliverReleaseFence(
+ std::move(fenceFd), hardwareBuffer->mId, aTransactionId,
+ /* usesImageBridge */ true));
+ }
+#endif
+
+ if (!(texture->GetFlags() & TextureFlags::RECYCLE) &&
+ !(texture->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END)) {
+ return;
+ }
+
+ uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
+ mPendingAsyncMessage.push_back(OpNotifyNotUsed(textureId, aTransactionId));
+
+ if (!IsAboutToSendAsyncMessages()) {
+ SendPendingAsyncMessages();
+ }
+}
+
+/* static */
+void ImageBridgeParent::NotifyBufferNotUsedOfCompositorBridge(
+ base::ProcessId aChildProcessId, TextureHost* aTexture,
+ uint64_t aTransactionId) {
+ RefPtr<ImageBridgeParent> bridge = GetInstance(aChildProcessId);
+ if (!bridge || bridge->mClosed) {
+ return;
+ }
+ bridge->NotifyBufferNotUsedOfCompositorBridge(aTexture, aTransactionId);
+}
+
+void ImageBridgeParent::NotifyBufferNotUsedOfCompositorBridge(
+ TextureHost* aTexture, uint64_t aTransactionId) {
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->GetAndroidHardwareBuffer());
+
+#ifdef MOZ_WIDGET_ANDROID
+ auto* compositor = aTexture->GetProvider()
+ ? aTexture->GetProvider()->AsCompositorOGL()
+ : nullptr;
+ Maybe<FileDescriptor> fenceFd = Some(FileDescriptor());
+ if (compositor) {
+ fenceFd = Some(compositor->GetReleaseFence());
+ }
+
+ auto* wrTexture = aTexture->AsWebRenderTextureHost();
+ if (wrTexture) {
+ MOZ_ASSERT(!fenceFd->IsValid());
+ fenceFd = Some(aTexture->GetAndResetReleaseFence());
+ }
+
+ // Invalid file descriptor could not be sent via IPC, but
+ // OpDeliverReleaseFence message needs to be sent to child side.
+ if (!fenceFd->IsValid()) {
+ fenceFd = Nothing();
+ }
+ mPendingAsyncMessage.push_back(
+ OpDeliverReleaseFence(fenceFd, aTexture->GetAndroidHardwareBuffer()->mId,
+ aTransactionId, /* usesImageBridge */ false));
+ SendPendingAsyncMessages();
+#else
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+#endif
+}
+
+#if defined(OS_WIN)
+
+ImageBridgeParent::PluginTextureDatas::PluginTextureDatas(
+ UniquePtr<D3D11TextureData>&& aPluginTextureData,
+ UniquePtr<D3D11TextureData>&& aDisplayTextureData)
+ : mPluginTextureData(std::move(aPluginTextureData)),
+ mDisplayTextureData(std::move(aDisplayTextureData)) {}
+
+ImageBridgeParent::PluginTextureDatas::~PluginTextureDatas() {}
+
+#endif // defined(OS_WIN)
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvMakeAsyncPluginSurfaces(
+ SurfaceFormat aFormat, IntSize aSize, SurfaceDescriptorPlugin* aSD) {
+#if defined(OS_WIN)
+ *aSD = SurfaceDescriptorPlugin();
+
+ RefPtr<ID3D11Device> d3dDevice =
+ DeviceManagerDx::Get()->GetCompositorDevice();
+ if (!d3dDevice) {
+ NS_WARNING("Failed to get D3D11 device for plugin display");
+ return IPC_OK();
+ }
+
+ auto pluginSurf = WrapUnique(D3D11TextureData::Create(
+ aSize, aFormat, ALLOC_FOR_OUT_OF_BAND_CONTENT, d3dDevice));
+ if (!pluginSurf) {
+ NS_ERROR("Failed to create plugin surface");
+ return IPC_OK();
+ }
+
+ auto dispSurf = WrapUnique(D3D11TextureData::Create(
+ aSize, aFormat, ALLOC_FOR_OUT_OF_BAND_CONTENT, d3dDevice));
+ if (!dispSurf) {
+ NS_ERROR("Failed to create plugin display surface");
+ return IPC_OK();
+ }
+
+ // Identify plugin surfaces with a simple non-zero 64-bit ID.
+ static uint64_t sPluginSurfaceId = 1;
+
+ SurfaceDescriptor pluginSD, dispSD;
+ if ((!pluginSurf->Serialize(pluginSD)) || (!dispSurf->Serialize(dispSD))) {
+ NS_ERROR("Failed to make surface descriptors for plugin");
+ return IPC_OK();
+ }
+
+ if (!mPluginTextureDatas.put(
+ sPluginSurfaceId, MakeUnique<PluginTextureDatas>(
+ std::move(pluginSurf), std::move(dispSurf)))) {
+ NS_ERROR("Failed to add plugin surfaces to map");
+ return IPC_OK();
+ }
+
+ SurfaceDescriptorPlugin sd(sPluginSurfaceId, pluginSD, dispSD);
+ RefPtr<TextureHost> displayHost = CreateTextureHostD3D11(
+ dispSD, this, LayersBackend::LAYERS_NONE, TextureFlags::RECYCLE);
+ if (!displayHost) {
+ NS_ERROR("Failed to create plugin display texture host");
+ return IPC_OK();
+ }
+
+ if (!mGPUVideoTextureHosts.put(sPluginSurfaceId, displayHost)) {
+ NS_ERROR("Failed to add plugin display texture host to map");
+ return IPC_OK();
+ }
+
+ *aSD = sd;
+ ++sPluginSurfaceId;
+#endif // defined(OS_WIN)
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvUpdateAsyncPluginSurface(
+ const SurfaceDescriptorPlugin& aSD) {
+#if defined(OS_WIN)
+ uint64_t surfaceId = aSD.id();
+ auto itTextures = mPluginTextureDatas.lookup(surfaceId);
+ if (!itTextures) {
+ return IPC_OK();
+ }
+
+ auto& textures = itTextures->value();
+ if (!textures->IsValid()) {
+ // The display texture may be gone. The plugin texture should never be gone
+ // here.
+ MOZ_ASSERT(textures->mPluginTextureData);
+ return IPC_OK();
+ }
+
+ RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetCompositorDevice();
+ if (!device) {
+ NS_WARNING("Failed to get D3D11 device for plugin display");
+ return IPC_OK();
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ device->GetImmediateContext(getter_AddRefs(context));
+ if (!context) {
+ NS_WARNING("Could not get an immediate D3D11 context");
+ return IPC_OK();
+ }
+
+ RefPtr<IDXGIKeyedMutex> dispMutex;
+ HRESULT hr = textures->mDisplayTextureData->GetD3D11Texture()->QueryInterface(
+ __uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(dispMutex));
+ if (FAILED(hr) || !dispMutex) {
+ NS_WARNING("Could not acquire plugin display IDXGIKeyedMutex");
+ return IPC_OK();
+ }
+
+ RefPtr<IDXGIKeyedMutex> pluginMutex;
+ hr = textures->mPluginTextureData->GetD3D11Texture()->QueryInterface(
+ __uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(pluginMutex));
+ if (FAILED(hr) || !pluginMutex) {
+ NS_WARNING("Could not acquire plugin offscreen IDXGIKeyedMutex");
+ return IPC_OK();
+ }
+
+ {
+ AutoTextureLock lock1(dispMutex, hr);
+ if (hr == WAIT_ABANDONED || hr == WAIT_TIMEOUT || FAILED(hr)) {
+ NS_WARNING(
+ "Could not acquire DXGI surface lock - display forgot to release?");
+ return IPC_OK();
+ }
+
+ AutoTextureLock lock2(pluginMutex, hr);
+ if (hr == WAIT_ABANDONED || hr == WAIT_TIMEOUT || FAILED(hr)) {
+ NS_WARNING(
+ "Could not acquire DXGI surface lock - plugin forgot to release?");
+ return IPC_OK();
+ }
+
+ context->CopyResource(textures->mDisplayTextureData->GetD3D11Texture(),
+ textures->mPluginTextureData->GetD3D11Texture());
+ }
+#endif // defined(OS_WIN)
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvReadbackAsyncPluginSurface(
+ const SurfaceDescriptorPlugin& aSD, SurfaceDescriptor* aResult) {
+#if defined(OS_WIN)
+ *aResult = null_t();
+
+ auto itTextures = mPluginTextureDatas.lookup(aSD.id());
+ if (!itTextures) {
+ return IPC_OK();
+ }
+
+ auto& textures = itTextures->value();
+ D3D11TextureData* displayTexData = textures->mDisplayTextureData.get();
+ MOZ_RELEASE_ASSERT(displayTexData);
+ if ((!displayTexData) || (!displayTexData->GetD3D11Texture())) {
+ NS_WARNING("Error in plugin display texture");
+ return IPC_OK();
+ }
+ MOZ_ASSERT(displayTexData->GetSurfaceFormat() == SurfaceFormat::B8G8R8A8 ||
+ displayTexData->GetSurfaceFormat() == SurfaceFormat::B8G8R8X8);
+
+ RefPtr<ID3D11Device> device;
+ displayTexData->GetD3D11Texture()->GetDevice(getter_AddRefs(device));
+ if (!device) {
+ NS_WARNING("Failed to get D3D11 device for plugin display");
+ return IPC_OK();
+ }
+
+ UniquePtr<BufferTextureData> shmemTexData(BufferTextureData::Create(
+ displayTexData->GetSize(), displayTexData->GetSurfaceFormat(),
+ gfx::BackendType::SKIA, LayersBackend::LAYERS_NONE,
+ displayTexData->GetTextureFlags(), TextureAllocationFlags::ALLOC_DEFAULT,
+ this));
+ if (!shmemTexData) {
+ NS_WARNING("Could not create BufferTextureData");
+ return IPC_OK();
+ }
+
+ if (!gfx::Factory::ReadbackTexture(shmemTexData.get(),
+ displayTexData->GetD3D11Texture())) {
+ NS_WARNING("Failed to read plugin texture into Shmem");
+ return IPC_OK();
+ }
+
+ // Take the Shmem from the TextureData.
+ shmemTexData->Serialize(*aResult);
+#endif // defined(OS_WIN)
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvRemoveAsyncPluginSurface(
+ const SurfaceDescriptorPlugin& aSD, bool aIsFrontSurface) {
+#if defined(OS_WIN)
+ auto itTextures = mPluginTextureDatas.lookup(aSD.id());
+ if (!itTextures) {
+ return IPC_OK();
+ }
+
+ auto& textures = itTextures->value();
+ if (aIsFrontSurface) {
+ textures->mDisplayTextureData = nullptr;
+ } else {
+ textures->mPluginTextureData = nullptr;
+ }
+ if ((!textures->mDisplayTextureData) && (!textures->mPluginTextureData)) {
+ mPluginTextureDatas.remove(aSD.id());
+ }
+#endif // defined(OS_WIN)
+ return IPC_OK();
+}
+
+#if defined(OS_WIN)
+RefPtr<TextureHost> GetNullPluginTextureHost() {
+ class NullPluginTextureHost : public TextureHost {
+ public:
+ NullPluginTextureHost() : TextureHost(TextureFlags::NO_FLAGS) {}
+
+ ~NullPluginTextureHost() {}
+
+ gfx::SurfaceFormat GetFormat() const override {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
+ return nullptr;
+ }
+
+ gfx::IntSize GetSize() const override { return gfx::IntSize(); }
+
+ bool BindTextureSource(CompositableTextureSourceRef& aTexture) override {
+ return false;
+ }
+
+ const char* Name() override { return "NullPluginTextureHost"; }
+
+ virtual bool Lock() { return false; }
+
+ void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) override {}
+
+ uint32_t NumSubTextures() override { return 0; }
+
+ void PushResourceUpdates(wr::TransactionBuilder& aResources,
+ ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys,
+ const wr::ExternalImageId& aExtID) override {}
+
+ void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) override {}
+ };
+
+ static StaticRefPtr<TextureHost> sNullPluginTextureHost;
+ if (!sNullPluginTextureHost) {
+ sNullPluginTextureHost = new NullPluginTextureHost();
+ ClearOnShutdown(&sNullPluginTextureHost);
+ };
+
+ MOZ_ASSERT(sNullPluginTextureHost);
+ return sNullPluginTextureHost.get();
+}
+#endif // defined(OS_WIN)
+
+RefPtr<TextureHost> ImageBridgeParent::LookupTextureHost(
+ const SurfaceDescriptorPlugin& aDescriptor) {
+#if defined(OS_WIN)
+ auto it = mGPUVideoTextureHosts.lookup(aDescriptor.id());
+ RefPtr<TextureHost> ret = it ? it->value() : nullptr;
+ return ret ? ret : GetNullPluginTextureHost();
+#else
+ MOZ_ASSERT_UNREACHABLE("Unsupported architecture.");
+ return nullptr;
+#endif // defined(OS_WIN)
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h
new file mode 100644
index 0000000000..08680cab39
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfx_layers_ipc_ImageBridgeParent_h_
+#define gfx_layers_ipc_ImageBridgeParent_h_
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include "CompositableTransactionParent.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/PImageBridgeParent.h"
+#include "nsISupportsImpl.h"
+#include "nsTArrayForwardDeclare.h" // for nsTArray
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+struct ImageCompositeNotificationInfo;
+
+/**
+ * ImageBridgeParent is the manager Protocol of async Compositables.
+ */
+class ImageBridgeParent final : public PImageBridgeParent,
+ public CompositableParentManager,
+ public mozilla::ipc::IShmemAllocator {
+ public:
+ typedef nsTArray<CompositableOperation> EditArray;
+ typedef nsTArray<OpDestroy> OpDestroyArray;
+
+ protected:
+ ImageBridgeParent(nsISerialEventTarget* aThread, ProcessId aChildProcessId);
+
+ public:
+ virtual ~ImageBridgeParent();
+
+ /**
+ * Creates the globals of ImageBridgeParent.
+ */
+ static void Setup();
+
+ static ImageBridgeParent* CreateSameProcess();
+ static bool CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint);
+ static bool CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint);
+ static void Shutdown();
+
+ IShmemAllocator* AsShmemAllocator() override { return this; }
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // CompositableParentManager
+ void SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) override;
+
+ void NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) override;
+
+ static void NotifyBufferNotUsedOfCompositorBridge(
+ base::ProcessId aChildProcessId, TextureHost* aTexture,
+ uint64_t aTransactionId);
+
+ void NotifyBufferNotUsedOfCompositorBridge(TextureHost* aTexture,
+ uint64_t aTransactionId);
+
+ base::ProcessId GetChildProcessId() override { return OtherPid(); }
+
+ // PImageBridge
+ mozilla::ipc::IPCResult RecvUpdate(EditArray&& aEdits,
+ OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId);
+
+ PTextureParent* AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+ bool DeallocPTextureParent(PTextureParent* actor);
+
+ mozilla::ipc::IPCResult RecvNewCompositable(
+ const CompositableHandle& aHandle, const TextureInfo& aInfo,
+ const LayersBackend& aLayersBackend);
+ mozilla::ipc::IPCResult RecvReleaseCompositable(
+ const CompositableHandle& aHandle);
+
+ PMediaSystemResourceManagerParent* AllocPMediaSystemResourceManagerParent();
+ bool DeallocPMediaSystemResourceManagerParent(
+ PMediaSystemResourceManagerParent* aActor);
+
+ // Shutdown step 1
+ mozilla::ipc::IPCResult RecvWillClose();
+
+ nsISerialEventTarget* GetThread() const { return mThread; }
+
+ // IShmemAllocator
+
+ bool AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ bool AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ bool DeallocShmem(ipc::Shmem& aShmem) override;
+
+ bool IsSameProcess() const override;
+
+ static already_AddRefed<ImageBridgeParent> GetInstance(ProcessId aId);
+
+ static bool NotifyImageComposites(
+ nsTArray<ImageCompositeNotificationInfo>& aNotifications);
+
+ bool UsesImageBridge() const override { return true; }
+
+ bool IPCOpen() const override { return !mClosed; }
+
+ // See PluginInstanceParent for details on the Windows async plugin
+ // rendering protocol.
+ mozilla::ipc::IPCResult RecvMakeAsyncPluginSurfaces(
+ SurfaceFormat aFormat, IntSize aSize, SurfaceDescriptorPlugin* aSD);
+ mozilla::ipc::IPCResult RecvUpdateAsyncPluginSurface(
+ const SurfaceDescriptorPlugin& aSD);
+ mozilla::ipc::IPCResult RecvReadbackAsyncPluginSurface(
+ const SurfaceDescriptorPlugin& aSD, SurfaceDescriptor* aResult);
+ mozilla::ipc::IPCResult RecvRemoveAsyncPluginSurface(
+ const SurfaceDescriptorPlugin& aSD, bool aIsFrontSurface);
+
+ RefPtr<TextureHost> LookupTextureHost(
+ const SurfaceDescriptorPlugin& aDescriptor);
+
+ protected:
+ void Bind(Endpoint<PImageBridgeParent>&& aEndpoint);
+
+ private:
+ static void ShutdownInternal();
+
+ void DeferredDestroy();
+ nsCOMPtr<nsISerialEventTarget> mThread;
+ // This keeps us alive until ActorDestroy(), at which point we do a
+ // deferred destruction of ourselves.
+ RefPtr<ImageBridgeParent> mSelfRef;
+
+ bool mClosed;
+
+ /**
+ * Map of all living ImageBridgeParent instances
+ */
+ typedef std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeMap;
+ static ImageBridgeMap sImageBridges;
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+
+#if defined(OS_WIN)
+ // Owns a pair of textures used to double-buffer a plugin async rendering
+ // instance.
+ struct PluginTextureDatas {
+ UniquePtr<D3D11TextureData> mPluginTextureData;
+ UniquePtr<D3D11TextureData> mDisplayTextureData;
+
+ PluginTextureDatas(UniquePtr<D3D11TextureData>&& aPluginTextureData,
+ UniquePtr<D3D11TextureData>&& aDisplayTextureData);
+
+ ~PluginTextureDatas();
+
+ PluginTextureDatas(const PluginTextureDatas& o) = delete;
+ PluginTextureDatas& operator=(const PluginTextureDatas& o) = delete;
+
+ bool IsValid() { return mPluginTextureData && mDisplayTextureData; }
+ };
+
+ HashMap<WindowsHandle, RefPtr<TextureHost>> mGPUVideoTextureHosts;
+ HashMap<WindowsHandle, UniquePtr<PluginTextureDatas>> mPluginTextureDatas;
+#endif // defined(OS_WIN)
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // gfx_layers_ipc_ImageBridgeParent_h_
diff --git a/gfx/layers/ipc/KnowsCompositor.h b/gfx/layers/ipc/KnowsCompositor.h
new file mode 100644
index 0000000000..68fd77eda1
--- /dev/null
+++ b/gfx/layers/ipc/KnowsCompositor.h
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_KNOWSCOMPOSITOR
+#define MOZILLA_LAYERS_KNOWSCOMPOSITOR
+
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/CompositorTypes.h"
+#include "nsExpirationTracker.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/layers/SyncObject.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureForwarder;
+class LayersIPCActor;
+class ImageBridgeChild;
+
+/**
+ * See ActiveResourceTracker below.
+ */
+class ActiveResource {
+ public:
+ virtual void NotifyInactive() = 0;
+ nsExpirationState* GetExpirationState() { return &mExpirationState; }
+ bool IsActivityTracked() { return mExpirationState.IsTracked(); }
+
+ private:
+ nsExpirationState mExpirationState;
+};
+
+/**
+ * A convenience class on top of nsExpirationTracker
+ */
+class ActiveResourceTracker : public nsExpirationTracker<ActiveResource, 3> {
+ public:
+ ActiveResourceTracker(uint32_t aExpirationCycle, const char* aName,
+ nsIEventTarget* aEventTarget)
+ : nsExpirationTracker(aExpirationCycle, aName, aEventTarget) {}
+
+ void NotifyExpired(ActiveResource* aResource) override {
+ RemoveObject(aResource);
+ aResource->NotifyInactive();
+ }
+};
+
+/**
+ * An abstract interface for classes that are tied to a specific Compositor
+ * across IPDL and uses TextureFactoryIdentifier to describe this Compositor.
+ */
+class KnowsCompositor {
+ public:
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ KnowsCompositor();
+ virtual ~KnowsCompositor();
+
+ void IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier);
+
+ // The sync object for the global content device.
+ RefPtr<SyncObjectClient> GetSyncObject() {
+ auto lock = mData.Lock();
+ if (lock.ref().mSyncObject) {
+ lock.ref().mSyncObject->EnsureInitialized();
+ }
+ return lock.ref().mSyncObject;
+ }
+
+ /// And by "thread-safe" here we merely mean "okay to hold strong references
+ /// to from multiple threads". Not all methods actually are thread-safe.
+ virtual bool IsThreadSafe() const { return true; }
+
+ virtual RefPtr<KnowsCompositor> GetForMedia() {
+ return RefPtr<KnowsCompositor>(this);
+ }
+
+ int32_t GetMaxTextureSize() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mMaxTextureSize;
+ }
+
+ /**
+ * Returns the type of backend that is used off the main thread.
+ * We only don't allow changing the backend type at runtime so this value can
+ * be queried once and will not change until Gecko is restarted.
+ */
+ LayersBackend GetCompositorBackendType() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mParentBackend;
+ }
+
+ bool SupportsTextureBlitting() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mSupportsTextureBlitting;
+ }
+
+ bool SupportsPartialUploads() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mSupportsPartialUploads;
+ }
+
+ bool SupportsComponentAlpha() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mSupportsComponentAlpha;
+ }
+
+ bool SupportsTextureDirectMapping() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mSupportsTextureDirectMapping;
+ }
+
+ bool SupportsD3D11() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_D3D11 ||
+ (lock.ref().mTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_WR &&
+ (lock.ref().mTextureFactoryIdentifier.mCompositorUseANGLE ||
+ lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor ==
+ layers::WebRenderCompositor::D3D11));
+ }
+
+ bool GetCompositorUseANGLE() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mCompositorUseANGLE;
+ }
+
+ bool GetCompositorUseDComp() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mCompositorUseDComp;
+ }
+
+ bool GetUseCompositorWnd() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mUseCompositorWnd;
+ }
+
+ bool UsingSoftwareWebRender() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_WR &&
+ lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
+ WebRenderBackend::SOFTWARE;
+ }
+
+ bool UsingSoftwareWebRenderD3D11() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_WR &&
+ lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
+ WebRenderBackend::SOFTWARE &&
+ lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor ==
+ layers::WebRenderCompositor::D3D11;
+ }
+
+ TextureFactoryIdentifier GetTextureFactoryIdentifier() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier;
+ }
+
+ bool DeviceCanReset() const {
+ return GetCompositorBackendType() != LayersBackend::LAYERS_BASIC;
+ }
+
+ int32_t GetSerial() const { return mSerial; }
+
+ /**
+ * Sends a synchronous ping to the compsoitor.
+ *
+ * This is bad for performance and should only be called as a last resort if
+ * the compositor may be blocked for a long period of time, to avoid that the
+ * content process accumulates resource allocations that the compositor is not
+ * consuming and releasing.
+ */
+ virtual void SyncWithCompositor() { MOZ_ASSERT_UNREACHABLE("Unimplemented"); }
+
+ /**
+ * Helpers for finding other related interface. These are infallible.
+ */
+ virtual TextureForwarder* GetTextureForwarder() = 0;
+ virtual LayersIPCActor* GetLayersIPCActor() = 0;
+ virtual ActiveResourceTracker* GetActiveResourceTracker() {
+ MOZ_ASSERT_UNREACHABLE("Unimplemented");
+ return nullptr;
+ }
+
+ protected:
+ struct SharedData {
+ TextureFactoryIdentifier mTextureFactoryIdentifier;
+ RefPtr<SyncObjectClient> mSyncObject;
+ };
+ mutable DataMutex<SharedData> mData;
+
+ const int32_t mSerial;
+ static mozilla::Atomic<int32_t> sSerialCounter;
+};
+
+/// Some implementations of KnowsCompositor can be used off their IPDL thread
+/// like the ImageBridgeChild, and others just can't. Instead of passing them
+/// we create a proxy KnowsCompositor that has information about compositor
+/// backend but proxies allocations to the ImageBridge.
+/// This is kind of specific to the needs of media which wants to allocate
+/// textures, usually through the Image Bridge accessed by KnowsCompositor but
+/// also wants access to the compositor backend information that ImageBridge
+/// doesn't know about.
+///
+/// This is really a band aid to what turned into a class hierarchy horror show.
+/// Hopefully we can come back and simplify this some way.
+class KnowsCompositorMediaProxy : public KnowsCompositor {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KnowsCompositorMediaProxy, override);
+
+ explicit KnowsCompositorMediaProxy(
+ const TextureFactoryIdentifier& aIdentifier);
+
+ TextureForwarder* GetTextureForwarder() override;
+
+ LayersIPCActor* GetLayersIPCActor() override;
+
+ ActiveResourceTracker* GetActiveResourceTracker() override;
+
+ void SyncWithCompositor() override;
+
+ protected:
+ virtual ~KnowsCompositorMediaProxy();
+
+ RefPtr<ImageBridgeChild> mThreadSafeAllocator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/LayerAnimationUtils.cpp b/gfx/layers/ipc/LayerAnimationUtils.cpp
new file mode 100644
index 0000000000..2902122ad3
--- /dev/null
+++ b/gfx/layers/ipc/LayerAnimationUtils.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerAnimationUtils.h"
+#include "mozilla/ComputedTimingFunction.h" // For ComputedTimingFunction
+#include "mozilla/layers/LayersMessages.h" // For TimingFunction etc.
+#include "nsTimingFunction.h"
+
+namespace mozilla {
+namespace layers {
+
+/* static */
+Maybe<ComputedTimingFunction>
+AnimationUtils::TimingFunctionToComputedTimingFunction(
+ const TimingFunction& aTimingFunction) {
+ switch (aTimingFunction.type()) {
+ case TimingFunction::Tnull_t:
+ return Nothing();
+ case TimingFunction::TCubicBezierFunction: {
+ CubicBezierFunction cbf = aTimingFunction.get_CubicBezierFunction();
+ return Some(ComputedTimingFunction::CubicBezier(cbf.x1(), cbf.y1(),
+ cbf.x2(), cbf.y2()));
+ }
+ case TimingFunction::TStepFunction: {
+ StepFunction sf = aTimingFunction.get_StepFunction();
+ StyleStepPosition pos = static_cast<StyleStepPosition>(sf.type());
+ return Some(ComputedTimingFunction::Steps(sf.steps(), pos));
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("Function must be null, bezier, step or frames");
+ break;
+ }
+ return Nothing();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/LayerAnimationUtils.h b/gfx/layers/ipc/LayerAnimationUtils.h
new file mode 100644
index 0000000000..0a17e51b70
--- /dev/null
+++ b/gfx/layers/ipc/LayerAnimationUtils.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_LayerAnimationUtils_h
+#define mozilla_layers_LayerAnimationUtils_h
+
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+class ComputedTimingFunction;
+
+namespace layers {
+
+class TimingFunction;
+
+class AnimationUtils {
+ public:
+ static Maybe<ComputedTimingFunction> TimingFunctionToComputedTimingFunction(
+ const TimingFunction& aTimingFunction);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_LayerAnimationUtils_h
diff --git a/gfx/layers/ipc/LayerTransactionChild.cpp b/gfx/layers/ipc/LayerTransactionChild.cpp
new file mode 100644
index 0000000000..4dbd40e089
--- /dev/null
+++ b/gfx/layers/ipc/LayerTransactionChild.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTransactionChild.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsTArray.h" // for nsTArray
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+namespace layers {
+
+void LayerTransactionChild::Destroy() {
+ if (!IPCOpen()) {
+ return;
+ }
+ // mDestroyed is used to prevent calling Send__delete__() twice.
+ // When this function is called from CompositorBridgeChild::Destroy(),
+ // under Send__delete__() call, this function is called from
+ // ShadowLayerForwarder's destructor.
+ // When it happens, IPCOpen() is still true.
+ // See bug 1004191.
+ mDestroyed = true;
+
+ SendShutdown();
+}
+
+void LayerTransactionChild::ActorDestroy(ActorDestroyReason why) {
+ mDestroyed = true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/LayerTransactionChild.h b/gfx/layers/ipc/LayerTransactionChild.h
new file mode 100644
index 0000000000..83ce36b022
--- /dev/null
+++ b/gfx/layers/ipc/LayerTransactionChild.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
+#define MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
+
+#include <stdint.h> // for uint32_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PLayerTransactionChild.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+namespace layers {
+
+class ShadowLayerForwarder;
+
+class LayerTransactionChild : public PLayerTransactionChild {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LayerTransactionChild)
+ /**
+ * Clean this up, finishing with SendShutDown() which will cause __delete__
+ * to be sent from the parent side.
+ *
+ * It is expected (checked with an assert) that all shadow layers
+ * created by this have already been destroyed and
+ * Send__delete__()d by the time this method is called.
+ */
+ void Destroy();
+
+ bool IPCOpen() const { return mIPCOpen && !mDestroyed; }
+ bool IsDestroyed() const { return mDestroyed; }
+
+ void SetForwarder(ShadowLayerForwarder* aForwarder) {
+ mForwarder = aForwarder;
+ }
+
+ LayersId GetId() const { return mId; }
+
+ void MarkDestroyed() { mDestroyed = true; }
+
+ protected:
+ explicit LayerTransactionChild(const LayersId& aId)
+ : mForwarder(nullptr), mIPCOpen(false), mDestroyed(false), mId(aId) {}
+ virtual ~LayerTransactionChild() = default;
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ void AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+ }
+ void ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == true);
+ mIPCOpen = false;
+ Release();
+ }
+ friend class CompositorBridgeChild;
+
+ ShadowLayerForwarder* mForwarder;
+ bool mIPCOpen;
+ bool mDestroyed;
+ LayersId mId;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_LAYERS_LAYERTRANSACTIONCHILD_H
diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp
new file mode 100644
index 0000000000..e7dc07f1d9
--- /dev/null
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -0,0 +1,1020 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTransactionParent.h"
+#include <vector> // for vector
+#include "CompositableHost.h" // for CompositableParent, Get, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "CompositableTransactionParent.h" // for EditReplyVector
+#include "CompositorBridgeParent.h"
+#include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D
+#include "mozilla/layers/AnimationHelper.h" // for GetAnimatedPropValue
+#include "mozilla/layers/CanvasLayerComposite.h"
+#include "mozilla/layers/ColorLayerComposite.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorAnimationStorage.h" // for CompositorAnimationStorage
+#include "mozilla/layers/ContainerLayerComposite.h"
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/ImageLayerComposite.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/PaintedLayerComposite.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/PerfStats.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "nsCoord.h" // for NSAppUnitsToFloatPixels
+#include "nsISupportsImpl.h" // for Layer::Release, etc
+#include "nsLayoutUtils.h" // for nsLayoutUtils
+#include "nsMathUtils.h" // for NS_round
+#include "nsPoint.h" // for nsPoint
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "TreeTraversal.h" // for ForEachNode
+#include "GeckoProfiler.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/AsyncCompositionManager.h"
+
+using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON;
+
+namespace mozilla {
+namespace layers {
+
+//--------------------------------------------------
+// LayerTransactionParent
+LayerTransactionParent::LayerTransactionParent(
+ HostLayerManager* aManager, CompositorBridgeParentBase* aBridge,
+ CompositorAnimationStorage* aAnimStorage, LayersId aId,
+ TimeDuration aVsyncRate)
+ : mLayerManager(aManager),
+ mCompositorBridge(aBridge),
+ mAnimStorage(aAnimStorage),
+ mId(aId),
+ mChildEpoch{0},
+ mParentEpoch{0},
+ mVsyncRate(aVsyncRate),
+ mDestroyed(false),
+ mIPCOpen(false),
+ mUpdateHitTestingTree(false) {
+ MOZ_ASSERT(mId.IsValid());
+}
+
+LayerTransactionParent::~LayerTransactionParent() = default;
+
+void LayerTransactionParent::SetLayerManager(
+ HostLayerManager* aLayerManager, CompositorAnimationStorage* aAnimStorage) {
+ if (mDestroyed) {
+ return;
+ }
+ mLayerManager = aLayerManager;
+ for (auto iter = mLayerMap.Iter(); !iter.Done(); iter.Next()) {
+ auto layer = iter.Data();
+ if (mAnimStorage && layer->GetCompositorAnimationsId()) {
+ mAnimStorage->ClearById(layer->GetCompositorAnimationsId());
+ }
+ layer->AsHostLayer()->SetLayerManager(aLayerManager);
+ }
+ mAnimStorage = aAnimStorage;
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvShutdown() {
+ Destroy();
+ IProtocol* mgr = Manager();
+ if (!Send__delete__(this)) {
+ return IPC_FAIL_NO_REASON(mgr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvShutdownSync() {
+ return RecvShutdown();
+}
+
+void LayerTransactionParent::Destroy() {
+ if (mDestroyed) {
+ return;
+ }
+ mDestroyed = true;
+ if (mAnimStorage) {
+ for (auto iter = mLayerMap.Iter(); !iter.Done(); iter.Next()) {
+ auto layer = iter.Data();
+ if (layer->GetCompositorAnimationsId()) {
+ mAnimStorage->ClearById(layer->GetCompositorAnimationsId());
+ }
+ layer->Disconnect();
+ }
+ }
+ mCompositables.clear();
+ mAnimStorage = nullptr;
+}
+
+class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender final {
+ public:
+ explicit AutoLayerTransactionParentAsyncMessageSender(
+ LayerTransactionParent* aLayerTransaction,
+ const nsTArray<OpDestroy>* aDestroyActors = nullptr)
+ : mLayerTransaction(aLayerTransaction), mActorsToDestroy(aDestroyActors) {
+ mLayerTransaction->SetAboutToSendAsyncMessages();
+ }
+
+ ~AutoLayerTransactionParentAsyncMessageSender() {
+ mLayerTransaction->SendPendingAsyncMessages();
+ if (mActorsToDestroy) {
+ // Destroy the actors after sending the async messages because the latter
+ // may contain references to some actors.
+ for (const auto& op : *mActorsToDestroy) {
+ mLayerTransaction->DestroyActor(op);
+ }
+ }
+ }
+
+ private:
+ LayerTransactionParent* mLayerTransaction;
+ const nsTArray<OpDestroy>* mActorsToDestroy;
+};
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvPaintTime(
+ const TransactionId& aTransactionId, const TimeDuration& aPaintTime) {
+ mCompositorBridge->UpdatePaintTime(this, aPaintTime);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvUpdate(
+ const TransactionInfo& aInfo) {
+ AUTO_PROFILER_TRACING_MARKER("Paint", "LayerTransaction", GRAPHICS);
+ AUTO_PROFILER_LABEL("LayerTransactionParent::RecvUpdate", GRAPHICS);
+ PerfStats::AutoMetricRecording<PerfStats::Metric::LayerTransactions>
+ autoRecording;
+
+ TimeStamp updateStart = TimeStamp::Now();
+
+ MOZ_LAYERS_LOG(
+ ("[ParentSide] received txn with %zu edits", aInfo.cset().Length()));
+
+ UpdateFwdTransactionId(aInfo.fwdTransactionId());
+
+ if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
+ for (const auto& op : aInfo.toDestroy()) {
+ DestroyActor(op);
+ }
+ return IPC_OK();
+ }
+
+ // This ensures that destroy operations are always processed. It is not safe
+ // to early-return from RecvUpdate without doing so.
+ AutoLayerTransactionParentAsyncMessageSender autoAsyncMessageSender(
+ this, &aInfo.toDestroy());
+
+ {
+ AutoResolveRefLayers resolve(
+ mCompositorBridge->GetCompositionManager(this));
+ nsCString none;
+ mLayerManager->BeginTransaction(none);
+ }
+
+ // Not all edits require an update to the hit testing tree.
+ mUpdateHitTestingTree = false;
+
+ for (EditArray::index_type i = 0; i < aInfo.cset().Length(); ++i) {
+ const Edit& edit = const_cast<Edit&>(aInfo.cset()[i]);
+
+ switch (edit.type()) {
+ // Create* ops
+ case Edit::TOpCreatePaintedLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreatePaintedLayer"));
+
+ RefPtr<PaintedLayer> layer = mLayerManager->CreatePaintedLayer();
+ if (!BindLayer(layer, edit.get_OpCreatePaintedLayer())) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "CreatePaintedLayer");
+ break;
+ }
+ case Edit::TOpCreateContainerLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer"));
+
+ RefPtr<ContainerLayer> layer = mLayerManager->CreateContainerLayer();
+ if (!BindLayer(layer, edit.get_OpCreateContainerLayer())) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "CreateContainerLayer");
+ break;
+ }
+ case Edit::TOpCreateImageLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer"));
+
+ RefPtr<ImageLayer> layer = mLayerManager->CreateImageLayer();
+ if (!BindLayer(layer, edit.get_OpCreateImageLayer())) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "CreateImageLayer");
+ break;
+ }
+ case Edit::TOpCreateColorLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer"));
+
+ RefPtr<ColorLayer> layer = mLayerManager->CreateColorLayer();
+ if (!BindLayer(layer, edit.get_OpCreateColorLayer())) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "CreateColorLayer");
+ break;
+ }
+ case Edit::TOpCreateCanvasLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer"));
+
+ RefPtr<CanvasLayer> layer = mLayerManager->CreateCanvasLayer();
+ if (!BindLayer(layer, edit.get_OpCreateCanvasLayer())) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "CreateCanvasLayer");
+ break;
+ }
+ case Edit::TOpCreateRefLayer: {
+ MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer"));
+
+ RefPtr<RefLayer> layer = mLayerManager->CreateRefLayer();
+ if (!BindLayer(layer, edit.get_OpCreateRefLayer())) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "CreateRefLayer");
+ break;
+ }
+ case Edit::TOpSetDiagnosticTypes: {
+ mLayerManager->SetDiagnosticTypes(
+ edit.get_OpSetDiagnosticTypes().diagnostics());
+ break;
+ }
+ // Tree ops
+ case Edit::TOpSetRoot: {
+ MOZ_LAYERS_LOG(("[ParentSide] SetRoot"));
+
+ Layer* newRoot = AsLayer(edit.get_OpSetRoot().root());
+ if (!newRoot) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ if (newRoot->GetParent()) {
+ // newRoot is not a root!
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mRoot = newRoot;
+
+ UpdateHitTestingTree(mRoot, "SetRoot");
+ break;
+ }
+ case Edit::TOpInsertAfter: {
+ MOZ_LAYERS_LOG(("[ParentSide] InsertAfter"));
+
+ const OpInsertAfter& oia = edit.get_OpInsertAfter();
+ Layer* child = AsLayer(oia.childLayer());
+ Layer* layer = AsLayer(oia.container());
+ Layer* after = AsLayer(oia.after());
+ if (!child || !layer || !after) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (!container || !container->InsertAfter(child, after)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "InsertAfter");
+ break;
+ }
+ case Edit::TOpPrependChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] PrependChild"));
+
+ const OpPrependChild& oac = edit.get_OpPrependChild();
+ Layer* child = AsLayer(oac.childLayer());
+ Layer* layer = AsLayer(oac.container());
+ if (!child || !layer) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (!container || !container->InsertAfter(child, nullptr)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "PrependChild");
+ break;
+ }
+ case Edit::TOpRemoveChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] RemoveChild"));
+
+ const OpRemoveChild& orc = edit.get_OpRemoveChild();
+ Layer* childLayer = AsLayer(orc.childLayer());
+ Layer* layer = AsLayer(orc.container());
+ if (!childLayer || !layer) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (!container || !container->RemoveChild(childLayer)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "RemoveChild");
+ break;
+ }
+ case Edit::TOpRepositionChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] RepositionChild"));
+
+ const OpRepositionChild& orc = edit.get_OpRepositionChild();
+ Layer* child = AsLayer(orc.childLayer());
+ Layer* after = AsLayer(orc.after());
+ Layer* layer = AsLayer(orc.container());
+ if (!child || !layer || !after) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (!container || !container->RepositionChild(child, after)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "RepositionChild");
+ break;
+ }
+ case Edit::TOpRaiseToTopChild: {
+ MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild"));
+
+ const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild();
+ Layer* child = AsLayer(rtc.childLayer());
+ if (!child) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ Layer* layer = AsLayer(rtc.container());
+ if (!layer) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ ContainerLayer* container = layer->AsContainerLayer();
+ if (!container || !container->RepositionChild(child, nullptr)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateHitTestingTree(layer, "RaiseToTopChild");
+ break;
+ }
+ case Edit::TCompositableOperation: {
+ if (!ReceiveCompositableUpdate(edit.get_CompositableOperation())) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ break;
+ }
+ case Edit::TOpAttachCompositable: {
+ const OpAttachCompositable& op = edit.get_OpAttachCompositable();
+ RefPtr<CompositableHost> host = FindCompositable(op.compositable());
+ if (!Attach(AsLayer(op.layer()), host, false)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ host->SetCompositorBridgeID(mLayerManager->GetCompositorBridgeID());
+ break;
+ }
+ case Edit::TOpAttachAsyncCompositable: {
+ const OpAttachAsyncCompositable& op =
+ edit.get_OpAttachAsyncCompositable();
+ RefPtr<ImageBridgeParent> imageBridge =
+ ImageBridgeParent::GetInstance(OtherPid());
+ if (!imageBridge) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ RefPtr<CompositableHost> host = imageBridge->FindCompositable(
+ op.compositable(), /* aAllowDisablingWebRender */ true);
+ if (!host) {
+ // This normally should not happen, but can after a GPU process crash.
+ // Media may not have had time to update the ImageContainer associated
+ // with a video frame, and we may try to attach a stale
+ // CompositableHandle. Rather than break the whole transaction, we
+ // just continue.
+ gfxCriticalNote << "CompositableHost " << op.compositable().Value()
+ << " not found";
+ continue;
+ }
+ if (!Attach(AsLayer(op.layer()), host, true)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ host->SetCompositorBridgeID(mLayerManager->GetCompositorBridgeID());
+ break;
+ }
+ default:
+ MOZ_CRASH("not reached");
+ }
+ }
+
+ // Process simple attribute updates.
+ for (const auto& op : aInfo.setSimpleAttrs()) {
+ MOZ_LAYERS_LOG(("[ParentSide] SetSimpleLayerAttributes"));
+ Layer* layer = AsLayer(op.layer());
+ if (!layer) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ const SimpleLayerAttributes& attrs = op.attrs();
+ const SimpleLayerAttributes& orig = layer->GetSimpleAttributes();
+ if (!attrs.HitTestingInfoIsEqual(orig)) {
+ UpdateHitTestingTree(layer, "scrolling info changed");
+ }
+ layer->SetSimpleAttributes(op.attrs());
+ }
+
+ // Process attribute updates.
+ for (const auto& op : aInfo.setAttrs()) {
+ MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes"));
+ if (!SetLayerAttributes(op)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ }
+
+ // Process paints separately, after all normal edits.
+ for (const auto& op : aInfo.paints()) {
+ if (!ReceiveCompositableUpdate(op)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ }
+
+ mCompositorBridge->ShadowLayersUpdated(this, aInfo, mUpdateHitTestingTree);
+
+ {
+ AutoResolveRefLayers resolve(
+ mCompositorBridge->GetCompositionManager(this));
+ mLayerManager->EndTransaction(TimeStamp(),
+ LayerManager::END_NO_IMMEDIATE_REDRAW);
+ }
+
+ if (!IsSameProcess()) {
+ // Ensure that any pending operations involving back and front
+ // buffers have completed, so that neither process stomps on the
+ // other's buffer contents.
+ LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+ }
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+ int compositeTime =
+ (int)(mozilla::TimeStamp::Now() - updateStart).ToMilliseconds();
+ if (compositeTime > 15) {
+ printf_stderr("Compositor: Layers update took %i ms (blocking gecko).\n",
+ compositeTime);
+ }
+#endif
+
+ // Enable visual warning for long transaction when draw FPS option is enabled
+ bool drawFps = StaticPrefs::layers_acceleration_draw_fps();
+ if (drawFps) {
+ uint32_t visualWarningTrigger =
+ StaticPrefs::layers_transaction_warning_ms();
+ // The default theshold is 200ms to trigger, hit red when it take 4 times
+ // longer
+ TimeDuration latency = TimeStamp::Now() - aInfo.transactionStart();
+ if (latency > TimeDuration::FromMilliseconds(visualWarningTrigger)) {
+ float severity =
+ (latency - TimeDuration::FromMilliseconds(visualWarningTrigger))
+ .ToMilliseconds() /
+ (4 * visualWarningTrigger);
+ if (severity > 1.f) {
+ severity = 1.f;
+ }
+ mLayerManager->VisualFrameWarning(severity);
+ printf_stderr(
+ "LayerTransactionParent::RecvUpdate transaction from process %d took "
+ "%f ms",
+ OtherPid(), latency.ToMilliseconds());
+ }
+
+ mLayerManager->RecordUpdateTime(
+ (TimeStamp::Now() - updateStart).ToMilliseconds());
+ }
+
+ return IPC_OK();
+}
+
+bool LayerTransactionParent::SetLayerAttributes(
+ const OpSetLayerAttributes& aOp) {
+ Layer* layer = AsLayer(aOp.layer());
+ if (!layer) {
+ return false;
+ }
+
+ const LayerAttributes& attrs = aOp.attrs();
+ const CommonLayerAttributes& common = attrs.common();
+ if (common.visibleRegion() != layer->GetVisibleRegion()) {
+ UpdateHitTestingTree(layer, "visible region changed");
+ layer->SetVisibleRegion(common.visibleRegion());
+ }
+ if (common.eventRegions() != layer->GetEventRegions()) {
+ UpdateHitTestingTree(layer, "event regions changed");
+ layer->SetEventRegions(common.eventRegions());
+ }
+ Maybe<ParentLayerIntRect> clipRect =
+ common.useClipRect() ? Some(common.clipRect()) : Nothing();
+ if (clipRect != layer->GetClipRect()) {
+ UpdateHitTestingTree(layer, "clip rect changed");
+ layer->SetClipRect(clipRect);
+ }
+ if (LayerHandle maskLayer = common.maskLayer()) {
+ layer->SetMaskLayer(AsLayer(maskLayer));
+ } else {
+ layer->SetMaskLayer(nullptr);
+ }
+ layer->SetCompositorAnimations(mId, common.compositorAnimations());
+ // Clean up the Animations by id in the CompositorAnimationStorage
+ // if there are no active animations on the layer
+ if (mAnimStorage && layer->GetCompositorAnimationsId() &&
+ layer->GetPropertyAnimationGroups().IsEmpty()) {
+ mAnimStorage->ClearById(layer->GetCompositorAnimationsId());
+ }
+ if (common.scrollMetadata() != layer->GetAllScrollMetadata()) {
+ UpdateHitTestingTree(layer, "scroll metadata changed");
+ layer->SetScrollMetadata(common.scrollMetadata());
+ }
+ layer->SetDisplayListLog(common.displayListLog().get());
+
+ // The updated invalid region is added to the existing one, since we can
+ // update multiple times before the next composite.
+ layer->AddInvalidRegion(common.invalidRegion());
+
+ nsTArray<RefPtr<Layer>> maskLayers;
+ for (size_t i = 0; i < common.ancestorMaskLayers().Length(); i++) {
+ Layer* maskLayer = AsLayer(common.ancestorMaskLayers().ElementAt(i));
+ if (!maskLayer) {
+ return false;
+ }
+ maskLayers.AppendElement(maskLayer);
+ }
+ layer->SetAncestorMaskLayers(maskLayers);
+
+ typedef SpecificLayerAttributes Specific;
+ const SpecificLayerAttributes& specific = attrs.specific();
+ switch (specific.type()) {
+ case Specific::Tnull_t:
+ break;
+
+ case Specific::TPaintedLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] painted layer"));
+
+ PaintedLayer* paintedLayer = layer->AsPaintedLayer();
+ if (!paintedLayer) {
+ return false;
+ }
+ const PaintedLayerAttributes& attrs =
+ specific.get_PaintedLayerAttributes();
+
+ paintedLayer->SetValidRegion(attrs.validRegion());
+ break;
+ }
+ case Specific::TContainerLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] container layer"));
+
+ ContainerLayer* containerLayer = layer->AsContainerLayer();
+ if (!containerLayer) {
+ return false;
+ }
+ const ContainerLayerAttributes& attrs =
+ specific.get_ContainerLayerAttributes();
+ containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale());
+ containerLayer->SetInheritedScale(attrs.inheritedXScale(),
+ attrs.inheritedYScale());
+ containerLayer->SetScaleToResolution(attrs.presShellResolution());
+ break;
+ }
+ case Specific::TColorLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] color layer"));
+
+ ColorLayer* colorLayer = layer->AsColorLayer();
+ if (!colorLayer) {
+ return false;
+ }
+ colorLayer->SetColor(specific.get_ColorLayerAttributes().color().value());
+ colorLayer->SetBounds(specific.get_ColorLayerAttributes().bounds());
+ break;
+ }
+ case Specific::TCanvasLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] canvas layer"));
+
+ CanvasLayer* canvasLayer = layer->AsCanvasLayer();
+ if (!canvasLayer) {
+ return false;
+ }
+ canvasLayer->SetSamplingFilter(
+ specific.get_CanvasLayerAttributes().samplingFilter());
+ canvasLayer->SetBounds(specific.get_CanvasLayerAttributes().bounds());
+ break;
+ }
+ case Specific::TRefLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] ref layer"));
+
+ RefLayer* refLayer = layer->AsRefLayer();
+ if (!refLayer) {
+ return false;
+ }
+ refLayer->SetReferentId(specific.get_RefLayerAttributes().id());
+ refLayer->SetEventRegionsOverride(
+ specific.get_RefLayerAttributes().eventRegionsOverride());
+ refLayer->SetRemoteDocumentSize(
+ specific.get_RefLayerAttributes().remoteDocumentSize());
+ UpdateHitTestingTree(layer, "ref layer attributes changed");
+ break;
+ }
+ case Specific::TImageLayerAttributes: {
+ MOZ_LAYERS_LOG(("[ParentSide] image layer"));
+
+ ImageLayer* imageLayer = layer->AsImageLayer();
+ if (!imageLayer) {
+ return false;
+ }
+ const ImageLayerAttributes& attrs = specific.get_ImageLayerAttributes();
+ imageLayer->SetSamplingFilter(attrs.samplingFilter());
+ imageLayer->SetScaleToSize(attrs.scaleToSize(), attrs.scaleMode());
+ break;
+ }
+ default:
+ MOZ_CRASH("not reached");
+ }
+
+ return true;
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvSetLayersObserverEpoch(
+ const LayersObserverEpoch& aChildEpoch) {
+ mChildEpoch = aChildEpoch;
+ return IPC_OK();
+}
+
+bool LayerTransactionParent::ShouldParentObserveEpoch() {
+ if (mParentEpoch == mChildEpoch) {
+ return false;
+ }
+
+ mParentEpoch = mChildEpoch;
+ return true;
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvSetTestSampleTime(
+ const TimeStamp& aTime) {
+ if (!mCompositorBridge->SetTestSampleTime(GetId(), aTime)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvLeaveTestMode() {
+ if (mDestroyed) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mCompositorBridge->LeaveTestMode(GetId());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvGetAnimationValue(
+ const uint64_t& aCompositorAnimationsId, OMTAValue* aValue) {
+ if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ // Make sure we apply the latest animation style or else we can end up with
+ // a race between when we temporarily clear the animation transform (in
+ // CompositorBridgeParent::SetShadowProperties) and when animation
+ // recalculates the value.
+ mCompositorBridge->ApplyAsyncProperties(
+ this, CompositorBridgeParentBase::TransformsToSkip::APZ);
+
+ if (!mAnimStorage) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ *aValue = mAnimStorage->GetOMTAValue(aCompositorAnimationsId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvGetTransform(
+ const LayerHandle& aLayerHandle, Maybe<Matrix4x4>* aTransform) {
+ if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ Layer* layer = AsLayer(aLayerHandle);
+ if (!layer) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mCompositorBridge->ApplyAsyncProperties(
+ this, CompositorBridgeParentBase::TransformsToSkip::NoneOfThem);
+
+ Matrix4x4 transform = layer->AsHostLayer()->GetShadowBaseTransform();
+ // Undo the scale transform applied by FrameTransformToTransformInDevice in
+ // AsyncCompositionManager.cpp.
+ if (ContainerLayer* c = layer->AsContainerLayer()) {
+ transform.PostScale(1.0f / c->GetInheritedXScale(),
+ 1.0f / c->GetInheritedYScale(), 1.0f);
+ }
+ float scale = 1;
+ Point3D scaledOrigin;
+ if (layer->GetTransformData()) {
+ const TransformData& data = *layer->GetTransformData();
+ scale = data.appUnitsPerDevPixel();
+ scaledOrigin = Point3D(
+ NS_round(NSAppUnitsToFloatPixels(data.origin().x, scale)),
+ NS_round(NSAppUnitsToFloatPixels(data.origin().y, scale)), 0.0f);
+ }
+
+ // If our parent isn't a perspective layer, then the offset into reference
+ // frame coordinates will have been applied to us. Add an inverse translation
+ // to cancel it out.
+ if (!layer->GetParent() || !layer->GetParent()->GetTransformIsPerspective()) {
+ transform.PostTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
+ }
+
+ *aTransform = Some(transform);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvSetAsyncScrollOffset(
+ const ScrollableLayerGuid::ViewID& aScrollID, const float& aX,
+ const float& aY) {
+ if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mCompositorBridge->SetTestAsyncScrollOffset(GetId(), aScrollID,
+ CSSPoint(aX, aY));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvSetAsyncZoom(
+ const ScrollableLayerGuid::ViewID& aScrollID, const float& aValue) {
+ if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mCompositorBridge->SetTestAsyncZoom(GetId(), aScrollID,
+ LayerToParentLayerScale(aValue));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvFlushApzRepaints() {
+ mCompositorBridge->FlushApzRepaints(GetId());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvGetAPZTestData(
+ APZTestData* aOutData) {
+ mCompositorBridge->GetAPZTestData(GetId(), aOutData);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvGetFrameUniformity(
+ FrameUniformityData* aOutData) {
+ mCompositorBridge->GetFrameUniformity(GetId(), aOutData);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvRequestProperty(
+ const nsString& aProperty, float* aValue) {
+ *aValue = -1;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvSetConfirmedTargetAPZC(
+ const uint64_t& aBlockId, nsTArray<ScrollableLayerGuid>&& aTargets) {
+ for (size_t i = 0; i < aTargets.Length(); i++) {
+ // Guard against bad data from hijacked child processes
+ if (aTargets[i].mLayersId != GetId()) {
+ NS_ERROR(
+ "Unexpected layers id in RecvSetConfirmedTargetAPZC; dropping "
+ "message...");
+ return IPC_FAIL(this, "Bad layers id");
+ }
+ }
+ mCompositorBridge->SetConfirmedTargetAPZC(GetId(), aBlockId,
+ std::move(aTargets));
+ return IPC_OK();
+}
+
+bool LayerTransactionParent::Attach(Layer* aLayer,
+ CompositableHost* aCompositable,
+ bool aIsAsync) {
+ if (!aCompositable || !aLayer) {
+ return false;
+ }
+
+ HostLayer* layer = aLayer->AsHostLayer();
+ if (!layer) {
+ return false;
+ }
+
+ TextureSourceProvider* provider =
+ static_cast<HostLayerManager*>(aLayer->Manager())
+ ->GetTextureSourceProvider();
+
+ MOZ_ASSERT(!aCompositable->AsWebRenderImageHost());
+ if (aCompositable->AsWebRenderImageHost()) {
+ gfxCriticalNote << "Use WebRenderImageHost at LayerTransactionParent.";
+ }
+ if (!layer->SetCompositableHost(aCompositable)) {
+ // not all layer types accept a compositable, see bug 967824
+ return false;
+ }
+ aCompositable->Attach(aLayer, provider,
+ aIsAsync ? CompositableHost::ALLOW_REATTACH |
+ CompositableHost::KEEP_ATTACHED
+ : CompositableHost::NO_FLAGS);
+ return true;
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvClearCachedResources() {
+ if (mRoot) {
+ // NB: |mRoot| here is the *child* context's root. In this parent
+ // context, it's just a subtree root. We need to scope the clear
+ // of resources to exactly that subtree, so we specify it here.
+ mLayerManager->ClearCachedResources(mRoot);
+ }
+ mCompositorBridge->NotifyClearCachedResources(this);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvScheduleComposite() {
+ mCompositorBridge->ScheduleComposite(this);
+ return IPC_OK();
+}
+
+void LayerTransactionParent::ActorDestroy(ActorDestroyReason why) { Destroy(); }
+
+bool LayerTransactionParent::AllocShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ if (!mIPCOpen || mDestroyed) {
+ return false;
+ }
+ return PLayerTransactionParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool LayerTransactionParent::AllocUnsafeShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ if (!mIPCOpen || mDestroyed) {
+ return false;
+ }
+
+ return PLayerTransactionParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool LayerTransactionParent::DeallocShmem(ipc::Shmem& aShmem) {
+ if (!mIPCOpen || mDestroyed) {
+ return false;
+ }
+ return PLayerTransactionParent::DeallocShmem(aShmem);
+}
+
+bool LayerTransactionParent::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void LayerTransactionParent::SetPendingTransactionId(
+ TransactionId aId, const VsyncId& aVsyncId,
+ const TimeStamp& aVsyncStartTime, const TimeStamp& aRefreshStartTime,
+ const TimeStamp& aTxnStartTime, const TimeStamp& aTxnEndTime,
+ bool aContainsSVG, const nsCString& aURL, const TimeStamp& aFwdTime) {
+ mPendingTransactions.AppendElement(PendingTransaction{
+ aId, aVsyncId, aVsyncStartTime, aRefreshStartTime, aTxnStartTime,
+ aTxnEndTime, aFwdTime, aURL, aContainsSVG});
+}
+
+TransactionId LayerTransactionParent::FlushTransactionId(
+ const VsyncId& aCompositeId, TimeStamp& aCompositeEnd) {
+ TransactionId id;
+ for (auto& transaction : mPendingTransactions) {
+ id = transaction.mId;
+ if (mId.IsValid() && transaction.mId.IsValid() && !mVsyncRate.IsZero()) {
+ RecordContentFrameTime(
+ transaction.mTxnVsyncId, transaction.mVsyncStartTime,
+ transaction.mTxnStartTime, aCompositeId, aCompositeEnd,
+ transaction.mTxnEndTime - transaction.mTxnStartTime, mVsyncRate,
+ transaction.mContainsSVG, false);
+ }
+
+#if defined(ENABLE_FRAME_LATENCY_LOG)
+ if (transaction.mId.IsValid()) {
+ if (transaction.mRefreshStartTime) {
+ int32_t latencyMs = lround(
+ (aCompositeEnd - transaction.mRefreshStartTime).ToMilliseconds());
+ printf_stderr(
+ "From transaction start to end of generate frame latencyMs %d this "
+ "%p\n",
+ latencyMs, this);
+ }
+ if (transaction.mFwdTime) {
+ int32_t latencyMs =
+ lround((aCompositeEnd - transaction.mFwdTime).ToMilliseconds());
+ printf_stderr(
+ "From forwarding transaction to end of generate frame latencyMs %d "
+ "this %p\n",
+ latencyMs, this);
+ }
+ }
+#endif
+ }
+
+ mPendingTransactions.Clear();
+ return id;
+}
+
+void LayerTransactionParent::SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+void LayerTransactionParent::SendPendingAsyncMessages() {
+ mCompositorBridge->SendPendingAsyncMessages();
+}
+
+void LayerTransactionParent::SetAboutToSendAsyncMessages() {
+ mCompositorBridge->SetAboutToSendAsyncMessages();
+}
+
+void LayerTransactionParent::NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+bool LayerTransactionParent::BindLayerToHandle(RefPtr<Layer> aLayer,
+ const LayerHandle& aHandle) {
+ if (!aHandle || !aLayer) {
+ return false;
+ }
+ auto entry = mLayerMap.LookupForAdd(aHandle.Value());
+ if (entry) {
+ return false;
+ }
+ entry.OrInsert([&aLayer]() { return aLayer; });
+ return true;
+}
+
+Layer* LayerTransactionParent::AsLayer(const LayerHandle& aHandle) {
+ if (!aHandle) {
+ return nullptr;
+ }
+ return mLayerMap.GetWeak(aHandle.Value());
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvNewCompositable(
+ const CompositableHandle& aHandle, const TextureInfo& aInfo) {
+ if (!AddCompositable(aHandle, aInfo, /* aUseWebRender */ false)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvReleaseLayer(
+ const LayerHandle& aHandle) {
+ RefPtr<Layer> layer;
+ if (!aHandle || !mLayerMap.Remove(aHandle.Value(), getter_AddRefs(layer))) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ if (mAnimStorage && layer->GetCompositorAnimationsId()) {
+ mAnimStorage->ClearById(layer->GetCompositorAnimationsId());
+ layer->ClearCompositorAnimations();
+ }
+ layer->Disconnect();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvReleaseCompositable(
+ const CompositableHandle& aHandle) {
+ ReleaseCompositable(aHandle);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvRecordPaintTimes(
+ const PaintTiming& aTiming) {
+ // Currently we only add paint timings for remote layers. In the future
+ // we could be smarter and use paint timings from the UI process, either
+ // as a separate overlay or if no remote layers are attached.
+ if (mLayerManager && mCompositorBridge->IsRemote()) {
+ mLayerManager->RecordPaintTimes(aTiming);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult LayerTransactionParent::RecvGetTextureFactoryIdentifier(
+ TextureFactoryIdentifier* aIdentifier) {
+ if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
+ // Default constructor sets mParentBackend to LAYERS_NONE.
+ return IPC_OK();
+ }
+
+ *aIdentifier = mLayerManager->GetTextureFactoryIdentifier();
+ return IPC_OK();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/LayerTransactionParent.h b/gfx/layers/ipc/LayerTransactionParent.h
new file mode 100644
index 0000000000..73850a5e6b
--- /dev/null
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H
+#define MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t, uint32_t
+#include "CompositableTransactionParent.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/PLayerTransactionParent.h"
+#include "nsRefPtrHashtable.h"
+#include "nsTArrayForwardDeclare.h" // for nsTArray
+
+namespace mozilla {
+
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+class Layer;
+class HostLayerManager;
+class ShadowLayerParent;
+class CompositableParent;
+class CompositorAnimationStorage;
+class CompositorBridgeParentBase;
+
+class LayerTransactionParent final : public PLayerTransactionParent,
+ public CompositableParentManager,
+ public mozilla::ipc::IShmemAllocator {
+ typedef nsTArray<Edit> EditArray;
+ typedef nsTArray<OpDestroy> OpDestroyArray;
+ typedef nsTArray<PluginWindowData> PluginsArray;
+
+ friend class PLayerTransactionParent;
+
+ public:
+ LayerTransactionParent(HostLayerManager* aManager,
+ CompositorBridgeParentBase* aBridge,
+ CompositorAnimationStorage* aAnimStorage, LayersId aId,
+ TimeDuration aVsyncRate);
+
+ protected:
+ virtual ~LayerTransactionParent();
+
+ public:
+ void Destroy();
+
+ void SetLayerManager(HostLayerManager* aLayerManager,
+ CompositorAnimationStorage* aAnimStorage);
+
+ LayersId GetId() const { return mId; }
+ Layer* GetRoot() const { return mRoot; }
+
+ LayersObserverEpoch GetChildEpoch() const { return mChildEpoch; }
+ bool ShouldParentObserveEpoch();
+
+ IShmemAllocator* AsShmemAllocator() override { return this; }
+
+ bool AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ bool AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ bool DeallocShmem(ipc::Shmem& aShmem) override;
+
+ bool IsSameProcess() const override;
+
+ void SetPendingTransactionId(TransactionId aId, const VsyncId& aVsyncId,
+ const TimeStamp& aVsyncStartTime,
+ const TimeStamp& aRefreshStartTime,
+ const TimeStamp& aTxnStartTime,
+ const TimeStamp& aTxnEndTime, bool aContainsSVG,
+ const nsCString& aURL,
+ const TimeStamp& aFwdTime);
+ TransactionId FlushTransactionId(const VsyncId& aId,
+ TimeStamp& aCompositeEnd);
+
+ // CompositableParentManager
+ void SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) override;
+
+ void SendPendingAsyncMessages() override;
+
+ void SetAboutToSendAsyncMessages() override;
+
+ void NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) override;
+
+ base::ProcessId GetChildProcessId() override { return OtherPid(); }
+
+ protected:
+ mozilla::ipc::IPCResult RecvShutdown();
+ mozilla::ipc::IPCResult RecvShutdownSync();
+
+ mozilla::ipc::IPCResult RecvPaintTime(const TransactionId& aTransactionId,
+ const TimeDuration& aPaintTime);
+
+ mozilla::ipc::IPCResult RecvUpdate(const TransactionInfo& aInfo);
+
+ mozilla::ipc::IPCResult RecvSetLayersObserverEpoch(
+ const LayersObserverEpoch& aChildEpoch);
+ mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
+ const TextureInfo& aInfo);
+ mozilla::ipc::IPCResult RecvReleaseLayer(const LayerHandle& aHandle);
+ mozilla::ipc::IPCResult RecvReleaseCompositable(
+ const CompositableHandle& aHandle);
+
+ mozilla::ipc::IPCResult RecvClearCachedResources();
+ mozilla::ipc::IPCResult RecvScheduleComposite();
+ mozilla::ipc::IPCResult RecvSetTestSampleTime(const TimeStamp& aTime);
+ mozilla::ipc::IPCResult RecvLeaveTestMode();
+ mozilla::ipc::IPCResult RecvGetAnimationValue(
+ const uint64_t& aCompositorAnimationsId, OMTAValue* aValue);
+ mozilla::ipc::IPCResult RecvGetTransform(const LayerHandle& aHandle,
+ Maybe<Matrix4x4>* aTransform);
+ mozilla::ipc::IPCResult RecvSetAsyncScrollOffset(
+ const ScrollableLayerGuid::ViewID& aId, const float& aX, const float& aY);
+ mozilla::ipc::IPCResult RecvSetAsyncZoom(
+ const ScrollableLayerGuid::ViewID& aId, const float& aValue);
+ mozilla::ipc::IPCResult RecvFlushApzRepaints();
+ mozilla::ipc::IPCResult RecvGetAPZTestData(APZTestData* aOutData);
+ mozilla::ipc::IPCResult RecvGetFrameUniformity(FrameUniformityData* aOutData);
+ mozilla::ipc::IPCResult RecvRequestProperty(const nsString& aProperty,
+ float* aValue);
+ mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC(
+ const uint64_t& aBlockId, nsTArray<ScrollableLayerGuid>&& aTargets);
+ mozilla::ipc::IPCResult RecvRecordPaintTimes(const PaintTiming& aTiming);
+ mozilla::ipc::IPCResult RecvGetTextureFactoryIdentifier(
+ TextureFactoryIdentifier* aIdentifier);
+
+ bool SetLayerAttributes(const OpSetLayerAttributes& aOp);
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ template <typename T>
+ bool BindLayer(const RefPtr<Layer>& aLayer, const T& aCreateOp) {
+ return BindLayerToHandle(aLayer, aCreateOp.layer());
+ }
+
+ bool BindLayerToHandle(RefPtr<Layer> aLayer, const LayerHandle& aHandle);
+
+ Layer* AsLayer(const LayerHandle& aLayer);
+
+ bool Attach(Layer* aLayer, CompositableHost* aCompositable,
+ bool aIsAsyncVideo);
+
+ void AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+ }
+ void ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == true);
+ mIPCOpen = false;
+ Release();
+ }
+ friend class CompositorBridgeParent;
+ friend class ContentCompositorBridgeParent;
+
+ private:
+ // This is a function so we can log or breakpoint on why hit
+ // testing tree changes are made.
+ void UpdateHitTestingTree(Layer* aLayer, const char* aWhy) {
+ mUpdateHitTestingTree = true;
+ }
+
+ private:
+ RefPtr<HostLayerManager> mLayerManager;
+ CompositorBridgeParentBase* mCompositorBridge;
+ RefPtr<CompositorAnimationStorage> mAnimStorage;
+
+ // Hold the root because it might be grafted under various
+ // containers in the "real" layer tree
+ RefPtr<Layer> mRoot;
+
+ // Mapping from LayerHandles to Layers.
+ nsRefPtrHashtable<nsUint64HashKey, Layer> mLayerMap;
+
+ LayersId mId;
+
+ // These fields keep track of the latest epoch values in the child and the
+ // parent. mChildEpoch is the latest epoch value received from the child.
+ // mParentEpoch is the latest epoch value that we have told BrowserParent
+ // about (via ObserveLayerUpdate).
+ LayersObserverEpoch mChildEpoch;
+ LayersObserverEpoch mParentEpoch;
+
+ TimeDuration mVsyncRate;
+
+ struct PendingTransaction {
+ TransactionId mId;
+ VsyncId mTxnVsyncId;
+ TimeStamp mVsyncStartTime;
+ TimeStamp mRefreshStartTime;
+ TimeStamp mTxnStartTime;
+ TimeStamp mTxnEndTime;
+ TimeStamp mFwdTime;
+ nsCString mTxnURL;
+ bool mContainsSVG;
+ };
+ AutoTArray<PendingTransaction, 2> mPendingTransactions;
+
+ // When the widget/frame/browser stuff in this process begins its
+ // destruction process, we need to Disconnect() all the currently
+ // live shadow layers, because some of them might be orphaned from
+ // the layer tree. This happens in Destroy() above. After we
+ // Destroy() ourself, there's a window in which that information
+ // hasn't yet propagated back to the child side and it might still
+ // send us layer transactions. We want to ignore those transactions
+ // because they refer to "zombie layers" on this side. So, we track
+ // that state with |mDestroyed|. This is similar to, but separate
+ // from, |mLayerManager->IsDestroyed()|; we might have had Destroy()
+ // called on us but the mLayerManager might not be destroyed, or
+ // vice versa. In both cases though, we want to ignore shadow-layer
+ // transactions posted by the child.
+
+ bool mDestroyed;
+ bool mIPCOpen;
+
+ // This is set during RecvUpdate to track whether we'll need to update
+ // APZ's hit test regions.
+ bool mUpdateHitTestingTree;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_LAYERS_LAYERTRANSACTIONPARENT_H
diff --git a/gfx/layers/ipc/LayerTreeOwnerTracker.cpp b/gfx/layers/ipc/LayerTreeOwnerTracker.cpp
new file mode 100644
index 0000000000..ab3551ad29
--- /dev/null
+++ b/gfx/layers/ipc/LayerTreeOwnerTracker.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTreeOwnerTracker.h"
+
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/gfx/GPUChild.h" // for GPUChild
+#include "mozilla/gfx/GPUProcessManager.h" // for GPUProcessManager
+
+#include <functional>
+#include <utility> // for std::make_pair
+
+namespace mozilla {
+namespace layers {
+
+static StaticAutoPtr<LayerTreeOwnerTracker> sSingleton;
+
+LayerTreeOwnerTracker::LayerTreeOwnerTracker()
+ : mLayerIdsLock("LayerTreeOwnerTrackerLock") {}
+
+void LayerTreeOwnerTracker::Initialize() {
+ MOZ_ASSERT(!sSingleton);
+ sSingleton = new LayerTreeOwnerTracker();
+}
+
+void LayerTreeOwnerTracker::Shutdown() { sSingleton = nullptr; }
+
+LayerTreeOwnerTracker* LayerTreeOwnerTracker::Get() { return sSingleton; }
+
+void LayerTreeOwnerTracker::Map(LayersId aLayersId,
+ base::ProcessId aProcessId) {
+ MutexAutoLock lock(mLayerIdsLock);
+
+ // Add the mapping to the list
+ mLayerIds[aLayersId] = aProcessId;
+}
+
+void LayerTreeOwnerTracker::Unmap(LayersId aLayersId,
+ base::ProcessId aProcessId) {
+ MutexAutoLock lock(mLayerIdsLock);
+
+ MOZ_ASSERT(mLayerIds[aLayersId] == aProcessId);
+ mLayerIds.erase(aLayersId);
+}
+
+bool LayerTreeOwnerTracker::IsMapped(LayersId aLayersId,
+ base::ProcessId aProcessId) {
+ MutexAutoLock lock(mLayerIdsLock);
+
+ auto iter = mLayerIds.find(aLayersId);
+ return iter != mLayerIds.end() && iter->second == aProcessId;
+}
+
+void LayerTreeOwnerTracker::Iterate(
+ const std::function<void(LayersId aLayersId, base::ProcessId aProcessId)>&
+ aCallback) {
+ MutexAutoLock lock(mLayerIdsLock);
+
+ for (const auto& iter : mLayerIds) {
+ aCallback(iter.first, iter.second);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/LayerTreeOwnerTracker.h b/gfx/layers/ipc/LayerTreeOwnerTracker.h
new file mode 100644
index 0000000000..a3454f2505
--- /dev/null
+++ b/gfx/layers/ipc/LayerTreeOwnerTracker.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_LayerTreeOwnerTracker_h
+#define mozilla_layers_LayerTreeOwnerTracker_h
+
+#include "base/process.h" // for base::ProcessId
+#include "LayersTypes.h" // for LayersId
+#include "mozilla/Mutex.h" // for mozilla::Mutex
+
+#include <functional>
+#include <map>
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+}
+
+namespace layers {
+
+/**
+ * A utility class for tracking which content processes should be allowed
+ * to access which layer trees.
+ *
+ * ProcessId's are used to track which content process can access the layer
+ * tree, and in the case of nested browser's we use the top level content
+ * processes' ProcessId.
+ *
+ * This class is only available in the main process and gpu process. Mappings
+ * are synced from main process to the gpu process. The actual syncing happens
+ * in GPUProcessManager, and so this class should not be used directly.
+ */
+class LayerTreeOwnerTracker final {
+ public:
+ static void Initialize();
+ static void Shutdown();
+ static LayerTreeOwnerTracker* Get();
+
+ /**
+ * Map aLayersId and aProcessId together so that that process
+ * can access that layer tree.
+ */
+ void Map(LayersId aLayersId, base::ProcessId aProcessId);
+
+ /**
+ * Remove an existing mapping.
+ */
+ void Unmap(LayersId aLayersId, base::ProcessId aProcessId);
+
+ /**
+ * Checks whether it is okay for aProcessId to access aLayersId.
+ */
+ bool IsMapped(LayersId aLayersId, base::ProcessId aProcessId);
+
+ void Iterate(
+ const std::function<void(LayersId aLayersId, base::ProcessId aProcessId)>&
+ aCallback);
+
+ private:
+ LayerTreeOwnerTracker();
+
+ mozilla::Mutex mLayerIdsLock;
+ std::map<LayersId, base::ProcessId> mLayerIds;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_LayerTreeOwnerTracker_h
diff --git a/gfx/layers/ipc/LayersMessageUtils.h b/gfx/layers/ipc/LayersMessageUtils.h
new file mode 100644
index 0000000000..1bef016108
--- /dev/null
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -0,0 +1,980 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_LayersMessageUtils
+#define mozilla_layers_LayersMessageUtils
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "FrameMetrics.h"
+#include "VsyncSource.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "ipc/EnumSerializer.h"
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/MotionPathUtils.h"
+#include "mozilla/ServoBindings.h"
+#include "mozilla/ipc/ByteBuf.h"
+#include "mozilla/layers/APZInputBridge.h"
+#include "mozilla/layers/AsyncDragMetrics.h"
+#include "mozilla/layers/CompositorOptions.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/FocusTarget.h"
+#include "mozilla/layers/GeckoContentControllerTypes.h"
+#include "mozilla/layers/KeyboardMap.h"
+#include "mozilla/layers/LayerAttributes.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/MatrixMessage.h"
+#include "mozilla/layers/RepaintRequest.h"
+#include "nsSize.h"
+
+// For ParamTraits, could be moved to cpp file
+#include "ipc/nsGUIEventIPC.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "mozilla/ipc/ByteBufUtils.h"
+
+#ifdef _MSC_VER
+# pragma warning(disable : 4800)
+#endif
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::LayersId>
+ : public PlainOldDataSerializer<mozilla::layers::LayersId> {};
+
+template <typename T>
+struct ParamTraits<mozilla::layers::BaseTransactionId<T>>
+ : public PlainOldDataSerializer<mozilla::layers::BaseTransactionId<T>> {};
+
+template <>
+struct ParamTraits<mozilla::VsyncId>
+ : public PlainOldDataSerializer<mozilla::VsyncId> {};
+
+template <>
+struct ParamTraits<mozilla::VsyncEvent> {
+ typedef mozilla::VsyncEvent paramType;
+
+ static void Write(Message* msg, const paramType& param) {
+ WriteParam(msg, param.mId);
+ WriteParam(msg, param.mTime);
+ WriteParam(msg, param.mOutputTime);
+ }
+ static bool Read(const Message* msg, PickleIterator* iter,
+ paramType* result) {
+ return ReadParam(msg, iter, &result->mId) &&
+ ReadParam(msg, iter, &result->mTime) &&
+ ReadParam(msg, iter, &result->mOutputTime);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::MatrixMessage> {
+ typedef mozilla::layers::MatrixMessage paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mMatrix);
+ WriteParam(aMsg, aParam.mTopLevelViewportVisibleRectInBrowserCoords);
+ WriteParam(aMsg, aParam.mLayersId);
+ }
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mMatrix) &&
+ ReadParam(aMsg, aIter,
+ &aResult->mTopLevelViewportVisibleRectInBrowserCoords) &&
+ ReadParam(aMsg, aIter, &aResult->mLayersId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::LayersObserverEpoch>
+ : public PlainOldDataSerializer<mozilla::layers::LayersObserverEpoch> {};
+
+template <>
+struct ParamTraits<mozilla::layers::WindowKind>
+ : public ContiguousEnumSerializer<mozilla::layers::WindowKind,
+ mozilla::layers::WindowKind::MAIN,
+ mozilla::layers::WindowKind::LAST> {};
+
+template <>
+struct ParamTraits<mozilla::layers::LayersBackend>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::LayersBackend,
+ mozilla::layers::LayersBackend::LAYERS_NONE,
+ mozilla::layers::LayersBackend::LAYERS_LAST> {};
+
+template <>
+struct ParamTraits<mozilla::layers::WebRenderBackend>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::WebRenderBackend,
+ mozilla::layers::WebRenderBackend::HARDWARE,
+ mozilla::layers::WebRenderBackend::LAST> {};
+
+template <>
+struct ParamTraits<mozilla::layers::WebRenderCompositor>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::WebRenderCompositor,
+ mozilla::layers::WebRenderCompositor::DRAW,
+ mozilla::layers::WebRenderCompositor::LAST> {};
+
+template <>
+struct ParamTraits<mozilla::layers::TextureType>
+ : public ContiguousEnumSerializer<mozilla::layers::TextureType,
+ mozilla::layers::TextureType::Unknown,
+ mozilla::layers::TextureType::Last> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScaleMode>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::ScaleMode, mozilla::layers::ScaleMode::SCALE_NONE,
+ mozilla::layers::kHighestScaleMode> {};
+
+template <>
+struct ParamTraits<mozilla::StyleScrollSnapStrictness>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::StyleScrollSnapStrictness,
+ mozilla::StyleScrollSnapStrictness::None,
+ mozilla::StyleScrollSnapStrictness::Proximity> {};
+
+template <>
+struct ParamTraits<mozilla::layers::TextureFlags>
+ : public BitFlagsEnumSerializer<mozilla::layers::TextureFlags,
+ mozilla::layers::TextureFlags::ALL_BITS> {};
+
+template <>
+struct ParamTraits<mozilla::layers::DiagnosticTypes>
+ : public BitFlagsEnumSerializer<
+ mozilla::layers::DiagnosticTypes,
+ mozilla::layers::DiagnosticTypes::ALL_BITS> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollDirection>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::ScrollDirection,
+ mozilla::layers::ScrollDirection::eVertical,
+ mozilla::layers::kHighestScrollDirection> {};
+
+template <>
+struct ParamTraits<mozilla::layers::FrameMetrics::ScrollOffsetUpdateType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::FrameMetrics::ScrollOffsetUpdateType,
+ mozilla::layers::FrameMetrics::ScrollOffsetUpdateType::eNone,
+ mozilla::layers::FrameMetrics::sHighestScrollOffsetUpdateType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::RepaintRequest::ScrollOffsetUpdateType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::RepaintRequest::ScrollOffsetUpdateType,
+ mozilla::layers::RepaintRequest::ScrollOffsetUpdateType::eNone,
+ mozilla::layers::RepaintRequest::sHighestScrollOffsetUpdateType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::OverscrollBehavior>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::OverscrollBehavior,
+ mozilla::layers::OverscrollBehavior::Auto,
+ mozilla::layers::kHighestOverscrollBehavior> {};
+
+template <>
+struct ParamTraits<mozilla::layers::LayerHandle> {
+ typedef mozilla::layers::LayerHandle paramType;
+
+ static void Write(Message* msg, const paramType& param) {
+ WriteParam(msg, param.mHandle);
+ }
+ static bool Read(const Message* msg, PickleIterator* iter,
+ paramType* result) {
+ return ReadParam(msg, iter, &result->mHandle);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositableHandle> {
+ typedef mozilla::layers::CompositableHandle paramType;
+
+ static void Write(Message* msg, const paramType& param) {
+ WriteParam(msg, param.mHandle);
+ }
+ static bool Read(const Message* msg, PickleIterator* iter,
+ paramType* result) {
+ return ReadParam(msg, iter, &result->mHandle);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::FrameMetrics>
+ : BitfieldHelper<mozilla::layers::FrameMetrics> {
+ typedef mozilla::layers::FrameMetrics paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mScrollId);
+ WriteParam(aMsg, aParam.mPresShellResolution);
+ WriteParam(aMsg, aParam.mCompositionBounds);
+ WriteParam(aMsg, aParam.mDisplayPort);
+ WriteParam(aMsg, aParam.mCriticalDisplayPort);
+ WriteParam(aMsg, aParam.mScrollableRect);
+ WriteParam(aMsg, aParam.mCumulativeResolution);
+ WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
+ WriteParam(aMsg, aParam.mScrollOffset);
+ WriteParam(aMsg, aParam.mZoom);
+ WriteParam(aMsg, aParam.mScrollGeneration);
+ WriteParam(aMsg, aParam.mRootCompositionSize);
+ WriteParam(aMsg, aParam.mDisplayPortMargins);
+ WriteParam(aMsg, aParam.mPresShellId);
+ WriteParam(aMsg, aParam.mLayoutViewport);
+ WriteParam(aMsg, aParam.mExtraResolution);
+ WriteParam(aMsg, aParam.mPaintRequestTime);
+ WriteParam(aMsg, aParam.mVisualDestination);
+ WriteParam(aMsg, aParam.mVisualScrollUpdateType);
+ WriteParam(aMsg, aParam.mFixedLayerMargins);
+ WriteParam(aMsg, aParam.mCompositionSizeWithoutDynamicToolbar);
+ WriteParam(aMsg, aParam.mIsRootContent);
+ WriteParam(aMsg, aParam.mIsScrollInfoLayer);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mScrollId) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellResolution) &&
+ ReadParam(aMsg, aIter, &aResult->mCompositionBounds) &&
+ ReadParam(aMsg, aIter, &aResult->mDisplayPort) &&
+ ReadParam(aMsg, aIter, &aResult->mCriticalDisplayPort) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollableRect) &&
+ ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) &&
+ ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollOffset) &&
+ ReadParam(aMsg, aIter, &aResult->mZoom) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
+ ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) &&
+ ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
+ ReadParam(aMsg, aIter, &aResult->mLayoutViewport) &&
+ ReadParam(aMsg, aIter, &aResult->mExtraResolution) &&
+ ReadParam(aMsg, aIter, &aResult->mPaintRequestTime) &&
+ ReadParam(aMsg, aIter, &aResult->mVisualDestination) &&
+ ReadParam(aMsg, aIter, &aResult->mVisualScrollUpdateType) &&
+ ReadParam(aMsg, aIter, &aResult->mFixedLayerMargins) &&
+ ReadParam(aMsg, aIter,
+ &aResult->mCompositionSizeWithoutDynamicToolbar) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetIsRootContent) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetIsScrollInfoLayer));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::RepaintRequest>
+ : BitfieldHelper<mozilla::layers::RepaintRequest> {
+ typedef mozilla::layers::RepaintRequest paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mScrollId);
+ WriteParam(aMsg, aParam.mPresShellResolution);
+ WriteParam(aMsg, aParam.mCompositionBounds);
+ WriteParam(aMsg, aParam.mCumulativeResolution);
+ WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
+ WriteParam(aMsg, aParam.mScrollOffset);
+ WriteParam(aMsg, aParam.mZoom);
+ WriteParam(aMsg, aParam.mScrollGeneration);
+ WriteParam(aMsg, aParam.mDisplayPortMargins);
+ WriteParam(aMsg, aParam.mPresShellId);
+ WriteParam(aMsg, aParam.mLayoutViewport);
+ WriteParam(aMsg, aParam.mExtraResolution);
+ WriteParam(aMsg, aParam.mPaintRequestTime);
+ WriteParam(aMsg, aParam.mScrollUpdateType);
+ WriteParam(aMsg, aParam.mIsRootContent);
+ WriteParam(aMsg, aParam.mIsAnimationInProgress);
+ WriteParam(aMsg, aParam.mIsScrollInfoLayer);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mScrollId) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellResolution) &&
+ ReadParam(aMsg, aIter, &aResult->mCompositionBounds) &&
+ ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) &&
+ ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollOffset) &&
+ ReadParam(aMsg, aIter, &aResult->mZoom) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
+ ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
+ ReadParam(aMsg, aIter, &aResult->mLayoutViewport) &&
+ ReadParam(aMsg, aIter, &aResult->mExtraResolution) &&
+ ReadParam(aMsg, aIter, &aResult->mPaintRequestTime) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollUpdateType) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetIsRootContent) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetIsAnimationInProgress) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetIsScrollInfoLayer));
+ }
+};
+
+template <>
+struct ParamTraits<nsSize> {
+ typedef nsSize paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.width);
+ WriteParam(aMsg, aParam.height);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->width) &&
+ ReadParam(aMsg, aIter, &aResult->height);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollSnapInfo::ScrollSnapRange> {
+ typedef mozilla::layers::ScrollSnapInfo::ScrollSnapRange paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mStart);
+ WriteParam(aMsg, aParam.mEnd);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mStart) &&
+ ReadParam(aMsg, aIter, &aResult->mEnd);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollSnapInfo> {
+ typedef mozilla::layers::ScrollSnapInfo paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mScrollSnapStrictnessX);
+ WriteParam(aMsg, aParam.mScrollSnapStrictnessY);
+ WriteParam(aMsg, aParam.mSnapPositionX);
+ WriteParam(aMsg, aParam.mSnapPositionY);
+ WriteParam(aMsg, aParam.mXRangeWiderThanSnapport);
+ WriteParam(aMsg, aParam.mYRangeWiderThanSnapport);
+ WriteParam(aMsg, aParam.mSnapportSize);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mScrollSnapStrictnessX) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollSnapStrictnessY) &&
+ ReadParam(aMsg, aIter, &aResult->mSnapPositionX) &&
+ ReadParam(aMsg, aIter, &aResult->mSnapPositionY) &&
+ ReadParam(aMsg, aIter, &aResult->mXRangeWiderThanSnapport) &&
+ ReadParam(aMsg, aIter, &aResult->mYRangeWiderThanSnapport) &&
+ ReadParam(aMsg, aIter, &aResult->mSnapportSize));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::OverscrollBehaviorInfo> {
+ // Not using PlainOldDataSerializer so we get enum validation
+ // for the members.
+
+ typedef mozilla::layers::OverscrollBehaviorInfo paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mBehaviorX);
+ WriteParam(aMsg, aParam.mBehaviorY);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mBehaviorX) &&
+ ReadParam(aMsg, aIter, &aResult->mBehaviorY));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::LayerClip> {
+ typedef mozilla::layers::LayerClip paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mClipRect);
+ WriteParam(aMsg, aParam.mMaskLayerIndex);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mClipRect) &&
+ ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::ScrollGeneration>
+ : PlainOldDataSerializer<mozilla::ScrollGeneration> {};
+
+template <>
+struct ParamTraits<mozilla::ScrollPositionUpdate>
+ : PlainOldDataSerializer<mozilla::ScrollPositionUpdate> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollMetadata>
+ : BitfieldHelper<mozilla::layers::ScrollMetadata> {
+ typedef mozilla::layers::ScrollMetadata paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mMetrics);
+ WriteParam(aMsg, aParam.mSnapInfo);
+ WriteParam(aMsg, aParam.mScrollParentId);
+ WriteParam(aMsg, aParam.mBackgroundColor);
+ WriteParam(aMsg, aParam.GetContentDescription());
+ WriteParam(aMsg, aParam.mLineScrollAmount);
+ WriteParam(aMsg, aParam.mPageScrollAmount);
+ WriteParam(aMsg, aParam.mScrollClip);
+ WriteParam(aMsg, aParam.mHasScrollgrab);
+ WriteParam(aMsg, aParam.mIsLayersIdRoot);
+ WriteParam(aMsg, aParam.mIsAutoDirRootContentRTL);
+ WriteParam(aMsg, aParam.mForceDisableApz);
+ WriteParam(aMsg, aParam.mResolutionUpdated);
+ WriteParam(aMsg, aParam.mIsRDMTouchSimulationActive);
+ WriteParam(aMsg, aParam.mDidContentGetPainted);
+ WriteParam(aMsg, aParam.mDisregardedDirection);
+ WriteParam(aMsg, aParam.mOverscrollBehavior);
+ WriteParam(aMsg, aParam.mScrollUpdates);
+ }
+
+ static bool ReadContentDescription(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ nsCString str;
+ if (!ReadParam(aMsg, aIter, &str)) {
+ return false;
+ }
+ aResult->SetContentDescription(str);
+ return true;
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mMetrics) &&
+ ReadParam(aMsg, aIter, &aResult->mSnapInfo) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollParentId) &&
+ ReadParam(aMsg, aIter, &aResult->mBackgroundColor) &&
+ ReadContentDescription(aMsg, aIter, aResult) &&
+ ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) &&
+ ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollClip) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetHasScrollgrab) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetIsLayersIdRoot) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetIsAutoDirRootContentRTL) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetForceDisableApz) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetResolutionUpdated) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetIsRDMTouchSimulationActive)) &&
+ ReadBoolForBitfield(aMsg, aIter, aResult,
+ &paramType::SetDidContentGetPainted) &&
+ ReadParam(aMsg, aIter, &aResult->mDisregardedDirection) &&
+ ReadParam(aMsg, aIter, &aResult->mOverscrollBehavior) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollUpdates);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::TextureFactoryIdentifier> {
+ typedef mozilla::layers::TextureFactoryIdentifier paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mParentBackend);
+ WriteParam(aMsg, aParam.mWebRenderBackend);
+ WriteParam(aMsg, aParam.mWebRenderCompositor);
+ WriteParam(aMsg, aParam.mParentProcessType);
+ WriteParam(aMsg, aParam.mMaxTextureSize);
+ WriteParam(aMsg, aParam.mSupportsTextureDirectMapping);
+ WriteParam(aMsg, aParam.mCompositorUseANGLE);
+ WriteParam(aMsg, aParam.mCompositorUseDComp);
+ WriteParam(aMsg, aParam.mUseCompositorWnd);
+ WriteParam(aMsg, aParam.mSupportsTextureBlitting);
+ WriteParam(aMsg, aParam.mSupportsPartialUploads);
+ WriteParam(aMsg, aParam.mSupportsComponentAlpha);
+ WriteParam(aMsg, aParam.mUsingAdvancedLayers);
+ WriteParam(aMsg, aParam.mSyncHandle);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ bool result =
+ ReadParam(aMsg, aIter, &aResult->mParentBackend) &&
+ ReadParam(aMsg, aIter, &aResult->mWebRenderBackend) &&
+ ReadParam(aMsg, aIter, &aResult->mWebRenderCompositor) &&
+ ReadParam(aMsg, aIter, &aResult->mParentProcessType) &&
+ ReadParam(aMsg, aIter, &aResult->mMaxTextureSize) &&
+ ReadParam(aMsg, aIter, &aResult->mSupportsTextureDirectMapping) &&
+ ReadParam(aMsg, aIter, &aResult->mCompositorUseANGLE) &&
+ ReadParam(aMsg, aIter, &aResult->mCompositorUseDComp) &&
+ ReadParam(aMsg, aIter, &aResult->mUseCompositorWnd) &&
+ ReadParam(aMsg, aIter, &aResult->mSupportsTextureBlitting) &&
+ ReadParam(aMsg, aIter, &aResult->mSupportsPartialUploads) &&
+ ReadParam(aMsg, aIter, &aResult->mSupportsComponentAlpha) &&
+ ReadParam(aMsg, aIter, &aResult->mUsingAdvancedLayers) &&
+ ReadParam(aMsg, aIter, &aResult->mSyncHandle);
+ return result;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::TextureInfo> {
+ typedef mozilla::layers::TextureInfo paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mCompositableType);
+ WriteParam(aMsg, aParam.mTextureFlags);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mCompositableType) &&
+ ReadParam(aMsg, aIter, &aResult->mTextureFlags);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositableType>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::CompositableType,
+ mozilla::layers::CompositableType::UNKNOWN,
+ mozilla::layers::CompositableType::COUNT> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollableLayerGuid> {
+ typedef mozilla::layers::ScrollableLayerGuid paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mLayersId);
+ WriteParam(aMsg, aParam.mPresShellId);
+ WriteParam(aMsg, aParam.mScrollId);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mLayersId) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollId));
+ }
+};
+
+template <>
+struct ParamTraits<nsEventStatus>
+ : public ContiguousEnumSerializer<nsEventStatus, nsEventStatus_eIgnore,
+ nsEventStatus_eSentinel> {};
+
+template <>
+struct ParamTraits<mozilla::layers::APZHandledResult>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::APZHandledResult,
+ mozilla::layers::APZHandledResult::Unhandled,
+ mozilla::layers::APZHandledResult::Last> {};
+
+template <>
+struct ParamTraits<mozilla::layers::APZEventResult> {
+ typedef mozilla::layers::APZEventResult paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mStatus);
+ WriteParam(aMsg, aParam.mTargetGuid);
+ WriteParam(aMsg, aParam.mInputBlockId);
+ WriteParam(aMsg, aParam.mHandledResult);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mStatus) &&
+ ReadParam(aMsg, aIter, &aResult->mTargetGuid) &&
+ ReadParam(aMsg, aIter, &aResult->mInputBlockId) &&
+ ReadParam(aMsg, aIter, &aResult->mHandledResult));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::ZoomConstraints> {
+ typedef mozilla::layers::ZoomConstraints paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mAllowZoom);
+ WriteParam(aMsg, aParam.mAllowDoubleTapZoom);
+ WriteParam(aMsg, aParam.mMinZoom);
+ WriteParam(aMsg, aParam.mMaxZoom);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mAllowZoom) &&
+ ReadParam(aMsg, aIter, &aResult->mAllowDoubleTapZoom) &&
+ ReadParam(aMsg, aIter, &aResult->mMinZoom) &&
+ ReadParam(aMsg, aIter, &aResult->mMaxZoom));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::EventRegions> {
+ typedef mozilla::layers::EventRegions paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mHitRegion);
+ WriteParam(aMsg, aParam.mDispatchToContentHitRegion);
+ WriteParam(aMsg, aParam.mNoActionRegion);
+ WriteParam(aMsg, aParam.mHorizontalPanRegion);
+ WriteParam(aMsg, aParam.mVerticalPanRegion);
+ WriteParam(aMsg, aParam.mDTCRequiresTargetConfirmation);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mHitRegion) &&
+ ReadParam(aMsg, aIter, &aResult->mDispatchToContentHitRegion) &&
+ ReadParam(aMsg, aIter, &aResult->mNoActionRegion) &&
+ ReadParam(aMsg, aIter, &aResult->mHorizontalPanRegion) &&
+ ReadParam(aMsg, aIter, &aResult->mVerticalPanRegion) &&
+ ReadParam(aMsg, aIter, &aResult->mDTCRequiresTargetConfirmation));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::FocusTarget::ScrollTargets> {
+ typedef mozilla::layers::FocusTarget::ScrollTargets paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mHorizontal);
+ WriteParam(aMsg, aParam.mVertical);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mHorizontal) &&
+ ReadParam(aMsg, aIter, &aResult->mVertical);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::FocusTarget::NoFocusTarget>
+ : public EmptyStructSerializer<
+ mozilla::layers::FocusTarget::NoFocusTarget> {};
+
+template <>
+struct ParamTraits<mozilla::layers::FocusTarget> {
+ typedef mozilla::layers::FocusTarget paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mSequenceNumber);
+ WriteParam(aMsg, aParam.mFocusHasKeyEventListeners);
+ WriteParam(aMsg, aParam.mData);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ if (!ReadParam(aMsg, aIter, &aResult->mSequenceNumber) ||
+ !ReadParam(aMsg, aIter, &aResult->mFocusHasKeyEventListeners) ||
+ !ReadParam(aMsg, aIter, &aResult->mData)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<
+ mozilla::layers::KeyboardScrollAction::KeyboardScrollActionType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::KeyboardScrollAction::KeyboardScrollActionType,
+ mozilla::layers::KeyboardScrollAction::KeyboardScrollActionType::
+ eScrollCharacter,
+ mozilla::layers::KeyboardScrollAction::
+ sHighestKeyboardScrollActionType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::KeyboardScrollAction> {
+ typedef mozilla::layers::KeyboardScrollAction paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mType);
+ WriteParam(aMsg, aParam.mForward);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mType) &&
+ ReadParam(aMsg, aIter, &aResult->mForward);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::KeyboardShortcut> {
+ typedef mozilla::layers::KeyboardShortcut paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mAction);
+ WriteParam(aMsg, aParam.mKeyCode);
+ WriteParam(aMsg, aParam.mCharCode);
+ WriteParam(aMsg, aParam.mModifiers);
+ WriteParam(aMsg, aParam.mModifiersMask);
+ WriteParam(aMsg, aParam.mEventType);
+ WriteParam(aMsg, aParam.mDispatchToContent);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mAction) &&
+ ReadParam(aMsg, aIter, &aResult->mKeyCode) &&
+ ReadParam(aMsg, aIter, &aResult->mCharCode) &&
+ ReadParam(aMsg, aIter, &aResult->mModifiers) &&
+ ReadParam(aMsg, aIter, &aResult->mModifiersMask) &&
+ ReadParam(aMsg, aIter, &aResult->mEventType) &&
+ ReadParam(aMsg, aIter, &aResult->mDispatchToContent);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::KeyboardMap> {
+ typedef mozilla::layers::KeyboardMap paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.Shortcuts());
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ nsTArray<mozilla::layers::KeyboardShortcut> shortcuts;
+ if (!ReadParam(aMsg, aIter, &shortcuts)) {
+ return false;
+ }
+ *aResult = mozilla::layers::KeyboardMap(std::move(shortcuts));
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::GeckoContentController_TapType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::GeckoContentController_TapType,
+ mozilla::layers::GeckoContentController_TapType::eSingleTap,
+ mozilla::layers::kHighestGeckoContentController_TapType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::GeckoContentController_APZStateChange>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::GeckoContentController_APZStateChange,
+ mozilla::layers::GeckoContentController_APZStateChange::
+ eTransformBegin,
+ mozilla::layers::kHighestGeckoContentController_APZStateChange> {};
+
+template <>
+struct ParamTraits<mozilla::layers::EventRegionsOverride>
+ : public BitFlagsEnumSerializer<
+ mozilla::layers::EventRegionsOverride,
+ mozilla::layers::EventRegionsOverride::ALL_BITS> {};
+
+template <>
+struct ParamTraits<mozilla::layers::AsyncDragMetrics> {
+ typedef mozilla::layers::AsyncDragMetrics paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mViewId);
+ WriteParam(aMsg, aParam.mPresShellId);
+ WriteParam(aMsg, aParam.mDragStartSequenceNumber);
+ WriteParam(aMsg, aParam.mScrollbarDragOffset);
+ WriteParam(aMsg, aParam.mDirection);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mViewId) &&
+ ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
+ ReadParam(aMsg, aIter, &aResult->mDragStartSequenceNumber) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollbarDragOffset) &&
+ ReadParam(aMsg, aIter, &aResult->mDirection));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositorOptions> {
+ typedef mozilla::layers::CompositorOptions paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mUseAPZ);
+ WriteParam(aMsg, aParam.mUseWebRender);
+ WriteParam(aMsg, aParam.mUseAdvancedLayers);
+ WriteParam(aMsg, aParam.mInitiallyPaused);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mUseAPZ) &&
+ ReadParam(aMsg, aIter, &aResult->mUseWebRender) &&
+ ReadParam(aMsg, aIter, &aResult->mUseAdvancedLayers) &&
+ ReadParam(aMsg, aIter, &aResult->mInitiallyPaused);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollbarLayerType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::ScrollbarLayerType,
+ mozilla::layers::ScrollbarLayerType::None,
+ mozilla::layers::kHighestScrollbarLayerType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollbarData> {
+ typedef mozilla::layers::ScrollbarData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mDirection);
+ WriteParam(aMsg, aParam.mScrollbarLayerType);
+ WriteParam(aMsg, aParam.mThumbRatio);
+ WriteParam(aMsg, aParam.mThumbStart);
+ WriteParam(aMsg, aParam.mThumbLength);
+ WriteParam(aMsg, aParam.mThumbIsAsyncDraggable);
+ WriteParam(aMsg, aParam.mScrollTrackStart);
+ WriteParam(aMsg, aParam.mScrollTrackLength);
+ WriteParam(aMsg, aParam.mTargetViewId);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mDirection) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollbarLayerType) &&
+ ReadParam(aMsg, aIter, &aResult->mThumbRatio) &&
+ ReadParam(aMsg, aIter, &aResult->mThumbStart) &&
+ ReadParam(aMsg, aIter, &aResult->mThumbLength) &&
+ ReadParam(aMsg, aIter, &aResult->mThumbIsAsyncDraggable) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollTrackStart) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollTrackLength) &&
+ ReadParam(aMsg, aIter, &aResult->mTargetViewId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::SimpleLayerAttributes::FixedPositionData>
+ : public PlainOldDataSerializer<
+ mozilla::layers::SimpleLayerAttributes::FixedPositionData> {};
+
+template <>
+struct ParamTraits<mozilla::layers::SimpleLayerAttributes::StickyPositionData>
+ : public PlainOldDataSerializer<
+ mozilla::layers::SimpleLayerAttributes::StickyPositionData> {};
+
+template <>
+struct ParamTraits<mozilla::layers::SimpleLayerAttributes> {
+ typedef mozilla::layers::SimpleLayerAttributes paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mTransform);
+ WriteParam(aMsg, aParam.mTransformIsPerspective);
+ WriteParam(aMsg, aParam.mScrolledClip);
+ WriteParam(aMsg, aParam.mPostXScale);
+ WriteParam(aMsg, aParam.mPostYScale);
+ WriteParam(aMsg, aParam.mContentFlags);
+ WriteParam(aMsg, aParam.mOpacity);
+ WriteParam(aMsg, aParam.mIsFixedPosition);
+ WriteParam(aMsg, aParam.mIsAsyncZoomContainerForViewId);
+ WriteParam(aMsg, aParam.mScrollbarData);
+ WriteParam(aMsg, aParam.mMixBlendMode);
+ WriteParam(aMsg, aParam.mForceIsolatedGroup);
+ WriteParam(aMsg, aParam.mFixedPositionData);
+ WriteParam(aMsg, aParam.mStickyPositionData);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mTransform) &&
+ ReadParam(aMsg, aIter, &aResult->mTransformIsPerspective) &&
+ ReadParam(aMsg, aIter, &aResult->mScrolledClip) &&
+ ReadParam(aMsg, aIter, &aResult->mPostXScale) &&
+ ReadParam(aMsg, aIter, &aResult->mPostYScale) &&
+ ReadParam(aMsg, aIter, &aResult->mContentFlags) &&
+ ReadParam(aMsg, aIter, &aResult->mOpacity) &&
+ ReadParam(aMsg, aIter, &aResult->mIsFixedPosition) &&
+ ReadParam(aMsg, aIter, &aResult->mIsAsyncZoomContainerForViewId) &&
+ ReadParam(aMsg, aIter, &aResult->mScrollbarData) &&
+ ReadParam(aMsg, aIter, &aResult->mMixBlendMode) &&
+ ReadParam(aMsg, aIter, &aResult->mForceIsolatedGroup) &&
+ ReadParam(aMsg, aIter, &aResult->mFixedPositionData) &&
+ ReadParam(aMsg, aIter, &aResult->mStickyPositionData);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositionPayloadType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::CompositionPayloadType,
+ mozilla::layers::CompositionPayloadType::eKeyPress,
+ mozilla::layers::kHighestCompositionPayloadType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositionPayload> {
+ typedef mozilla::layers::CompositionPayload paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mType);
+ WriteParam(aMsg, aParam.mTimeStamp);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mType) &&
+ ReadParam(aMsg, aIter, &aResult->mTimeStamp);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::RayReferenceData> {
+ typedef mozilla::RayReferenceData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mInitialPosition);
+ WriteParam(aMsg, aParam.mContainingBlockRect);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mInitialPosition) &&
+ ReadParam(aMsg, aIter, &aResult->mContainingBlockRect));
+ }
+};
+
+#define IMPL_PARAMTRAITS_BY_SERDE(type_) \
+ template <> \
+ struct ParamTraits<mozilla::type_> { \
+ typedef mozilla::type_ paramType; \
+ static void Write(Message* aMsg, const paramType& aParam) { \
+ mozilla::ipc::ByteBuf v; \
+ mozilla::DebugOnly<bool> rv = Servo_##type_##_Serialize(&aParam, &v); \
+ MOZ_ASSERT(rv, "Serialize ##type_## failed"); \
+ WriteParam(aMsg, std::move(v)); \
+ } \
+ static bool Read(const Message* aMsg, PickleIterator* aIter, \
+ paramType* aResult) { \
+ mozilla::ipc::ByteBuf in; \
+ bool rv = ReadParam(aMsg, aIter, &in); \
+ if (!rv) { \
+ return false; \
+ } \
+ return in.mData && Servo_##type_##_Deserialize(&in, aResult); \
+ } \
+ };
+
+IMPL_PARAMTRAITS_BY_SERDE(LengthPercentage)
+IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetPath)
+IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetRotate)
+IMPL_PARAMTRAITS_BY_SERDE(StylePositionOrAuto)
+IMPL_PARAMTRAITS_BY_SERDE(StyleRotate)
+IMPL_PARAMTRAITS_BY_SERDE(StyleScale)
+IMPL_PARAMTRAITS_BY_SERDE(StyleTranslate)
+IMPL_PARAMTRAITS_BY_SERDE(StyleTransform)
+
+} /* namespace IPC */
+
+#endif /* mozilla_layers_LayersMessageUtils */
diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh
new file mode 100644
index 0000000000..ee283795e8
--- /dev/null
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -0,0 +1,573 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include protocol PCompositorBridge;
+include protocol PTexture;
+
+include "gfxipc/ShadowLayerUtils.h";
+include "mozilla/GfxMessageUtils.h";
+include "ImageLayers.h";
+
+using mozilla::gfx::Glyph from "mozilla/gfx/2D.h";
+using mozilla::gfx::SamplingFilter from "mozilla/gfx/2D.h";
+using struct mozilla::gfx::DeviceColor from "mozilla/gfx/2D.h";
+using struct mozilla::gfx::Point from "mozilla/gfx/Point.h";
+using struct mozilla::gfx::Point3D from "mozilla/gfx/Point.h";
+using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
+using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using class mozilla::SideBits from "mozilla/gfx/Types.h";
+using nscolor from "nsColor.h";
+using nscoord from "nsCoord.h";
+using struct nsRect from "nsRect.h";
+using struct nsPoint from "nsPoint.h";
+using class mozilla::TimeDuration from "mozilla/TimeStamp.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using mozilla::ScreenRotation from "mozilla/WidgetUtils.h";
+using nsCSSPropertyID from "nsCSSPropertyID.h";
+using hal::ScreenOrientation from "mozilla/HalScreenConfiguration.h";
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::CSSPoint from "Units.h";
+using mozilla::CSSRect from "Units.h";
+using mozilla::LayerMargin from "Units.h";
+using mozilla::LayerPoint from "Units.h";
+using mozilla::LayerCoord from "Units.h";
+using mozilla::LayerSize from "Units.h";
+using mozilla::LayerRect from "Units.h";
+using mozilla::LayerIntSize from "Units.h";
+using mozilla::LayerIntRect from "Units.h";
+using mozilla::LayerIntRegion from "Units.h";
+using mozilla::ParentLayerIntRect from "Units.h";
+using mozilla::ParentLayerRect from "Units.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
+using mozilla::LayoutDevicePoint from "Units.h";
+using mozilla::LayoutDeviceRect from "Units.h";
+using mozilla::layers::ScaleMode from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::EventRegions from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::FocusTarget from "mozilla/layers/FocusTarget.h";
+using struct mozilla::layers::ScrollMetadata from "FrameMetrics.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositionPayload from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::SimpleLayerAttributes from "mozilla/layers/LayerAttributes.h";
+using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
+using mozilla::VsyncId from "mozilla/VsyncDispatcher.h";
+using mozilla::LengthPercentage from "mozilla/ServoStyleConsts.h";
+using mozilla::RayReferenceData from "mozilla/MotionPathUtils.h";
+using mozilla::StyleOffsetPath from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleOffsetRotate from "mozilla/ServoStyleConsts.h";
+using mozilla::StylePositionOrAuto from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleRotate from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleScale from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleTranslate from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleTransform from "mozilla/ServoStyleConsts.h";
+
+namespace mozilla {
+namespace layers {
+
+struct TargetConfig {
+ IntRect naturalBounds;
+ ScreenRotation rotation;
+ ScreenOrientation orientation;
+ nsIntRegion clearRegion;
+};
+
+// Create a shadow layer for |layer|
+struct OpCreatePaintedLayer { LayerHandle layer; };
+struct OpCreateContainerLayer { LayerHandle layer; };
+struct OpCreateImageLayer { LayerHandle layer; };
+struct OpCreateColorLayer { LayerHandle layer; };
+struct OpCreateCanvasLayer { LayerHandle layer; };
+struct OpCreateRefLayer { LayerHandle layer; };
+
+struct OpAttachCompositable {
+ LayerHandle layer;
+ CompositableHandle compositable;
+};
+
+struct OpAttachAsyncCompositable {
+ LayerHandle layer;
+ CompositableHandle compositable;
+};
+
+struct ThebesBufferData {
+ IntRect rect;
+ IntPoint rotation;
+};
+
+struct CubicBezierFunction {
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+};
+
+struct StepFunction {
+ int steps;
+ uint8_t type; // Converted from StyleStepPosition.
+};
+
+union TimingFunction {
+ null_t;
+ CubicBezierFunction;
+ StepFunction;
+};
+
+struct LayerColor { DeviceColor value; };
+
+comparable union Animatable {
+ null_t;
+ float;
+ nscolor;
+ StyleRotate;
+ StyleScale;
+ StyleTranslate;
+ StyleTransform;
+ StyleOffsetPath;
+ LengthPercentage;
+ StyleOffsetRotate;
+ StylePositionOrAuto;
+};
+
+struct AnimationSegment {
+ Animatable startState;
+ Animatable endState;
+ float startPortion;
+ float endPortion;
+ uint8_t startComposite;
+ uint8_t endComposite;
+ TimingFunction sampleFn;
+};
+
+comparable struct MotionPathData {
+ // the transform-origin property for motion in css pixels
+ CSSPoint origin;
+ // the adjustment for the anchor point of motion path.
+ CSSPoint anchorAdjustment;
+ RayReferenceData rayReferenceData;
+};
+
+comparable struct PartialPrerenderData {
+ LayoutDeviceRect rect;
+ SideBits overflowedSides;
+ // the scroll id of the nearest scrollable frame of this partial prerender
+ // data.
+ ViewID scrollId;
+ // The clip rectangle of the nearest scrollable frame.
+ // NOTE: This should be used only for fallback cases where APZ is not enabled.
+ ParentLayerRect clipRect;
+ // a transform from the coordinate space of the animated element to a
+ // coordinate space where the `clipRect` can be applied.
+ Matrix4x4 transformInClip; // Used only for WebRender.
+ // the position relative to the reference frame of the animated transform
+ // element in the element coordinate space.
+ LayoutDevicePoint position; // Used only for WebRender.
+};
+
+// Transforms need extra information to correctly convert the list of transform
+// functions to a Matrix4x4 that can be applied directly to the layer.
+comparable struct TransformData {
+ // the origin of the frame being transformed in app units
+ nsPoint origin;
+ // the transform-origin property for the transform in device pixels
+ Point3D transformOrigin;
+ nsRect bounds;
+ int32_t appUnitsPerDevPixel;
+ MotionPathData? motionPathData;
+ PartialPrerenderData? partialPrerenderData;
+};
+
+struct Animation {
+ // The zero time of this Animation's timeline. May be null if isNotPlaying is
+ // true.
+ TimeStamp originTime;
+ // The start time is relative to the originTime. This allows us to represent
+ // start times in the distant past that cannot be expressed using a TimeStamp.
+ TimeDuration? startTime;
+ TimeDuration delay;
+ TimeDuration endDelay;
+ // The value of the animation's current time at the moment it was sent to the
+ // compositor. This value will be used for below cases:
+ // 1) Animations that are play-pending. Initially these animations will have a
+ // null |startTime|. Once the animation is ready to start (i.e. painting
+ // has finished), we calculate an appropriate value of |startTime| such
+ // that playback begins from |holdTime|.
+ // 2) Not playing animations (e.g. paused and finished animations). In this
+ // case the |holdTime| represents the current time the animation will
+ // maintain.
+ TimeDuration holdTime;
+ TimeDuration duration;
+ // For each frame, the interpolation point is computed based on the
+ // startTime, the direction, the duration, and the current time.
+ // The segments must uniquely cover the portion from 0.0 to 1.0
+ AnimationSegment[] segments;
+ // Number of times to repeat the animation, including positive infinity.
+ // Values <= 0 mean the animation will not play (although events are still
+ // dispatched on the main thread).
+ float iterations;
+ float iterationStart;
+ // This uses the NS_STYLE_ANIMATION_DIRECTION_* constants.
+ uint8_t direction;
+ // This uses dom::FillMode.
+ uint8_t fillMode;
+ nsCSSPropertyID property;
+ float playbackRate;
+ // When performing an asynchronous update to the playbackRate, |playbackRate|
+ // above is the updated playbackRate while |previousPlaybackRate| is the
+ // existing playbackRate. This is used by AnimationInfo to update the
+ // startTime based on the 'readyTime' (timestamp at the end of painting)
+ // and is not used beyond that point.
+ //
+ // It is set to numeric_limits<float>::quiet_NaN() when no asynchronous update
+ // to the playbackRate is being performed.
+ float previousPlaybackRate;
+ // This is used in the transformed progress calculation.
+ TimingFunction easingFunction;
+ uint8_t iterationComposite;
+ // True if the animation has a fixed current time (e.g. paused and
+ // forward-filling animations).
+ bool isNotPlaying;
+ // True if this is not an animating property. For some transform-like
+ // properties, we just send their baseStyles for merging with other animating
+ // properties. In this case, we don't have animation information on this
+ // property, and so don't need to do interpolation.
+ bool isNotAnimating;
+ // The base style that animations should composite with. This is only set for
+ // animations with a composite mode of additive or accumulate, and only for
+ // the first animation in the set (i.e. the animation that is lowest in the
+ // stack). In all other cases the value is null_t.
+ Animatable baseStyle;
+ // An optional data specific for transform like properies.
+ TransformData? transformData;
+};
+
+struct CompositorAnimations {
+ Animation[] animations;
+ // This id is used to map the layer animations between content
+ // and compositor side
+ uint64_t id;
+};
+
+// Change a layer's attributes
+struct CommonLayerAttributes {
+ LayerIntRegion visibleRegion;
+ EventRegions eventRegions;
+ bool useClipRect;
+ ParentLayerIntRect clipRect;
+ LayerHandle maskLayer;
+ LayerHandle[] ancestorMaskLayers;
+ // Animated colors will only honored for ColorLayers.
+ CompositorAnimations compositorAnimations;
+ nsIntRegion invalidRegion;
+ ScrollMetadata[] scrollMetadata;
+ nsCString displayListLog;
+};
+
+struct PaintedLayerAttributes {
+ nsIntRegion validRegion;
+};
+struct ContainerLayerAttributes {
+ float preXScale;
+ float preYScale;
+ float inheritedXScale;
+ float inheritedYScale;
+ float presShellResolution;
+};
+
+struct ColorLayerAttributes { LayerColor color; IntRect bounds; };
+struct CanvasLayerAttributes { SamplingFilter samplingFilter; IntRect bounds; };
+struct RefLayerAttributes {
+ LayersId id;
+ EventRegionsOverride eventRegionsOverride;
+ LayerIntSize remoteDocumentSize;
+};
+struct ImageLayerAttributes { SamplingFilter samplingFilter; IntSize scaleToSize; ScaleMode scaleMode; };
+
+union SpecificLayerAttributes {
+ null_t;
+ PaintedLayerAttributes;
+ ContainerLayerAttributes;
+ ColorLayerAttributes;
+ CanvasLayerAttributes;
+ RefLayerAttributes;
+ ImageLayerAttributes;
+};
+
+struct LayerAttributes {
+ CommonLayerAttributes common;
+ SpecificLayerAttributes specific;
+};
+
+// See nsIWidget Configurations
+comparable struct PluginWindowData {
+ uintptr_t windowId;
+ LayoutDeviceIntRect[] clip;
+ LayoutDeviceIntRect bounds;
+ bool visible;
+};
+
+struct OpSetSimpleLayerAttributes {
+ LayerHandle layer;
+ SimpleLayerAttributes attrs;
+};
+
+struct OpSetLayerAttributes {
+ LayerHandle layer;
+ LayerAttributes attrs;
+};
+
+// Monkey with the tree structure
+struct OpSetRoot { LayerHandle root; };
+struct OpInsertAfter { LayerHandle container; LayerHandle childLayer; LayerHandle after; };
+struct OpPrependChild { LayerHandle container; LayerHandle childLayer; };
+struct OpRemoveChild { LayerHandle container; LayerHandle childLayer; };
+struct OpRepositionChild { LayerHandle container; LayerHandle childLayer; LayerHandle after; };
+struct OpRaiseToTopChild { LayerHandle container; LayerHandle childLayer; };
+
+struct OpSetDiagnosticTypes { DiagnosticTypes diagnostics; };
+
+struct ShmemSection {
+ Shmem shmem;
+ uint32_t offset;
+ uint32_t size;
+};
+
+struct CrossProcessSemaphoreDescriptor {
+ CrossProcessSemaphoreHandle sem;
+};
+
+union ReadLockDescriptor {
+ ShmemSection;
+ CrossProcessSemaphoreDescriptor;
+ uintptr_t;
+ null_t;
+};
+
+struct TexturedTileDescriptor {
+ PTexture texture;
+ PTexture? textureOnWhite;
+ IntRect updateRect;
+ bool readLocked;
+ bool readLockedOnWhite;
+ bool wasPlaceholder;
+};
+
+struct PlaceholderTileDescriptor {
+};
+
+union TileDescriptor {
+ TexturedTileDescriptor;
+ PlaceholderTileDescriptor;
+};
+
+struct SurfaceDescriptorTiles {
+ nsIntRegion validRegion;
+ TileDescriptor[] tiles;
+ IntPoint tileOrigin;
+ IntSize tileSize;
+ int firstTileX;
+ int firstTileY;
+ int retainedWidth;
+ int retainedHeight;
+ float resolution;
+ float frameXResolution;
+ float frameYResolution;
+ bool isProgressive;
+};
+
+struct OpUseTiledLayerBuffer {
+ SurfaceDescriptorTiles tileLayerDescriptor;
+};
+
+struct OpPaintTextureRegion {
+ ThebesBufferData bufferData;
+ nsIntRegion updatedRegion;
+};
+
+/**
+ * Tells the CompositableHost to remove the corresponding TextureHost
+ */
+struct OpRemoveTexture {
+ PTexture texture;
+};
+
+struct TimedTexture {
+ PTexture texture;
+ TimeStamp timeStamp;
+ IntRect picture;
+ uint32_t frameID;
+ uint32_t producerID;
+ bool readLocked;
+};
+
+/**
+ * Tells the compositor-side which textures to use (for example, as front buffer
+ * if there are several textures for double buffering).
+ * This provides a list of textures with timestamps, ordered by timestamp.
+ * The newest texture whose timestamp is <= the current time is rendered
+ * (where null is considered less than every other timestamp). If there is no
+ * such texture, the first texture is rendered.
+ * The first timestamp value can be null, but the others must not be.
+ * The list must not be empty.
+ */
+struct OpUseTexture {
+ TimedTexture[] textures;
+};
+
+struct OpUseComponentAlphaTextures {
+ PTexture textureOnBlack;
+ PTexture textureOnWhite;
+ bool readLockedBlack;
+ bool readLockedWhite;
+};
+
+struct OpNotifyNotUsed {
+ uint64_t TextureId;
+ uint64_t fwdTransactionId;
+};
+
+struct OpDeliverAcquireFence {
+ PTexture texture;
+ FileDescriptor fenceFd;
+};
+
+struct OpDeliverReleaseFence {
+ FileDescriptor? fenceFd;
+ uint64_t bufferId;
+ uint64_t fwdTransactionId;
+ bool usesImageBridge;
+};
+
+union CompositableOperationDetail {
+ OpPaintTextureRegion;
+
+ OpUseTiledLayerBuffer;
+
+ OpRemoveTexture;
+
+ OpUseTexture;
+ OpUseComponentAlphaTextures;
+
+ OpDeliverAcquireFence;
+};
+
+struct CompositableOperation {
+ CompositableHandle compositable;
+ CompositableOperationDetail detail;
+};
+
+// A unit of a changeset; a set of these comprise a changeset
+// If adding a new edit type that requires the hit testing tree to be updated,
+// set the updateHitTestingTree flag to true in RecvUpdate()
+union Edit {
+ OpCreatePaintedLayer;
+ OpCreateContainerLayer;
+ OpCreateImageLayer;
+ OpCreateColorLayer;
+ OpCreateCanvasLayer;
+ OpCreateRefLayer;
+
+ OpSetDiagnosticTypes;
+
+ OpSetRoot;
+ OpInsertAfter;
+ OpPrependChild;
+ OpRemoveChild;
+ OpRepositionChild;
+ OpRaiseToTopChild;
+
+ OpAttachCompositable;
+ OpAttachAsyncCompositable;
+
+ CompositableOperation;
+};
+
+// Operations related to destroying resources, always handled after the other
+// operations for safety.
+union OpDestroy {
+ PTexture;
+ CompositableHandle;
+};
+
+// Replies to operations
+
+struct OpContentBufferSwap {
+ CompositableHandle compositable;
+ nsIntRegion frontUpdatedRegion;
+};
+
+/**
+ * An ImageCompositeNotification is sent the first time a particular
+ * image is composited by an ImageHost.
+ */
+struct ImageCompositeNotification {
+ CompositableHandle compositable;
+ TimeStamp imageTimeStamp;
+ TimeStamp firstCompositeTimeStamp;
+ uint32_t frameID;
+ uint32_t producerID;
+};
+
+union AsyncParentMessageData {
+ OpNotifyNotUsed;
+ OpDeliverReleaseFence;
+};
+
+struct PaintTiming {
+ float serializeMs;
+ float sendMs;
+ float dlMs;
+ float dl2Ms;
+ float flbMs;
+ float rasterMs;
+};
+
+struct TransactionInfo
+{
+ Edit[] cset;
+ OpSetSimpleLayerAttributes[] setSimpleAttrs;
+ OpSetLayerAttributes[] setAttrs;
+ CompositableOperation[] paints;
+ OpDestroy[] toDestroy;
+ uint64_t fwdTransactionId;
+ TransactionId id;
+ TargetConfig targetConfig;
+ PluginWindowData[] plugins;
+ bool isFirstPaint;
+ FocusTarget focusTarget;
+ bool scheduleComposite;
+ uint32_t paintSequenceNumber;
+ bool isRepeatTransaction;
+ VsyncId vsyncId;
+ TimeStamp vsyncStart;
+ TimeStamp refreshStart;
+ TimeStamp transactionStart;
+ bool containsSVG;
+ nsCString url;
+ TimeStamp fwdTime;
+ /* This provides some timing information on any content that is meant to be
+ * presented during this transaction.
+ */
+ CompositionPayload[] payload;
+};
+
+union OMTAValue {
+ null_t;
+ nscolor;
+ float;
+ Matrix4x4;
+};
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh
new file mode 100644
index 0000000000..8661d6748f
--- /dev/null
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -0,0 +1,204 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "gfxipc/ShadowLayerUtils.h";
+
+using struct gfxPoint from "gfxPoint.h";
+using nsIntRegion from "nsRegion.h";
+using struct mozilla::layers::SurfaceDescriptorX11 from "gfxipc/SurfaceDescriptor.h";
+using mozilla::StereoMode from "ImageTypes.h";
+using struct mozilla::null_t from "mozilla/ipc/IPCCore.h";
+using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h";
+using mozilla::gfx::YUVColorSpace from "mozilla/gfx/Types.h";
+using mozilla::gfx::ColorDepth from "mozilla/gfx/Types.h";
+using mozilla::gfx::ColorRange from "mozilla/gfx/Types.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using gfxImageFormat from "gfxTypes.h";
+using mozilla::layers::MaybeVideoBridgeSource from "mozilla/layers/VideoBridgeUtils.h";
+
+namespace mozilla {
+namespace layers {
+
+comparable struct SurfaceDescriptorFileMapping {
+ WindowsHandle handle;
+ SurfaceFormat format;
+ IntSize size;
+};
+
+comparable struct SurfaceDescriptorDIB {
+ // gfxWindowsSurface*
+ uintptr_t surface;
+};
+
+comparable struct SurfaceDescriptorD3D10 {
+ WindowsHandle handle;
+ SurfaceFormat format;
+ IntSize size;
+ YUVColorSpace yUVColorSpace;
+ ColorRange colorRange;
+};
+
+comparable struct SurfaceDescriptorDXGIYCbCr {
+ WindowsHandle handleY;
+ WindowsHandle handleCb;
+ WindowsHandle handleCr;
+ IntSize size;
+ IntSize sizeY;
+ IntSize sizeCbCr;
+ ColorDepth colorDepth;
+ YUVColorSpace yUVColorSpace;
+ ColorRange colorRange;
+};
+
+comparable struct SurfaceDescriptorMacIOSurface {
+ uint32_t surfaceId;
+ double scaleFactor;
+ bool isOpaque;
+ YUVColorSpace yUVColorSpace;
+};
+
+comparable struct SurfaceDescriptorDMABuf {
+ uint32_t bufferType;
+ uint64_t modifier;
+ uint32_t flags;
+ FileDescriptor[] fds;
+ uint32_t[] width;
+ uint32_t[] height;
+ uint32_t[] format;
+ uint32_t[] strides;
+ uint32_t[] offsets;
+ YUVColorSpace yUVColorSpace;
+ FileDescriptor[] fence;
+ uint32_t uid;
+ FileDescriptor[] refCount;
+};
+
+comparable struct SurfaceTextureDescriptor {
+ uint64_t handle;
+ IntSize size;
+ SurfaceFormat format;
+ bool continuous;
+ bool ignoreTransform;
+};
+
+comparable struct SurfaceDescriptorAndroidHardwareBuffer {
+ FileDescriptor handle;
+ uint64_t bufferId;
+ IntSize size;
+ SurfaceFormat format;
+};
+
+comparable struct EGLImageDescriptor {
+ uintptr_t image; // `EGLImage` is a `void*`.
+ uintptr_t fence;
+ IntSize size;
+ bool hasAlpha;
+};
+
+comparable struct SurfaceDescriptorSharedGLTexture {
+ uint32_t texture;
+ uint32_t target;
+ uintptr_t fence;
+ IntSize size;
+ bool hasAlpha;
+};
+
+
+comparable union RemoteDecoderVideoSubDescriptor {
+ SurfaceDescriptorD3D10;
+ SurfaceDescriptorDXGIYCbCr;
+ SurfaceDescriptorDMABuf;
+ SurfaceDescriptorMacIOSurface;
+ null_t;
+};
+
+comparable struct SurfaceDescriptorRemoteDecoder {
+ uint64_t handle;
+ RemoteDecoderVideoSubDescriptor subdesc;
+ MaybeVideoBridgeSource source;
+};
+
+comparable struct SurfaceDescriptorPlugin {
+ uint64_t id;
+ SurfaceDescriptorD3D10 pluginSurf;
+ SurfaceDescriptorD3D10 displaySurf;
+};
+
+comparable union SurfaceDescriptorGPUVideo {
+ SurfaceDescriptorRemoteDecoder;
+ SurfaceDescriptorPlugin;
+};
+
+comparable struct RGBDescriptor {
+ IntSize size;
+ SurfaceFormat format;
+ bool hasIntermediateBuffer;
+};
+
+comparable struct YCbCrDescriptor {
+ IntRect display;
+ IntSize ySize;
+ uint32_t yStride;
+ IntSize cbCrSize;
+ uint32_t cbCrStride;
+ uint32_t yOffset;
+ uint32_t cbOffset;
+ uint32_t crOffset;
+ StereoMode stereoMode;
+ ColorDepth colorDepth;
+ YUVColorSpace yUVColorSpace;
+ ColorRange colorRange;
+ bool hasIntermediateBuffer;
+};
+
+comparable union BufferDescriptor {
+ RGBDescriptor;
+ YCbCrDescriptor;
+};
+
+comparable union MemoryOrShmem {
+ uintptr_t;
+ Shmem;
+};
+
+comparable struct SurfaceDescriptorBuffer {
+ BufferDescriptor desc;
+ MemoryOrShmem data;
+};
+
+comparable struct SurfaceDescriptorShared
+{
+ IntSize size;
+ int32_t stride;
+ SurfaceFormat format;
+ Handle handle;
+};
+
+comparable struct SurfaceDescriptorRecorded {
+ int64_t textureId;
+};
+
+comparable union SurfaceDescriptor {
+ SurfaceDescriptorBuffer;
+ SurfaceDescriptorDIB;
+ SurfaceDescriptorD3D10;
+ SurfaceDescriptorFileMapping;
+ SurfaceDescriptorDXGIYCbCr;
+ SurfaceDescriptorX11;
+ SurfaceDescriptorDMABuf;
+ SurfaceTextureDescriptor;
+ SurfaceDescriptorAndroidHardwareBuffer;
+ EGLImageDescriptor;
+ SurfaceDescriptorMacIOSurface;
+ SurfaceDescriptorSharedGLTexture;
+ SurfaceDescriptorGPUVideo;
+ SurfaceDescriptorRecorded;
+ null_t;
+};
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/ipc/PAPZ.ipdl b/gfx/layers/ipc/PAPZ.ipdl
new file mode 100644
index 0000000000..9f0a3db340
--- /dev/null
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+
+include protocol PCompositorBridge;
+
+using CSSRect from "Units.h";
+using struct mozilla::layers::RepaintRequest from "mozilla/layers/RepaintRequest.h";
+using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.h";
+using mozilla::layers::GeckoContentController_APZStateChange from "mozilla/layers/GeckoContentControllerTypes.h";
+using mozilla::layers::ScrollDirection from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::MatrixMessage from "mozilla/layers/MatrixMessage.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
+using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
+using class nsRegion from "nsRegion.h";
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * PAPZ is a protocol for remoting a GeckoContentController. PAPZ lives on the
+ * PCompositorBridge protocol which either connects to the compositor thread
+ * in the main process, or to the compositor thread in the gpu processs.
+ *
+ * PAPZParent lives in the compositor thread, while PAPZChild lives wherever the remoted
+ * GeckoContentController lives (generally the main thread of the main or content process).
+ * RemoteContentController implements PAPZParent, while APZChild implements PAPZChild.
+ *
+ * PAPZ is always used for ContentProcessController and only used for ChromeProcessController
+ * when there is a gpu process, otherwhise ChromeProcessController is used directly on the
+ * compositor thread. Only the methods that are used by the [Chrome,Content]ProcessController
+ * are implemented. If a new method is needed then PAPZ, APZChild, and RemoteContentController
+ * must be updated to handle it.
+ */
+sync protocol PAPZ
+{
+ manager PCompositorBridge;
+
+parent:
+ async __delete__();
+
+child:
+ async LayerTransforms(MatrixMessage[] aTransforms);
+
+ async RequestContentRepaint(RepaintRequest request);
+
+ async UpdateOverscrollVelocity(ScrollableLayerGuid aGuid, float aX, float aY, bool aIsRootContent);
+
+ async UpdateOverscrollOffset(ScrollableLayerGuid aGuid, float aX, float aY, bool aIsRootContent);
+
+ async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent);
+
+ async NotifyAPZStateChange(ScrollableLayerGuid aGuid, GeckoContentController_APZStateChange aChange, int aArg);
+
+ async NotifyFlushComplete();
+
+ async NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId, ViewID aScrollId, ScrollDirection aDirection);
+
+ async NotifyAsyncScrollbarDragRejected(ViewID aScrollId);
+
+ async NotifyAsyncAutoscrollRejected(ViewID aScrollId);
+
+both:
+ async Destroy();
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PAPZCTreeManager.ipdl b/gfx/layers/ipc/PAPZCTreeManager.ipdl
new file mode 100644
index 0000000000..e6d70a9ddf
--- /dev/null
+++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "ipc/nsGUIEventIPC.h";
+
+include protocol PCompositorBridge;
+
+using CSSRect from "Units.h";
+using LayoutDeviceCoord from "Units.h";
+using mozilla::LayoutDevicePoint from "Units.h";
+using ScreenPoint from "Units.h";
+using mozilla::layers::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.h";
+using mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
+using mozilla::layers::GeckoContentController_TapType from "mozilla/layers/GeckoContentControllerTypes.h";
+using class mozilla::layers::KeyboardMap from "mozilla/layers/KeyboardMap.h";
+using mozilla::wr::RenderRoot from "mozilla/webrender/WebRenderTypes.h";
+
+using mozilla::Modifiers from "mozilla/EventForwards.h";
+using mozilla::PinchGestureInput::PinchGestureType from "InputData.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PAPZCTreeManager is a protocol for remoting an IAPZCTreeManager. PAPZCTreeManager
+ * lives on the PCompositorBridge protocol which either connects to the compositor
+ * thread in the main process, or to the compositor thread in the gpu processs.
+ *
+ * PAPZCTreeManagerParent lives in the compositor thread, while PAPZCTreeManagerChild
+ * lives in the main thread of the main or the content process. APZCTreeManagerParent
+ * and APZCTreeManagerChild implement this protocol.
+ */
+protocol PAPZCTreeManager
+{
+manager PCompositorBridge;
+
+parent:
+
+ // These messages correspond to the methods
+ // on the IAPZCTreeManager interface
+
+ async ZoomToRect(ScrollableLayerGuid aGuid, CSSRect aRect, uint32_t Flags);
+
+ async ContentReceivedInputBlock(uint64_t aInputBlockId, bool PreventDefault);
+
+ async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] Targets);
+
+ async UpdateZoomConstraints(ScrollableLayerGuid aGuid, MaybeZoomConstraints aConstraints);
+
+ async SetKeyboardMap(KeyboardMap aKeyboardMap);
+
+ async SetDPI(float aDpiValue);
+
+ async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aValues);
+
+ async StartScrollbarDrag(ScrollableLayerGuid aGuid, AsyncDragMetrics aDragMetrics);
+
+ async StartAutoscroll(ScrollableLayerGuid aGuid, ScreenPoint aAnchorLocation);
+
+ async StopAutoscroll(ScrollableLayerGuid aGuid);
+
+ async SetLongTapEnabled(bool aTapGestureEnabled);
+
+ async __delete__();
+
+child:
+
+ async HandleTap(GeckoContentController_TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
+ ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+
+ async NotifyPinchGesture(PinchGestureType aType, ScrollableLayerGuid aGuid,
+ LayoutDevicePoint aFocusPoint, LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers);
+
+ async CancelAutoscroll(ViewID aScrollId);
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/layers/ipc/PAPZInputBridge.ipdl b/gfx/layers/ipc/PAPZInputBridge.ipdl
new file mode 100644
index 0000000000..160a74f81c
--- /dev/null
+++ b/gfx/layers/ipc/PAPZInputBridge.ipdl
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using LayoutDeviceIntPoint from "Units.h";
+using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::APZEventResult from "mozilla/layers/APZInputBridge.h";
+
+using EventMessage from "mozilla/EventForwards.h";
+using class mozilla::MultiTouchInput from "InputData.h";
+using class mozilla::MouseInput from "InputData.h";
+using class mozilla::PanGestureInput from "InputData.h";
+using class mozilla::PinchGestureInput from "InputData.h";
+using class mozilla::TapGestureInput from "InputData.h";
+using class mozilla::ScrollWheelInput from "InputData.h";
+using class mozilla::KeyboardInput from "InputData.h";
+
+using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+
+include protocol PGPU;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This protocol is used to send input events from the UI process to the
+ * GPU process for handling by APZ. There is one instance per top-level
+ * compositor, or in other words, one instance per concrete APZCTreeManager
+ * instance. The child side lives on the main thread in the UI process,
+ * and the parent side lives on the main thread in the GPU process. If there
+ * is no GPU process, then this protocol is not instantiated.
+ */
+sync refcounted protocol PAPZInputBridge
+{
+manager PGPU;
+
+parent:
+ // The following messages are used to
+ // implement the ReceiveInputEvent methods
+
+ sync ReceiveMultiTouchInputEvent(MultiTouchInput aEvent)
+ returns (APZEventResult aOutResult,
+ MultiTouchInput aOutEvent);
+
+ sync ReceiveMouseInputEvent(MouseInput aEvent)
+ returns (APZEventResult aOutResult,
+ MouseInput aOutEvent);
+
+ sync ReceivePanGestureInputEvent(PanGestureInput aEvent)
+ returns (APZEventResult aOutResult,
+ PanGestureInput aOutEvent);
+
+ sync ReceivePinchGestureInputEvent(PinchGestureInput aEvent)
+ returns (APZEventResult aOutResult,
+ PinchGestureInput aOutEvent);
+
+ sync ReceiveTapGestureInputEvent(TapGestureInput aEvent)
+ returns (APZEventResult aOutResult,
+ TapGestureInput aOutEvent);
+
+ sync ReceiveScrollWheelInputEvent(ScrollWheelInput aEvent)
+ returns (APZEventResult aOutResult,
+ ScrollWheelInput aOutEvent);
+
+ sync ReceiveKeyboardInputEvent(KeyboardInput aEvent)
+ returns (APZEventResult aOutResult,
+ KeyboardInput aOutEvent);
+
+ async UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage);
+
+ sync ProcessUnhandledEvent(LayoutDeviceIntPoint aRefPoint)
+ returns (LayoutDeviceIntPoint aOutRefPoint,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutFocusSequenceNumber,
+ LayersId aOutLayersId);
+
+ async __delete__();
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/layers/ipc/PCanvas.ipdl b/gfx/layers/ipc/PCanvas.ipdl
new file mode 100644
index 0000000000..2be61bd142
--- /dev/null
+++ b/gfx/layers/ipc/PCanvas.ipdl
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/layers/LayersMessageUtils.h";
+
+using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
+using mozilla::layers::TextureType from "mozilla/layers/LayersTypes.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PCanvas is the IPDL for recorded Canvas drawing.
+ */
+async refcounted protocol PCanvas {
+parent:
+ /**
+ * Initialize a CanvasTranslator for a particular TextureType, which
+ * translates events from a CanvasEventRingBuffer. aReadHandle is the shared
+ * memory handle for the ring buffer. aReaderSem and aWriterSem are handles
+ * for the semaphores to handle waiting on either side.
+ */
+ async InitTranslator(TextureType aTextureType, Handle aReadHandle,
+ CrossProcessSemaphoreHandle aReaderSem,
+ CrossProcessSemaphoreHandle aWriterSem);
+
+ /**
+ * Used to tell the CanvasTranslator to start translating again after it has
+ * stopped due to a timeout waiting for events.
+ */
+ async ResumeTranslation();
+
+ child:
+ /**
+ * Notify that the canvas device used by the translator has changed.
+ */
+ async NotifyDeviceChanged();
+
+ /**
+ * Deactivate remote canvas, which will cause fall back to software.
+ */
+ async Deactivate();
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl
new file mode 100644
index 0000000000..1a14ac5d4b
--- /dev/null
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include PlatformWidgetTypes;
+include PCompositorBridgeTypes;
+include protocol PAPZ;
+include protocol PAPZCTreeManager;
+include protocol PBrowser;
+include protocol PCanvas;
+include protocol PCompositorManager;
+include protocol PCompositorWidget;
+include protocol PLayerTransaction;
+include protocol PTexture;
+include protocol PWebGL;
+include protocol PWebRenderBridge;
+include protocol PWebGPU;
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+using struct mozilla::null_t from "mozilla/ipc/IPCCore.h";
+using struct mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.h";
+using mozilla::layers::WindowKind from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::CSSIntRegion from "Units.h";
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using mozilla::LayoutDeviceIntRegion from "Units.h";
+using mozilla::LayoutDeviceIntSize from "Units.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
+using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h";
+using base::ProcessId from "base/process.h";
+using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
+using struct DxgiAdapterDesc from "mozilla/D3DMessageUtils.h";
+
+namespace mozilla {
+namespace layers {
+
+struct FrameStats {
+ TransactionId id;
+ TimeStamp compositeStart;
+ TimeStamp renderStart;
+ TimeStamp compositeEnd;
+ int32_t contentFrameTime;
+ double resourceUploadTime;
+ double gpuCacheUploadTime;
+ TimeStamp transactionStart;
+ TimeStamp refreshStart;
+ TimeStamp fwdTime;
+ TimeStamp sceneBuiltTime;
+ uint32_t skippedComposites;
+ nsCString url;
+};
+
+/**
+ * The PCompositorBridge protocol is a top-level protocol for the compositor.
+ * There is an instance of the protocol for each compositor, plus one for each
+ * content process. In other words:
+ * - There is a CompositorBridgeParent/CompositorBridgeChild pair created
+ * for each "top level browser window", which has its own compositor. The
+ * CompositorBridgeChild instance lives in the UI process, and the
+ * CompositorBridgeParent lives in the GPU process (if there is one) or the
+ * UI process otherwise.
+ * - There is also a ContentCompositorBridgeParent/CompositorBridgeChild
+ * pair created for each content process. The ContentCompositorBridgeParent
+ * lives in the GPU process (if there is one) or the UI process otherwise. The
+ * CompositorBridgeChild is a singleton in the content process. Note that
+ * a given content process may have multiple browser instances (represented
+ * by BrowserChild instances), that are attached to different windows, and therefore
+ * rendered by different compositors. This means that when a browser instance
+ * sends messages via its CompositorBridgeChild, the corresponding
+ * ContentCompositorBridgeParent has to use the layers id in the message
+ * to find the correct compositor or CompositorBridgeParent to pass the message
+ * on to.
+ *
+ * One of the main goals of this protocol is to manage the PLayerTransaction sub-
+ * protocol, which is per-browser. A lot of the functions in the protocol are
+ * basically multiplexing/demultiplexing stuff in PLayerTransaction.
+ */
+sync refcounted protocol PCompositorBridge
+{
+ manager PCompositorManager;
+
+ manages PAPZ;
+ manages PAPZCTreeManager;
+ // A Compositor manages a single Layer Manager (PLayerTransaction)
+ manages PLayerTransaction;
+ manages PTexture;
+ manages PCompositorWidget;
+ manages PWebRenderBridge;
+ manages PWebGL;
+ manages PWebGPU;
+
+child:
+ // The child should invalidate retained layers. This is used for local
+ // compositor device resets, such as in CompositorD3D9, and ensures that
+ // TextureSources are recreated.
+ async InvalidateLayers(LayersId layersId);
+
+ // The compositor completed a layers transaction. id is the layers id
+ // of the child layer tree that was composited (or 0 when notifying
+ // the root layer tree).
+ // transactionId is the id of the transaction before this composite, or 0
+ // if there was no transaction since the last composite.
+ prio(mediumhigh) async DidComposite(LayersId id, TransactionId transactionId,
+ TimeStamp compositeStart,
+ TimeStamp compositeEnd);
+
+ async NotifyFrameStats(FrameStats[] aFrameStats);
+
+ /**
+ * Parent informs the child that the graphics objects are ready for
+ * compositing. This usually means that the graphics objects (textures
+ * and the like) are available on the GPU. This is used for chrome UI.
+ * @see RequestNotifyAfterRemotePaint
+ * @see PBrowser
+ */
+ async RemotePaintIsReady();
+
+ /**
+ * Bounce plugin widget configurations over to the main thread for
+ * application on the widgets. Used on Windows and Linux in managing
+ * plugin widgets.
+ */
+ async UpdatePluginConfigurations(LayoutDeviceIntPoint aContentOffset,
+ LayoutDeviceIntRegion aVisibleRegion,
+ PluginWindowData[] aPlugins);
+
+ /**
+ * Captures an image for all visible child plugins of a given widget for use
+ * during scrolling.
+ * @param aParentWidget parent of widgets to be captured
+ */
+ async CaptureAllPlugins(uintptr_t aParentWidget);
+
+ /**
+ * Hides all registered plugin widgets associated with a particular chrome
+ * widget.
+ */
+ async HideAllPlugins(uintptr_t aParentWidget);
+
+ async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
+
+ async ObserveLayersUpdate(LayersId aLayersId, LayersObserverEpoch aEpoch, bool aActive);
+
+ async CompositorOptionsChanged(LayersId id, CompositorOptions newOptions);
+
+ async NotifyJankedAnimations(LayersId id, uint64_t[] aJankedAnimations);
+
+parent:
+ async __delete__();
+
+ // Must be called before Initialize().
+ async PCompositorWidget(CompositorWidgetInitData aInitData);
+
+ // When out-of-process, this must be called to finish initialization.
+ sync Initialize(LayersId rootLayerTreeId);
+
+ // Must be called after Initialize(), and only succeeds if AsyncPanZoomEnabled() is true.
+ async PAPZ(LayersId layersId);
+ async PAPZCTreeManager(LayersId layersId);
+
+ // Constructor for WebGPU IPDL
+ // Must be called before Initialize().
+ async PWebGPU();
+
+ /**
+ * Confirmation callback for UpdatePluginConfigurations and HideAllPlugins.
+ */
+ async RemotePluginsReady();
+
+ // The child is about to be destroyed, so perform any necessary cleanup.
+ sync WillClose();
+
+ // Pause/resume the compositor. These are intended to be used on mobile, when
+ // the compositor needs to pause/resume in lockstep with the application.
+ sync Pause();
+ sync Resume();
+ async ResumeAsync();
+
+ // See bug 1316632 comment #33 for why this has to be sync. Otherwise,
+ // there are ordering issues with SendPLayerTransactionConstructor.
+ sync NotifyChildCreated(LayersId id)
+ returns (CompositorOptions compositorOptions);
+
+ // This version of NotifyChildCreated also performs a layer tree mapping.
+ //
+ // See bug 1316632 comment #33 for why this has to be sync. Otherwise,
+ // there are ordering issues with SendPLayerTransactionConstructor.
+ sync MapAndNotifyChildCreated(LayersId id, ProcessId owner)
+ returns (CompositorOptions compositorOptions);
+
+ async AdoptChild(LayersId id);
+
+ // Same as NotifyChildCreated, but used when child processes need to
+ // reassociate layers. This must be synchronous to ensure that the
+ // association happens before PLayerTransactions are sent over the
+ // cross-process bridge.
+ sync NotifyChildRecreated(LayersId id)
+ returns (CompositorOptions compositorOptions);
+
+ // Make a snapshot of the content that would have been drawn to our
+ // render target at the time this message is received. If the size
+ // or format of |inSnapshot| doesn't match our render target,
+ // results are undefined.
+ //
+ // NB: this message will result in animations, transforms, effects,
+ // and so forth being interpolated. That's what we want to happen.
+ sync MakeSnapshot(SurfaceDescriptor inSnapshot, IntRect dirtyRect);
+
+ // Make sure any pending composites are started immediately and
+ // block until they are completed.
+ sync FlushRendering();
+
+ // Same as FlushRendering, but asynchronous, since not all platforms require
+ // synchronous repaints on resize.
+ async FlushRenderingAsync();
+
+ // Make sure any pending composites have been received.
+ sync WaitOnTransactionProcessed();
+
+ // Force an additional frame presentation to be executed. This is used to
+ // work around a windows presentation bug (See Bug 1232042)
+ async ForcePresent();
+
+ sync StartFrameTimeRecording(int32_t bufferSize)
+ returns (uint32_t startIndex);
+
+ sync StopFrameTimeRecording(uint32_t startIndex)
+ returns (float[] intervals);
+
+ // layersBackendHints is an ordered list of preffered backends where
+ // layersBackendHints[0] is the best backend. If any hints are LayersBackend::LAYERS_NONE
+ // that hint is ignored.
+ async PLayerTransaction(LayersBackend[] layersBackendHints, LayersId id);
+
+ // Notify the compositor that a region of the screen has been invalidated.
+ async NotifyRegionInvalidated(nsIntRegion region);
+
+ /**
+ * The child (content/chrome thread) requests that the parent inform it when
+ * the graphics objects are ready to display.
+ * @see PBrowser
+ * @see RemotePaintIsReady
+ */
+ async RequestNotifyAfterRemotePaint();
+
+ /**
+ * Sent when the child has finished CaptureAllPlugins.
+ */
+ async AllPluginsCaptured();
+
+ async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend, TextureFlags aTextureFlags, LayersId id, uint64_t aSerial, MaybeExternalImageId aExternalImageId);
+
+ async InitPCanvasParent(Endpoint<PCanvasParent> aEndpoint);
+ async ReleasePCanvasParent();
+
+ sync SyncWithCompositor();
+
+ // The pipelineId is the same as the layersId
+ async PWebRenderBridge(PipelineId pipelineId, LayoutDeviceIntSize aSize, WindowKind aKind);
+
+ sync CheckContentOnlyTDR(uint32_t sequenceNum)
+ returns (bool isContentOnlyTDR);
+
+ async BeginRecording(TimeStamp aRecordingStart)
+ returns (bool success);
+
+ async EndRecordingToDisk()
+ returns (bool success);
+
+ async EndRecordingToMemory()
+ returns (CollectedFramesParams? frames);
+
+ sync SupportsAsyncDXGISurface()
+ returns (bool value);
+ sync PreferredDXGIAdapter()
+ returns (DxgiAdapterDesc desc);
+
+ // To set up sharing the composited output to Firefox Reality on Desktop
+ async RequestFxrOutput();
+
+ // Actor that represents one WebGL context.
+ async PWebGL();
+
+child:
+ // Send back Compositor Frame Metrics from APZCs so tiled layers can
+ // update progressively.
+ async SharedCompositorFrameMetrics(Handle metrics, CrossProcessMutexHandle mutex, LayersId aLayersId, uint32_t aAPZCId);
+ async ReleaseSharedCompositorFrameMetrics(ViewID aId, uint32_t aAPZCId);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh b/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh
new file mode 100644
index 0000000000..e1f3a76fdb
--- /dev/null
+++ b/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+namespace mozilla {
+namespace layers {
+
+struct CollectedFrameParams {
+ double timeOffset;
+ uint32_t length;
+};
+
+struct CollectedFramesParams {
+ double recordingStart;
+ CollectedFrameParams[] frames;
+ Shmem buffer;
+};
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/PCompositorManager.ipdl b/gfx/layers/ipc/PCompositorManager.ipdl
new file mode 100644
index 0000000000..93f08e532b
--- /dev/null
+++ b/gfx/layers/ipc/PCompositorManager.ipdl
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PCompositorBridge;
+include LayersSurfaces;
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+using mozilla::TimeDuration from "mozilla/TimeStamp.h";
+using mozilla::CSSToLayoutDeviceScale from "Units.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
+using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MemoryReport from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::WebRenderError from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::layers::SharedSurfacesMemoryReport from "mozilla/layers/SharedSurfacesMemoryReport.h";
+
+namespace mozilla {
+namespace layers {
+
+struct WidgetCompositorOptions {
+ CSSToLayoutDeviceScale scale;
+ TimeDuration vsyncRate;
+ CompositorOptions options;
+ bool useExternalSurfaceSize;
+ IntSize surfaceSize;
+};
+
+struct ContentCompositorOptions {
+};
+
+struct SameProcessWidgetCompositorOptions {
+};
+
+union CompositorBridgeOptions {
+ ContentCompositorOptions;
+ WidgetCompositorOptions;
+ SameProcessWidgetCompositorOptions;
+};
+
+/**
+ * The PCompositorManager protocol is the top-level protocol between the
+ * compositor thread and the creators of compositors. It exists in the
+ * following conditions:
+ * - One PCompositorManager between the GPU process and each content process.
+ * If the GPU/UI processes are combined, there is one PCompositorManager
+ * between the combined GPU/UI process and each content process.
+ * - One PCompositorManager between the GPU process and the UI process. If
+ * they are combined, there is still one PCompositorManager, but both the
+ * child and parent live in the same process.
+ * The intention for this protocol is to facilitate communication with the
+ * compositor thread for compositor data that is only shared once, rather than
+ * per PCompositorBridge instance.
+ */
+sync protocol PCompositorManager
+{
+ manages PCompositorBridge;
+
+parent:
+ /**
+ * There are three variants of a PCompositorBridge protocol, each of which can
+ * only be created by certain processes and configurations:
+ * - A "content" PCompositorBridge is requested by each content process,
+ * representing the drawable area for Web content.
+ * - A "widget" PCompositorBridge is requested by the UI process for each
+ * "top level browser window" for chrome and such.
+ * - A "same process widget" PCompositorBridge is requested by the combined
+ * GPU/UI process for each "top level browser window" as above.
+ * See gfx/layers/ipc/PCompositorBridge.ipdl for more details.
+ */
+ async PCompositorBridge(CompositorBridgeOptions options);
+
+ async AddSharedSurface(ExternalImageId aId, SurfaceDescriptorShared aDesc);
+ async RemoveSharedSurface(ExternalImageId aId);
+ async ReportSharedSurfacesMemory() returns (SharedSurfacesMemoryReport aReport);
+
+ async NotifyMemoryPressure();
+
+ async ReportMemory() returns (MemoryReport aReport);
+
+child:
+ async NotifyWebRenderError(WebRenderError error);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PImageBridge.ipdl b/gfx/layers/ipc/PImageBridge.ipdl
new file mode 100644
index 0000000000..73afb55d4a
--- /dev/null
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PTexture;
+include ProtocolTypes;
+include protocol PMediaSystemResourceManager;
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PImageBridge protocol is used to allow isolated threads or processes to push
+ * frames directly to the compositor thread/process without relying on the main thread
+ * which might be too busy dealing with content script.
+ */
+sync protocol PImageBridge
+{
+ manages PTexture;
+ manages PMediaSystemResourceManager;
+
+child:
+ async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
+
+ async DidComposite(ImageCompositeNotification[] aNotifications);
+
+ // Report the number of frames dropped for the given CompositableHost.
+ async ReportFramesDropped(CompositableHandle aHandle, uint32_t aFrames);
+
+parent:
+ async Update(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId);
+
+ // First step of the destruction sequence. This puts ImageBridge
+ // in a state in which it can't send asynchronous messages
+ // so as to not race with the channel getting closed.
+ // In the child side, the Closing the channel does not happen right after WillClose,
+ // it is scheduled in the ImageBridgeChild's message queue in order to ensure
+ // that all of the messages from the parent side have been received and processed
+ // before sending closing the channel.
+ async WillClose();
+
+ async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t aSerial, MaybeExternalImageId aExternalImageId);
+ async PMediaSystemResourceManager();
+
+ sync NewCompositable(CompositableHandle aHandle, TextureInfo aInfo, LayersBackend aBackend);
+ async ReleaseCompositable(CompositableHandle aHandle);
+
+ sync MakeAsyncPluginSurfaces(SurfaceFormat format, IntSize size)
+ returns (SurfaceDescriptorPlugin aSD);
+ async UpdateAsyncPluginSurface(SurfaceDescriptorPlugin aSD);
+ sync ReadbackAsyncPluginSurface(SurfaceDescriptorPlugin aSD)
+ returns (SurfaceDescriptor result);
+ async RemoveAsyncPluginSurface(SurfaceDescriptorPlugin aSD, bool isFrontSurface);
+};
+
+
+} // namespace
+} // namespace
+
diff --git a/gfx/layers/ipc/PLayerTransaction.ipdl b/gfx/layers/ipc/PLayerTransaction.ipdl
new file mode 100644
index 0000000000..ddc50216e5
--- /dev/null
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PCompositorBridge;
+include protocol PTexture;
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+using struct mozilla::null_t from "mozilla/ipc/IPCCore.h";
+using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
+using mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
+using mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
+
+/**
+ * The layers protocol is spoken between thread contexts that manage
+ * layer (sub)trees. The protocol comprises atomically publishing
+ * layer subtrees to a "shadow" thread context (which grafts the
+ * subtree into its own tree), and atomically updating a published
+ * subtree. ("Atomic" in this sense is wrt painting.)
+ */
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PLayerTransaction protocol manages the layer tree for a single "browser".
+ * The "browser" can be a top-level browser window, in which case the PLayer-
+ * TransactionChild exists in the UI process. The "browser" can also be a content
+ * tab, in which case the PLayerTransactionChild exists in the content process.
+ * In either case, the PLayerTransactionParent exists in the GPU process (if
+ * there is one) or the UI process otherwise.
+ */
+sync protocol PLayerTransaction {
+ manager PCompositorBridge;
+
+parent:
+ // The isFirstPaint flag can be used to indicate that this is the first update
+ // for a particular document.
+ async Update(TransactionInfo txn);
+
+ async PaintTime(TransactionId id, TimeDuration paintTime);
+
+ async SetLayersObserverEpoch(LayersObserverEpoch aChildEpoch);
+
+ // Create a new Compositable.
+ async NewCompositable(CompositableHandle handle, TextureInfo info);
+
+ // Release an object that is no longer in use.
+ async ReleaseLayer(LayerHandle layer);
+ async ReleaseCompositable(CompositableHandle compositable);
+
+ // Tell the compositor to notify APZ that a layer has been confirmed for an
+ // input event.
+ async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
+
+ // Testing APIs
+
+ // Enter test mode, set the sample time to sampleTime, and resample
+ // animations. sampleTime must not be null.
+ sync SetTestSampleTime(TimeStamp sampleTime);
+ // Leave test mode and resume normal compositing
+ sync LeaveTestMode();
+
+ // Returns |OMTAValue| applied to the layer.
+ sync GetAnimationValue(uint64_t aCompositorAnimationId) returns (OMTAValue value);
+
+ // Returns the value of the transform applied to the layer by animation and
+ // APZC.
+ sync GetTransform(LayerHandle layer) returns (Matrix4x4? transform);
+
+ // The next time the layer tree is composited, add this async scroll offset in
+ // CSS pixels for the given ViewID.
+ // Useful for testing rendering of async scrolling.
+ sync SetAsyncScrollOffset(ViewID id, float x, float y);
+
+ // The next time the layer tree is composited, include this async zoom in
+ // for the given ViewID.
+ // Useful for testing rendering of async zooming.
+ sync SetAsyncZoom(ViewID id, float zoom);
+
+ // Flush any pending APZ repaints to the main thread.
+ async FlushApzRepaints();
+
+ // Drop any front buffers that might be retained on the compositor
+ // side.
+ async ClearCachedResources();
+
+ // Schedule a composite if one isn't already scheduled.
+ async ScheduleComposite();
+
+ // Get a copy of the compositor-side APZ test data instance for this
+ // layers id.
+ sync GetAPZTestData() returns (APZTestData data);
+
+ // Child requests frame uniformity measurements
+ sync GetFrameUniformity() returns (FrameUniformityData data);
+
+ // Query a named property from the last frame
+ sync RequestProperty(nsString property) returns (float value);
+
+ // Return the TextureFactoryIdentifier for this compositor.
+ sync GetTextureFactoryIdentifier() returns (TextureFactoryIdentifier aIdentifier);
+
+ async RecordPaintTimes(PaintTiming timing);
+
+ async Shutdown();
+ sync ShutdownSync();
+
+child:
+ async __delete__();
+};
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/PTexture.ipdl b/gfx/layers/ipc/PTexture.ipdl
new file mode 100644
index 0000000000..b82a757bbc
--- /dev/null
+++ b/gfx/layers/ipc/PTexture.ipdl
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include protocol PLayerTransaction;
+include protocol PCompositorBridge;
+include protocol PImageBridge;
+include protocol PVideoBridge;
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PTexture is the IPDL glue between a TextureClient and a TextureHost.
+ */
+sync protocol PTexture {
+ manager PImageBridge or PCompositorBridge or PVideoBridge;
+
+child:
+ async __delete__();
+
+parent:
+ /**
+ * Asynchronously tell the compositor side to remove the texture.
+ */
+ async Destroy();
+
+ async RecycleTexture(TextureFlags aTextureFlags);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PUiCompositorController.ipdl b/gfx/layers/ipc/PUiCompositorController.ipdl
new file mode 100644
index 0000000000..515fcfd023
--- /dev/null
+++ b/gfx/layers/ipc/PUiCompositorController.ipdl
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using CSSRect from "Units.h";
+using CSSToScreenScale from "Units.h";
+using ScreenIntSize from "Units.h";
+using ScreenPoint from "Units.h";
+
+include "mozilla/GfxMessageUtils.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PUiCompositorController protocol is used to pause and resume the
+ * compositor from the UI thread. Primarily used on Android to coordinate registering and
+ * releasing the surface with the compositor.
+ */
+sync protocol PUiCompositorController
+{
+
+parent:
+ // Pause/resume the compositor. These are intended to be used on mobile, when
+ // the compositor needs to pause/resume in lockstep with the application.
+ sync Pause();
+ sync Resume();
+ sync ResumeAndResize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight);
+
+ async InvalidateAndRender();
+ async MaxToolbarHeight(int32_t aHeight);
+ async FixedBottomOffset(int32_t aOffset);
+ async DefaultClearColor(uint32_t aColor);
+ async RequestScreenPixels();
+ async EnableLayerUpdateNotifications(bool aEnable);
+child:
+ async ToolbarAnimatorMessageFromCompositor(int32_t aMessage);
+ async RootFrameMetrics(ScreenPoint aScrollOffset, CSSToScreenScale aZoom);
+ async ScreenPixels(Shmem aMem, ScreenIntSize aSize, bool aNeedsYFlip);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PVideoBridge.ipdl b/gfx/layers/ipc/PVideoBridge.ipdl
new file mode 100644
index 0000000000..5a4270981b
--- /dev/null
+++ b/gfx/layers/ipc/PVideoBridge.ipdl
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PTexture;
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PVideoBridge protocol is used to share textures from the video decoders
+ * to the compositor.
+ */
+sync protocol PVideoBridge
+{
+ manages PTexture;
+
+parent:
+ async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend,
+ TextureFlags aTextureFlags, uint64_t aSerial);
+};
+
+} // namespace
+} // namespace
+
diff --git a/gfx/layers/ipc/PWebRenderBridge.ipdl b/gfx/layers/ipc/PWebRenderBridge.ipdl
new file mode 100644
index 0000000000..8e19731230
--- /dev/null
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+include WebRenderMessages;
+include protocol PCompositorBridge;
+include protocol PTexture;
+
+using mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
+using mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
+using mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositionPayload from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MaybeIdNamespace from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::ExternalImageKeyPair from "mozilla/webrender/WebRenderTypes.h";
+using moveonly mozilla::layers::DisplayListData from "mozilla/layers/RenderRootTypes.h";
+using moveonly mozilla::layers::MaybeTransactionData from "mozilla/layers/RenderRootTypes.h";
+using mozilla::layers::FocusTarget from "mozilla/layers/FocusTarget.h";
+using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
+using mozilla::VsyncId from "mozilla/VsyncDispatcher.h";
+
+namespace mozilla {
+namespace layers {
+
+sync protocol PWebRenderBridge
+{
+ manager PCompositorBridge;
+
+parent:
+ sync EnsureConnected()
+ returns (TextureFactoryIdentifier textureFactoryIdentifier, MaybeIdNamespace maybeIdNamespace, nsCString error);
+
+ async NewCompositable(CompositableHandle handle, TextureInfo info);
+ async ReleaseCompositable(CompositableHandle compositable);
+
+ async DeleteCompositorAnimations(uint64_t[] aIds);
+ async SetDisplayList(DisplayListData displayList,
+ OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
+ bool containsSVGGroup,
+ VsyncId vsyncId, TimeStamp vsyncStartTime,
+ TimeStamp refreshStartTime, TimeStamp txnStartTime, nsCString txnURL, TimeStamp fwdTime,
+ CompositionPayload[] payloads);
+ async EmptyTransaction(FocusTarget focusTarget,
+ MaybeTransactionData transationData,
+ OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
+ VsyncId vsyncId, TimeStamp vsyncStartTime,
+ TimeStamp refreshStartTime, TimeStamp txnStartTime,
+ nsCString txnURL, TimeStamp fwdTime,
+ CompositionPayload[] payloads);
+ async SetFocusTarget(FocusTarget focusTarget);
+ async UpdateResources(IdNamespace aIdNamespace, OpUpdateResource[] aResourceUpdates,
+ RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems);
+ async ParentCommands(WebRenderParentCommand[] commands);
+ sync GetSnapshot(PTexture texture) returns (bool aNeedsYFlip);
+ async SetLayersObserverEpoch(LayersObserverEpoch childEpoch);
+ async ClearCachedResources();
+ // Invalidate rendered frame
+ async InvalidateRenderedFrame();
+ // Schedule a composite if one isn't already scheduled.
+ async ScheduleComposite();
+ // Save the frame capture to disk
+ async Capture();
+ // Start capturing each frame and save to disk, and if already started, stop.
+ async ToggleCaptureSequence();
+
+ // Replacement for PCompositorBridge::SyncWithCompositor, but for WR. We need
+ // it on PWebRenderBridge because it associated with a particular top-level
+ // window, and PCompositorBridge doesn't allow doing that in a secure manner.
+ sync SyncWithCompositor();
+
+ // These correspond exactly to the equivalent APIs in PLayerTransaction -
+ // see those for documentation.
+ async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
+ // More copied from PLayerTransaction, but these are only used for testing.
+ sync SetTestSampleTime(TimeStamp sampleTime);
+ sync LeaveTestMode();
+ sync GetAnimationValue(uint64_t aCompositorAnimationsId) returns (OMTAValue value);
+ sync SetAsyncScrollOffset(ViewID scrollId, float x, float y);
+ sync SetAsyncZoom(ViewID scrollId, float zoom);
+ async FlushApzRepaints();
+ sync GetAPZTestData() returns (APZTestData data);
+ sync GetFrameUniformity() returns (FrameUniformityData data);
+
+ async Shutdown();
+ sync ShutdownSync();
+child:
+ async WrUpdated(IdNamespace aNewIdNamespace, TextureFactoryIdentifier textureFactoryIdentifier);
+ async WrReleasedImages(ExternalImageKeyPair[] pairs);
+ async __delete__();
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/RefCountedShmem.cpp b/gfx/layers/ipc/RefCountedShmem.cpp
new file mode 100644
index 0000000000..205fa990fd
--- /dev/null
+++ b/gfx/layers/ipc/RefCountedShmem.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RefCountedShmem.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/WebRenderMessages.h"
+
+#define SHM_REFCOUNT_HEADER_SIZE 16
+
+namespace mozilla {
+namespace layers {
+
+uint8_t* RefCountedShm::GetBytes(const RefCountedShmem& aShm) {
+ uint8_t* data = aShm.buffer().get<uint8_t>();
+ if (!data) {
+ return nullptr;
+ }
+ return data + SHM_REFCOUNT_HEADER_SIZE;
+}
+
+size_t RefCountedShm::GetSize(const RefCountedShmem& aShm) {
+ if (!IsValid(aShm)) {
+ return 0;
+ }
+ size_t totalSize = aShm.buffer().Size<uint8_t>();
+ if (totalSize < SHM_REFCOUNT_HEADER_SIZE) {
+ return 0;
+ }
+
+ return totalSize - SHM_REFCOUNT_HEADER_SIZE;
+}
+
+bool RefCountedShm::IsValid(const RefCountedShmem& aShm) {
+ return aShm.buffer().IsWritable() &&
+ aShm.buffer().Size<uint8_t>() > SHM_REFCOUNT_HEADER_SIZE;
+}
+
+int32_t RefCountedShm::GetReferenceCount(const RefCountedShmem& aShm) {
+ if (!IsValid(aShm)) {
+ return 0;
+ }
+
+ return *aShm.buffer().get<Atomic<int32_t>>();
+}
+
+int32_t RefCountedShm::AddRef(const RefCountedShmem& aShm) {
+ if (!IsValid(aShm)) {
+ return 0;
+ }
+
+ auto* counter = aShm.buffer().get<Atomic<int32_t>>();
+ if (counter) {
+ return (*counter)++;
+ }
+ return 0;
+}
+
+int32_t RefCountedShm::Release(const RefCountedShmem& aShm) {
+ if (!IsValid(aShm)) {
+ return 0;
+ }
+
+ auto* counter = aShm.buffer().get<Atomic<int32_t>>();
+ if (counter) {
+ return --(*counter);
+ }
+
+ return 0;
+}
+
+bool RefCountedShm::Alloc(mozilla::ipc::IProtocol* aAllocator, size_t aSize,
+ RefCountedShmem& aShm) {
+ MOZ_ASSERT(!IsValid(aShm));
+ auto shmType = ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
+ auto size = aSize + SHM_REFCOUNT_HEADER_SIZE;
+ if (!aAllocator->AllocUnsafeShmem(size, shmType, &aShm.buffer())) {
+ return false;
+ }
+ return true;
+}
+
+void RefCountedShm::Dealloc(mozilla::ipc::IProtocol* aAllocator,
+ RefCountedShmem& aShm) {
+ aAllocator->DeallocShmem(aShm.buffer());
+ aShm.buffer() = ipc::Shmem();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/RefCountedShmem.h b/gfx/layers/ipc/RefCountedShmem.h
new file mode 100644
index 0000000000..390d6ea25f
--- /dev/null
+++ b/gfx/layers/ipc/RefCountedShmem.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_REFCOUNTED_SHMEM_H
+#define MOZILLA_LAYERS_REFCOUNTED_SHMEM_H
+
+#include "mozilla/ipc/Shmem.h"
+#include "chrome/common/ipc_message_utils.h"
+
+namespace mozilla {
+namespace ipc {
+class IProtocol;
+}
+
+namespace layers {
+
+// This class is IPDL-defined
+class RefCountedShmem;
+
+// This just implement the methods externally.
+class RefCountedShm {
+ public:
+ static uint8_t* GetBytes(const RefCountedShmem& aShm);
+
+ static size_t GetSize(const RefCountedShmem& aShm);
+
+ static bool IsValid(const RefCountedShmem& aShm);
+
+ static bool Alloc(mozilla::ipc::IProtocol* aAllocator, size_t aSize,
+ RefCountedShmem& aShm);
+
+ static void Dealloc(mozilla::ipc::IProtocol* aAllocator,
+ RefCountedShmem& aShm);
+
+ static int32_t GetReferenceCount(const RefCountedShmem& aShm);
+
+ static int32_t AddRef(const RefCountedShmem& aShm);
+
+ static int32_t Release(const RefCountedShmem& aShm);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/RemoteContentController.cpp b/gfx/layers/ipc/RemoteContentController.cpp
new file mode 100644
index 0000000000..8b110f1397
--- /dev/null
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -0,0 +1,425 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/RemoteContentController.h"
+
+#include "CompositorThread.h"
+#include "MainThreadUtils.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/MatrixMessage.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/Unused.h"
+#include "Units.h"
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/jni/Utils.h"
+#endif
+
+static mozilla::LazyLogModule sApzRemoteLog("apz.cc.remote");
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+RemoteContentController::RemoteContentController()
+ : mCompositorThread(NS_GetCurrentThread()), mCanSend(true) {
+ MOZ_ASSERT(CompositorThread()->IsOnCurrentThread());
+}
+
+RemoteContentController::~RemoteContentController() = default;
+
+void RemoteContentController::NotifyLayerTransforms(
+ nsTArray<MatrixMessage>&& aTransforms) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<StoreCopyPassByRRef<nsTArray<MatrixMessage>>>(
+ "layers::RemoteContentController::NotifyLayerTransforms", this,
+ &RemoteContentController::NotifyLayerTransforms,
+ std::move(aTransforms)));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendLayerTransforms(aTransforms);
+ }
+}
+
+void RemoteContentController::RequestContentRepaint(
+ const RepaintRequest& aRequest) {
+ MOZ_ASSERT(IsRepaintThread());
+
+ if (mCanSend) {
+ Unused << SendRequestContentRepaint(aRequest);
+ }
+}
+
+void RemoteContentController::HandleTapOnMainThread(TapType aTapType,
+ LayoutDevicePoint aPoint,
+ Modifiers aModifiers,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId) {
+ MOZ_LOG(sApzRemoteLog, LogLevel::Debug,
+ ("HandleTapOnMainThread(%d)", (int)aTapType));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ dom::BrowserParent* tab =
+ dom::BrowserParent::GetBrowserParentFromLayersId(aGuid.mLayersId);
+ if (tab) {
+ tab->SendHandleTap(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
+ }
+}
+
+void RemoteContentController::HandleTapOnCompositorThread(
+ TapType aTapType, LayoutDevicePoint aPoint, Modifiers aModifiers,
+ ScrollableLayerGuid aGuid, uint64_t aInputBlockId) {
+ MOZ_ASSERT(XRE_IsGPUProcess());
+ MOZ_ASSERT(mCompositorThread->IsOnCurrentThread());
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on the
+ // compositor thread.
+ APZCTreeManagerParent* apzctmp =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId);
+ if (apzctmp) {
+ Unused << apzctmp->SendHandleTap(aTapType, aPoint, aModifiers, aGuid,
+ aInputBlockId);
+ }
+}
+
+void RemoteContentController::HandleTap(TapType aTapType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) {
+ MOZ_LOG(sApzRemoteLog, LogLevel::Debug, ("HandleTap(%d)", (int)aTapType));
+ APZThreadUtils::AssertOnControllerThread();
+
+ if (XRE_GetProcessType() == GeckoProcessType_GPU) {
+ if (mCompositorThread->IsOnCurrentThread()) {
+ HandleTapOnCompositorThread(aTapType, aPoint, aModifiers, aGuid,
+ aInputBlockId);
+ } else {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers,
+ ScrollableLayerGuid, uint64_t>(
+ "layers::RemoteContentController::HandleTapOnCompositorThread",
+ this, &RemoteContentController::HandleTapOnCompositorThread,
+ aTapType, aPoint, aModifiers, aGuid, aInputBlockId));
+ }
+ return;
+ }
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (NS_IsMainThread()) {
+ HandleTapOnMainThread(aTapType, aPoint, aModifiers, aGuid, aInputBlockId);
+ } else {
+ // We must be on Android, running on the Java UI thread
+#ifndef MOZ_WIDGET_ANDROID
+ MOZ_ASSERT(false);
+#else
+ // We don't want to get the BrowserParent or call
+ // BrowserParent::SendHandleTap() from a non-main thread, so we need to
+ // redispatch to the main thread. However, we should use the same mechanism
+ // that the Android widget uses when dispatching input events to Gecko,
+ // which is nsAppShell::PostEvent. Note in particular that using
+ // NS_DispatchToMainThread would post to a different message loop, and
+ // introduces the possibility of this tap event getting processed out of
+ // order with respect to the touch events that synthesized it.
+ mozilla::jni::DispatchToGeckoPriorityQueue(
+ NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers,
+ ScrollableLayerGuid, uint64_t>(
+ "layers::RemoteContentController::HandleTapOnMainThread", this,
+ &RemoteContentController::HandleTapOnMainThread, aTapType, aPoint,
+ aModifiers, aGuid, aInputBlockId));
+#endif
+ }
+}
+
+void RemoteContentController::NotifyPinchGestureOnCompositorThread(
+ PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) {
+ MOZ_ASSERT(mCompositorThread->IsOnCurrentThread());
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on the
+ // compositor thread.
+ APZCTreeManagerParent* apzctmp =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId);
+ if (apzctmp) {
+ Unused << apzctmp->SendNotifyPinchGesture(aType, aGuid, aFocusPoint,
+ aSpanChange, aModifiers);
+ }
+}
+
+void RemoteContentController::NotifyPinchGesture(
+ PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ // For now we only ever want to handle this NotifyPinchGesture message in
+ // the parent process, even if the APZ is sending it to a content process.
+
+ // If we're in the GPU process, try to find a handle to the parent process
+ // and send it there.
+ if (XRE_IsGPUProcess()) {
+ if (mCompositorThread->IsOnCurrentThread()) {
+ NotifyPinchGestureOnCompositorThread(aType, aGuid, aFocusPoint,
+ aSpanChange, aModifiers);
+ } else {
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<PinchGestureInput::PinchGestureType,
+ ScrollableLayerGuid, LayoutDevicePoint,
+ LayoutDeviceCoord, Modifiers>(
+ "layers::RemoteContentController::"
+ "NotifyPinchGestureOnCompositorThread",
+ this,
+ &RemoteContentController::NotifyPinchGestureOnCompositorThread,
+ aType, aGuid, aFocusPoint, aSpanChange, aModifiers));
+ }
+ return;
+ }
+
+ // If we're in the parent process, handle it directly. We don't have a handle
+ // to the widget though, so we fish out the ChromeProcessController and
+ // delegate to that instead.
+ if (XRE_IsParentProcess()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<GeckoContentController> rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ aGuid.mLayersId);
+ if (rootController) {
+ rootController->NotifyPinchGesture(aType, aGuid, aFocusPoint, aSpanChange,
+ aModifiers);
+ }
+ }
+}
+
+bool RemoteContentController::IsRepaintThread() {
+ return mCompositorThread->IsOnCurrentThread();
+}
+
+void RemoteContentController::DispatchToRepaintThread(
+ already_AddRefed<Runnable> aTask) {
+ mCompositorThread->Dispatch(std::move(aTask));
+}
+
+void RemoteContentController::NotifyAPZStateChange(
+ const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<ScrollableLayerGuid, APZStateChange, int>(
+ "layers::RemoteContentController::NotifyAPZStateChange", this,
+ &RemoteContentController::NotifyAPZStateChange, aGuid, aChange,
+ aArg));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAPZStateChange(aGuid, aChange, aArg);
+ }
+}
+
+void RemoteContentController::UpdateOverscrollVelocity(
+ const ScrollableLayerGuid& aGuid, float aX, float aY, bool aIsRootContent) {
+ if (XRE_IsParentProcess()) {
+#ifdef MOZ_WIDGET_ANDROID
+ // We always want these to go to the parent process on Android
+ if (!NS_IsMainThread()) {
+ mozilla::jni::DispatchToGeckoPriorityQueue(
+ NewRunnableMethod<ScrollableLayerGuid, float, float, bool>(
+ "layers::RemoteContentController::UpdateOverscrollVelocity", this,
+ &RemoteContentController::UpdateOverscrollVelocity, aGuid, aX, aY,
+ aIsRootContent));
+ return;
+ }
+#endif
+
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ RefPtr<GeckoContentController> rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ aGuid.mLayersId);
+ if (rootController) {
+ rootController->UpdateOverscrollVelocity(aGuid, aX, aY, aIsRootContent);
+ }
+ }
+}
+
+void RemoteContentController::UpdateOverscrollOffset(
+ const ScrollableLayerGuid& aGuid, float aX, float aY, bool aIsRootContent) {
+ if (XRE_IsParentProcess()) {
+#ifdef MOZ_WIDGET_ANDROID
+ // We always want these to go to the parent process on Android
+ if (!NS_IsMainThread()) {
+ mozilla::jni::DispatchToGeckoPriorityQueue(
+ NewRunnableMethod<ScrollableLayerGuid, float, float, bool>(
+ "layers::RemoteContentController::UpdateOverscrollOffset", this,
+ &RemoteContentController::UpdateOverscrollOffset, aGuid, aX, aY,
+ aIsRootContent));
+ return;
+ }
+#endif
+
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ RefPtr<GeckoContentController> rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ aGuid.mLayersId);
+ if (rootController) {
+ rootController->UpdateOverscrollOffset(aGuid, aX, aY, aIsRootContent);
+ }
+ }
+}
+
+void RemoteContentController::NotifyMozMouseScrollEvent(
+ const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<ScrollableLayerGuid::ViewID, nsString>(
+ "layers::RemoteContentController::NotifyMozMouseScrollEvent", this,
+ &RemoteContentController::NotifyMozMouseScrollEvent, aScrollId,
+ aEvent));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyMozMouseScrollEvent(aScrollId, aEvent);
+ }
+}
+
+void RemoteContentController::NotifyFlushComplete() {
+ MOZ_ASSERT(IsRepaintThread());
+
+ if (mCanSend) {
+ Unused << SendNotifyFlushComplete();
+ }
+}
+
+void RemoteContentController::NotifyAsyncScrollbarDragInitiated(
+ uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId,
+ ScrollDirection aDirection) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(NewRunnableMethod<uint64_t,
+ ScrollableLayerGuid::ViewID,
+ ScrollDirection>(
+ "layers::RemoteContentController::NotifyAsyncScrollbarDragInitiated",
+ this, &RemoteContentController::NotifyAsyncScrollbarDragInitiated,
+ aDragBlockId, aScrollId, aDirection));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId,
+ aDirection);
+ }
+}
+
+void RemoteContentController::NotifyAsyncScrollbarDragRejected(
+ const ScrollableLayerGuid::ViewID& aScrollId) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
+ "layers::RemoteContentController::NotifyAsyncScrollbarDragRejected",
+ this, &RemoteContentController::NotifyAsyncScrollbarDragRejected,
+ aScrollId));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAsyncScrollbarDragRejected(aScrollId);
+ }
+}
+
+void RemoteContentController::NotifyAsyncAutoscrollRejected(
+ const ScrollableLayerGuid::ViewID& aScrollId) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
+ "layers::RemoteContentController::NotifyAsyncAutoscrollRejected", this,
+ &RemoteContentController::NotifyAsyncAutoscrollRejected, aScrollId));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAsyncAutoscrollRejected(aScrollId);
+ }
+}
+
+void RemoteContentController::CancelAutoscroll(
+ const ScrollableLayerGuid& aGuid) {
+ if (XRE_GetProcessType() == GeckoProcessType_GPU) {
+ CancelAutoscrollCrossProcess(aGuid);
+ } else {
+ CancelAutoscrollInProcess(aGuid);
+ }
+}
+
+void RemoteContentController::CancelAutoscrollInProcess(
+ const ScrollableLayerGuid& aGuid) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(NewRunnableMethod<ScrollableLayerGuid>(
+ "layers::RemoteContentController::CancelAutoscrollInProcess", this,
+ &RemoteContentController::CancelAutoscrollInProcess, aGuid));
+ return;
+ }
+
+ APZCCallbackHelper::CancelAutoscroll(aGuid.mScrollId);
+}
+
+void RemoteContentController::CancelAutoscrollCrossProcess(
+ const ScrollableLayerGuid& aGuid) {
+ MOZ_ASSERT(XRE_IsGPUProcess());
+
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid>(
+ "layers::RemoteContentController::CancelAutoscrollCrossProcess", this,
+ &RemoteContentController::CancelAutoscrollCrossProcess, aGuid));
+ return;
+ }
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on the
+ // compositor thread.
+ if (APZCTreeManagerParent* parent =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(
+ aGuid.mLayersId)) {
+ Unused << parent->SendCancelAutoscroll(aGuid.mScrollId);
+ }
+}
+
+void RemoteContentController::ActorDestroy(ActorDestroyReason aWhy) {
+ // This controller could possibly be kept alive longer after this
+ // by a RefPtr, but it is no longer valid to send messages.
+ mCanSend = false;
+}
+
+void RemoteContentController::Destroy() {
+ if (mCanSend) {
+ mCanSend = false;
+ Unused << SendDestroy();
+ }
+}
+
+mozilla::ipc::IPCResult RemoteContentController::RecvDestroy() {
+ // The actor on the other side is about to get destroyed, so let's not send
+ // it any more messages.
+ mCanSend = false;
+ return IPC_OK();
+}
+
+bool RemoteContentController::IsRemote() { return true; }
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/RemoteContentController.h b/gfx/layers/ipc/RemoteContentController.h
new file mode 100644
index 0000000000..1f18323a3c
--- /dev/null
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_RemoteContentController_h
+#define mozilla_layers_RemoteContentController_h
+
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/PAPZParent.h"
+
+namespace mozilla {
+
+namespace dom {
+class BrowserParent;
+}
+
+namespace layers {
+
+/**
+ * RemoteContentController implements PAPZChild and is used to access a
+ * GeckoContentController that lives in a different process.
+ *
+ * RemoteContentController lives on the compositor thread. All methods can
+ * be called off the compositor thread and will get dispatched to the right
+ * thread, with the exception of RequestContentRepaint and NotifyFlushComplete,
+ * which must be called on the repaint thread, which in this case is the
+ * compositor thread.
+ */
+class RemoteContentController : public GeckoContentController,
+ public PAPZParent {
+ using GeckoContentController::APZStateChange;
+ using GeckoContentController::TapType;
+
+ public:
+ RemoteContentController();
+
+ virtual ~RemoteContentController();
+
+ void NotifyLayerTransforms(nsTArray<MatrixMessage>&& aTransforms) override;
+
+ void RequestContentRepaint(const RepaintRequest& aRequest) override;
+
+ void HandleTap(TapType aTapType, const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers, const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId) override;
+
+ void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) override;
+
+ bool IsRepaintThread() override;
+
+ void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
+
+ void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange, int aArg) override;
+
+ void UpdateOverscrollVelocity(const ScrollableLayerGuid& aGuid, float aX,
+ float aY, bool aIsRootContent) override;
+
+ void UpdateOverscrollOffset(const ScrollableLayerGuid& aGuid, float aX,
+ float aY, bool aIsRootContent) override;
+
+ void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId,
+ const nsString& aEvent) override;
+
+ void NotifyFlushComplete() override;
+
+ void NotifyAsyncScrollbarDragInitiated(
+ uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId,
+ ScrollDirection aDirection) override;
+ void NotifyAsyncScrollbarDragRejected(
+ const ScrollableLayerGuid::ViewID& aScrollId) override;
+
+ void NotifyAsyncAutoscrollRejected(
+ const ScrollableLayerGuid::ViewID& aScrollId) override;
+
+ void CancelAutoscroll(const ScrollableLayerGuid& aScrollId) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ void Destroy() override;
+ mozilla::ipc::IPCResult RecvDestroy();
+
+ bool IsRemote() override;
+
+ private:
+ nsCOMPtr<nsISerialEventTarget> mCompositorThread;
+ bool mCanSend;
+
+ void HandleTapOnMainThread(TapType aType, LayoutDevicePoint aPoint,
+ Modifiers aModifiers, ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ void HandleTapOnCompositorThread(TapType aType, LayoutDevicePoint aPoint,
+ Modifiers aModifiers,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ void NotifyPinchGestureOnCompositorThread(
+ PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid, const LayoutDevicePoint& aFocusPoint,
+ LayoutDeviceCoord aSpanChange, Modifiers aModifiers);
+
+ void CancelAutoscrollInProcess(const ScrollableLayerGuid& aScrollId);
+ void CancelAutoscrollCrossProcess(const ScrollableLayerGuid& aScrollId);
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_RemoteContentController_h
diff --git a/gfx/layers/ipc/ShadowLayerUtils.h b/gfx/layers/ipc/ShadowLayerUtils.h
new file mode 100644
index 0000000000..941ddded1f
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtils.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_ShadowLayerUtils_h
+#define IPC_ShadowLayerUtils_h
+
+#include "ipc/EnumSerializer.h"
+#include "ipc/IPCMessageUtils.h"
+#include "GLContextTypes.h"
+#include "SurfaceDescriptor.h"
+#include "SurfaceTypes.h"
+#include "mozilla/WidgetUtils.h"
+
+#if defined(MOZ_X11)
+# include "mozilla/layers/ShadowLayerUtilsX11.h"
+#endif
+
+namespace IPC {
+
+#if !defined(MOZ_HAVE_SURFACEDESCRIPTORX11)
+template <>
+struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> {
+ typedef mozilla::layers::SurfaceDescriptorX11 paramType;
+ static void Write(Message*, const paramType&) {}
+ static bool Read(const Message*, PickleIterator*, paramType*) {
+ return false;
+ }
+};
+#endif // !defined(MOZ_HAVE_XSURFACEDESCRIPTORX11)
+
+template <>
+struct ParamTraits<mozilla::ScreenRotation>
+ : public ContiguousEnumSerializer<mozilla::ScreenRotation,
+ mozilla::ROTATION_0,
+ mozilla::ROTATION_COUNT> {};
+
+} // namespace IPC
+
+#endif // IPC_ShadowLayerUtils_h
diff --git a/gfx/layers/ipc/ShadowLayerUtilsMac.cpp b/gfx/layers/ipc/ShadowLayerUtilsMac.cpp
new file mode 100644
index 0000000000..891b16e8a2
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsMac.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/PLayerTransaction.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/CompositorTypes.h"
+
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+/*static*/
+void ShadowLayerForwarder::PlatformSyncBeforeUpdate() {}
+
+/*static*/
+void LayerManagerComposite::PlatformSyncBeforeReplyUpdate() {}
+
+/*static*/
+bool LayerManagerComposite::SupportsDirectTexturing() { return false; }
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
new file mode 100644
index 0000000000..25f6ceba21
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ShadowLayerUtilsX11.h"
+#include <X11/X.h> // for Drawable, XID
+#include <X11/Xlib.h> // for Display, Visual, etc
+#include <X11/extensions/Xrender.h> // for XRenderPictFormat, etc
+#include <X11/extensions/render.h> // for PictFormat
+#include "cairo-xlib.h"
+#include "X11UndefineNone.h"
+#include <stdint.h> // for uint32_t
+#include "GLDefs.h" // for GLenum
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxXlibSurface.h" // for gfxXlibSurface
+#include "gfx2DGlue.h" // for Moz2D transistion helpers
+#include "mozilla/X11Util.h" // for DefaultXDisplay, FinishX, etc
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for OpenMode
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessageUtils.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "gfxEnv.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+class TextureImage;
+} // namespace gl
+
+namespace layers {
+
+// Return true if we're likely compositing using X and so should use
+// Xlib surfaces in shadow layers.
+static bool UsingXCompositing() {
+ if (!gfxEnv::LayersEnableXlibSurfaces()) {
+ return false;
+ }
+ return (gfxSurfaceType::Xlib ==
+ gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType());
+}
+
+// LookReturn a pointer to |aFormat| that lives in the Xrender library.
+// All code using render formats assumes it doesn't need to copy.
+static XRenderPictFormat* GetXRenderPictFormatFromId(Display* aDisplay,
+ PictFormat aFormatId) {
+ XRenderPictFormat tmplate;
+ tmplate.id = aFormatId;
+ return XRenderFindFormat(aDisplay, PictFormatID, &tmplate, 0);
+}
+
+SurfaceDescriptorX11::SurfaceDescriptorX11(gfxXlibSurface* aSurf,
+ bool aForwardGLX)
+ : mId(aSurf->XDrawable()), mSize(aSurf->GetSize()), mGLXPixmap(X11None) {
+ const XRenderPictFormat* pictFormat = aSurf->XRenderFormat();
+ if (pictFormat) {
+ mFormat = pictFormat->id;
+ } else {
+ mFormat = cairo_xlib_surface_get_visual(aSurf->CairoSurface())->visualid;
+ }
+
+ if (aForwardGLX) {
+ mGLXPixmap = aSurf->GetGLXPixmap();
+ }
+}
+
+SurfaceDescriptorX11::SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID,
+ const gfx::IntSize& aSize)
+ : mId(aDrawable), mFormat(aFormatID), mSize(aSize), mGLXPixmap(X11None) {}
+
+already_AddRefed<gfxXlibSurface> SurfaceDescriptorX11::OpenForeign() const {
+ Display* display = DefaultXDisplay();
+ if (!display) {
+ return nullptr;
+ }
+ Screen* screen = DefaultScreenOfDisplay(display);
+
+ RefPtr<gfxXlibSurface> surf;
+ XRenderPictFormat* pictFormat = GetXRenderPictFormatFromId(display, mFormat);
+ if (pictFormat) {
+ surf = new gfxXlibSurface(screen, mId, pictFormat, mSize);
+ } else {
+ Visual* visual;
+ int depth;
+ FindVisualAndDepth(display, mFormat, &visual, &depth);
+ if (!visual) return nullptr;
+
+ surf = new gfxXlibSurface(display, mId, visual, mSize);
+ }
+
+ if (mGLXPixmap) surf->BindGLXPixmap(mGLXPixmap);
+
+ return surf->CairoStatus() ? nullptr : surf.forget();
+}
+
+/*static*/
+void ShadowLayerForwarder::PlatformSyncBeforeUpdate() {
+ if (UsingXCompositing()) {
+ // If we're using X surfaces, then we need to finish all pending
+ // operations on the back buffers before handing them to the
+ // parent, otherwise the surface might be used by the parent's
+ // Display in between two operations queued by our Display.
+ FinishX(DefaultXDisplay());
+ }
+}
+
+/*static*/
+void LayerManagerComposite::PlatformSyncBeforeReplyUpdate() {
+ if (UsingXCompositing()) {
+ // If we're using X surfaces, we need to finish all pending
+ // operations on the *front buffers* before handing them back to
+ // the child, even though they will be read operations.
+ // Otherwise, the child might start scribbling on new back buffers
+ // that are still participating in requests as old front buffers.
+ FinishX(DefaultXDisplay());
+ }
+}
+
+/*static*/
+bool LayerManagerComposite::SupportsDirectTexturing() { return false; }
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+void ParamTraits<mozilla::layers::SurfaceDescriptorX11>::Write(
+ Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mId);
+ WriteParam(aMsg, aParam.mSize);
+ WriteParam(aMsg, aParam.mFormat);
+ WriteParam(aMsg, aParam.mGLXPixmap);
+}
+
+bool ParamTraits<mozilla::layers::SurfaceDescriptorX11>::Read(
+ const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
+ return (ReadParam(aMsg, aIter, &aResult->mId) &&
+ ReadParam(aMsg, aIter, &aResult->mSize) &&
+ ReadParam(aMsg, aIter, &aResult->mFormat) &&
+ ReadParam(aMsg, aIter, &aResult->mGLXPixmap));
+}
+
+} // namespace IPC
diff --git a/gfx/layers/ipc/ShadowLayerUtilsX11.h b/gfx/layers/ipc/ShadowLayerUtilsX11.h
new file mode 100644
index 0000000000..9cbee37cf1
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsX11.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayerUtilsX11_h
+#define mozilla_layers_ShadowLayerUtilsX11_h
+
+#include "gfxipc/SurfaceDescriptor.h"
+#include "ipc/IPCMessageUtils.h"
+
+namespace IPC {
+class Message;
+
+template <>
+struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> {
+ typedef mozilla::layers::SurfaceDescriptorX11 paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam);
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif // mozilla_layers_ShadowLayerUtilsX11_h
diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp
new file mode 100644
index 0000000000..571a05d491
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -0,0 +1,1074 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ShadowLayers.h"
+
+#include <set> // for _Rb_tree_const_iterator, etc
+#include <vector> // for vector
+
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "GeckoProfiler.h" // for AUTO_PROFILER_LABEL
+#include "IPDLActor.h"
+#include "ISurfaceAllocator.h" // for IsSurfaceDescriptorValid
+#include "Layers.h" // for Layer
+#include "RenderTrace.h" // for RenderTraceScope
+#include "gfx2DGlue.h" // for Moz2D transition helpers
+#include "gfxPlatform.h" // for gfxImageFormat, gfxPlatform
+#include "ipc/IPCMessageUtils.h" // for gfxContentType, null_t
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient, etc
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ContentClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/LayersMessages.h" // for Edit, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/PTextureChild.h"
+#include "mozilla/layers/SyncObject.h"
+#ifdef XP_DARWIN
+# include "mozilla/layers/TextureSync.h"
+#endif
+#include "ShadowLayerUtils.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsIXULRuntime.h" // for BrowserTabsRemoteAutostart
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+using namespace mozilla::ipc;
+
+class ClientTiledLayerBuffer;
+
+typedef nsTArray<SurfaceDescriptor> BufferArray;
+typedef nsTArray<Edit> EditVector;
+typedef nsTHashtable<nsPtrHashKey<ShadowableLayer>> ShadowableLayerSet;
+typedef nsTArray<OpDestroy> OpDestroyVector;
+
+class Transaction {
+ public:
+ Transaction()
+ : mTargetRotation(ROTATION_0),
+ mTargetOrientation(hal::eScreenOrientation_None),
+ mOpen(false),
+ mRotationChanged(false) {}
+
+ void Begin(const gfx::IntRect& aTargetBounds, ScreenRotation aRotation,
+ hal::ScreenOrientation aOrientation) {
+ mOpen = true;
+ mTargetBounds = aTargetBounds;
+ if (aRotation != mTargetRotation) {
+ // the first time this is called, mRotationChanged will be false if
+ // aRotation is 0, but we should be OK because for the first transaction
+ // we should only compose if it is non-empty. See the caller(s) of
+ // RotationChanged.
+ mRotationChanged = true;
+ }
+ mTargetRotation = aRotation;
+ mTargetOrientation = aOrientation;
+ }
+ void AddEdit(const Edit& aEdit) {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mCset.AppendElement(aEdit);
+ }
+ void AddEdit(const CompositableOperation& aEdit) { AddEdit(Edit(aEdit)); }
+
+ void AddNoSwapPaint(const CompositableOperation& aPaint) {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mPaints.AppendElement(Edit(aPaint));
+ }
+ void AddMutant(ShadowableLayer* aLayer) {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mMutants.PutEntry(aLayer);
+ }
+ void AddSimpleMutant(ShadowableLayer* aLayer) {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mSimpleMutants.PutEntry(aLayer);
+ }
+ void End() {
+ mCset.Clear();
+ mPaints.Clear();
+ mMutants.Clear();
+ mSimpleMutants.Clear();
+ mDestroyedActors.Clear();
+ mOpen = false;
+ mRotationChanged = false;
+ }
+
+ bool Empty() const {
+ return mCset.IsEmpty() && mPaints.IsEmpty() && mMutants.IsEmpty() &&
+ mSimpleMutants.IsEmpty() && mDestroyedActors.IsEmpty();
+ }
+ bool RotationChanged() const { return mRotationChanged; }
+ bool Finished() const { return !mOpen && Empty(); }
+
+ bool Opened() const { return mOpen; }
+
+ EditVector mCset;
+ nsTArray<CompositableOperation> mPaints;
+ OpDestroyVector mDestroyedActors;
+ ShadowableLayerSet mMutants;
+ ShadowableLayerSet mSimpleMutants;
+ gfx::IntRect mTargetBounds;
+ ScreenRotation mTargetRotation;
+ hal::ScreenOrientation mTargetOrientation;
+
+ private:
+ bool mOpen;
+ bool mRotationChanged;
+
+ // disabled
+ Transaction(const Transaction&);
+ Transaction& operator=(const Transaction&);
+};
+struct AutoTxnEnd final {
+ explicit AutoTxnEnd(Transaction* aTxn) : mTxn(aTxn) {}
+ ~AutoTxnEnd() { mTxn->End(); }
+ Transaction* mTxn;
+};
+
+void KnowsCompositor::IdentifyTextureHost(
+ const TextureFactoryIdentifier& aIdentifier) {
+ auto lock = mData.Lock();
+ lock.ref().mTextureFactoryIdentifier = aIdentifier;
+
+ lock.ref().mSyncObject =
+ SyncObjectClient::CreateSyncObjectClientForContentDevice(
+ aIdentifier.mSyncHandle);
+}
+
+KnowsCompositor::KnowsCompositor()
+ : mData("KnowsCompositorMutex"), mSerial(++sSerialCounter) {}
+
+KnowsCompositor::~KnowsCompositor() = default;
+
+KnowsCompositorMediaProxy::KnowsCompositorMediaProxy(
+ const TextureFactoryIdentifier& aIdentifier) {
+ auto lock = mData.Lock();
+ lock.ref().mTextureFactoryIdentifier = aIdentifier;
+ // overwrite mSerial's value set by the parent class because we use the same
+ // serial as the KnowsCompositor we are proxying.
+ mThreadSafeAllocator = ImageBridgeChild::GetSingleton();
+ lock.ref().mSyncObject = mThreadSafeAllocator->GetSyncObject();
+}
+
+KnowsCompositorMediaProxy::~KnowsCompositorMediaProxy() = default;
+
+TextureForwarder* KnowsCompositorMediaProxy::GetTextureForwarder() {
+ return mThreadSafeAllocator->GetTextureForwarder();
+}
+
+LayersIPCActor* KnowsCompositorMediaProxy::GetLayersIPCActor() {
+ return mThreadSafeAllocator->GetLayersIPCActor();
+}
+
+ActiveResourceTracker* KnowsCompositorMediaProxy::GetActiveResourceTracker() {
+ return mThreadSafeAllocator->GetActiveResourceTracker();
+}
+
+void KnowsCompositorMediaProxy::SyncWithCompositor() {
+ mThreadSafeAllocator->SyncWithCompositor();
+}
+
+RefPtr<KnowsCompositor> ShadowLayerForwarder::GetForMedia() {
+ return MakeAndAddRef<KnowsCompositorMediaProxy>(
+ GetTextureFactoryIdentifier());
+}
+
+ShadowLayerForwarder::ShadowLayerForwarder(
+ ClientLayerManager* aClientLayerManager)
+ : mClientLayerManager(aClientLayerManager),
+ mThread(NS_GetCurrentThread()),
+ mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC),
+ mIsFirstPaint(false),
+ mNextLayerHandle(1) {
+ mTxn = new Transaction();
+ mEventTarget = GetMainThreadSerialEventTarget();
+
+ MOZ_ASSERT(mEventTarget || !XRE_IsContentProcess());
+ mActiveResourceTracker = MakeUnique<ActiveResourceTracker>(
+ 1000, "CompositableForwarder", mEventTarget);
+}
+
+template <typename T>
+struct ReleaseOnMainThreadTask : public Runnable {
+ UniquePtr<T> mObj;
+
+ explicit ReleaseOnMainThreadTask(UniquePtr<T>& aObj)
+ : Runnable("layers::ReleaseOnMainThreadTask"), mObj(std::move(aObj)) {}
+
+ NS_IMETHOD Run() override {
+ mObj = nullptr;
+ return NS_OK;
+ }
+};
+
+ShadowLayerForwarder::~ShadowLayerForwarder() {
+ MOZ_ASSERT(mTxn->Finished(), "unfinished transaction?");
+ delete mTxn;
+ if (mShadowManager) {
+ mShadowManager->SetForwarder(nullptr);
+ if (NS_IsMainThread()) {
+ mShadowManager->Destroy();
+ } else {
+ if (mEventTarget) {
+ mEventTarget->Dispatch(
+ NewRunnableMethod("LayerTransactionChild::Destroy", mShadowManager,
+ &LayerTransactionChild::Destroy),
+ nsIEventTarget::DISPATCH_NORMAL);
+ } else {
+ NS_DispatchToMainThread(
+ NewRunnableMethod("layers::LayerTransactionChild::Destroy",
+ mShadowManager, &LayerTransactionChild::Destroy));
+ }
+ }
+ }
+
+ if (!NS_IsMainThread()) {
+ RefPtr<ReleaseOnMainThreadTask<ActiveResourceTracker>> event =
+ new ReleaseOnMainThreadTask<ActiveResourceTracker>(
+ mActiveResourceTracker);
+ if (mEventTarget) {
+ mEventTarget->Dispatch(event.forget(), nsIEventTarget::DISPATCH_NORMAL);
+ } else {
+ NS_DispatchToMainThread(event);
+ }
+ }
+}
+
+void ShadowLayerForwarder::BeginTransaction(
+ const gfx::IntRect& aTargetBounds, ScreenRotation aRotation,
+ hal::ScreenOrientation aOrientation) {
+ MOZ_ASSERT(IPCOpen(), "no manager to forward to");
+ MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
+ UpdateFwdTransactionId();
+ mTxn->Begin(aTargetBounds, aRotation, aOrientation);
+}
+
+static const LayerHandle& Shadow(ShadowableLayer* aLayer) {
+ return aLayer->GetShadow();
+}
+
+template <typename OpCreateT>
+static void CreatedLayer(Transaction* aTxn, ShadowableLayer* aLayer) {
+ aTxn->AddEdit(OpCreateT(Shadow(aLayer)));
+}
+
+void ShadowLayerForwarder::CreatedPaintedLayer(ShadowableLayer* aThebes) {
+ CreatedLayer<OpCreatePaintedLayer>(mTxn, aThebes);
+}
+void ShadowLayerForwarder::CreatedContainerLayer(ShadowableLayer* aContainer) {
+ CreatedLayer<OpCreateContainerLayer>(mTxn, aContainer);
+}
+void ShadowLayerForwarder::CreatedImageLayer(ShadowableLayer* aImage) {
+ CreatedLayer<OpCreateImageLayer>(mTxn, aImage);
+}
+void ShadowLayerForwarder::CreatedColorLayer(ShadowableLayer* aColor) {
+ CreatedLayer<OpCreateColorLayer>(mTxn, aColor);
+}
+void ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas) {
+ CreatedLayer<OpCreateCanvasLayer>(mTxn, aCanvas);
+}
+void ShadowLayerForwarder::CreatedRefLayer(ShadowableLayer* aRef) {
+ CreatedLayer<OpCreateRefLayer>(mTxn, aRef);
+}
+
+void ShadowLayerForwarder::Mutated(ShadowableLayer* aMutant) {
+ mTxn->AddMutant(aMutant);
+}
+
+void ShadowLayerForwarder::MutatedSimple(ShadowableLayer* aMutant) {
+ mTxn->AddSimpleMutant(aMutant);
+}
+
+void ShadowLayerForwarder::SetRoot(ShadowableLayer* aRoot) {
+ mTxn->AddEdit(OpSetRoot(Shadow(aRoot)));
+}
+void ShadowLayerForwarder::InsertAfter(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter) {
+ if (!aChild->HasShadow()) {
+ return;
+ }
+
+ while (aAfter && !aAfter->HasShadow()) {
+ aAfter = aAfter->AsLayer()->GetPrevSibling()
+ ? aAfter->AsLayer()->GetPrevSibling()->AsShadowableLayer()
+ : nullptr;
+ }
+
+ if (aAfter) {
+ mTxn->AddEdit(
+ OpInsertAfter(Shadow(aContainer), Shadow(aChild), Shadow(aAfter)));
+ } else {
+ mTxn->AddEdit(OpPrependChild(Shadow(aContainer), Shadow(aChild)));
+ }
+}
+void ShadowLayerForwarder::RemoveChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpRemoveChild container=%p child=%p\n",
+ aContainer->AsLayer(), aChild->AsLayer()));
+
+ if (!aChild->HasShadow()) {
+ return;
+ }
+
+ mTxn->AddEdit(OpRemoveChild(Shadow(aContainer), Shadow(aChild)));
+}
+void ShadowLayerForwarder::RepositionChild(ShadowableLayer* aContainer,
+ ShadowableLayer* aChild,
+ ShadowableLayer* aAfter) {
+ if (!aChild->HasShadow()) {
+ return;
+ }
+
+ while (aAfter && !aAfter->HasShadow()) {
+ aAfter = aAfter->AsLayer()->GetPrevSibling()
+ ? aAfter->AsLayer()->GetPrevSibling()->AsShadowableLayer()
+ : nullptr;
+ }
+
+ if (aAfter) {
+ MOZ_LAYERS_LOG(
+ ("[LayersForwarder] OpRepositionChild container=%p child=%p after=%p",
+ aContainer->AsLayer(), aChild->AsLayer(), aAfter->AsLayer()));
+ mTxn->AddEdit(
+ OpRepositionChild(Shadow(aContainer), Shadow(aChild), Shadow(aAfter)));
+ } else {
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpRaiseToTopChild container=%p child=%p",
+ aContainer->AsLayer(), aChild->AsLayer()));
+ mTxn->AddEdit(OpRaiseToTopChild(Shadow(aContainer), Shadow(aChild)));
+ }
+}
+
+#ifdef DEBUG
+void ShadowLayerForwarder::CheckSurfaceDescriptor(
+ const SurfaceDescriptor* aDescriptor) const {
+ if (!aDescriptor) {
+ return;
+ }
+
+ if (aDescriptor->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer &&
+ aDescriptor->get_SurfaceDescriptorBuffer().data().type() ==
+ MemoryOrShmem::TShmem) {
+ const Shmem& shmem =
+ aDescriptor->get_SurfaceDescriptorBuffer().data().get_Shmem();
+ shmem.AssertInvariants();
+ MOZ_ASSERT(mShadowManager &&
+ mShadowManager->IsTrackingSharedMemory(shmem.mSegment));
+ }
+}
+#endif
+
+void ShadowLayerForwarder::UseTiledLayerBuffer(
+ CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTileLayerDescriptor) {
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ mTxn->AddNoSwapPaint(
+ CompositableOperation(aCompositable->GetIPCHandle(),
+ OpUseTiledLayerBuffer(aTileLayerDescriptor)));
+}
+
+void ShadowLayerForwarder::UpdateTextureRegion(
+ CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) {
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ mTxn->AddNoSwapPaint(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpPaintTextureRegion(aThebesBufferData, aUpdatedRegion)));
+}
+
+void ShadowLayerForwarder::UseTextures(
+ CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) {
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ AutoTArray<TimedTexture, 4> textures;
+
+ for (auto& t : aTextures) {
+ MOZ_ASSERT(t.mTextureClient);
+ MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
+ MOZ_RELEASE_ASSERT(t.mTextureClient->GetIPDLActor()->GetIPCChannel() ==
+ mShadowManager->GetIPCChannel());
+ bool readLocked = t.mTextureClient->OnForwardedToHost();
+ textures.AppendElement(
+ TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), t.mTimeStamp,
+ t.mPictureRect, t.mFrameID, t.mProducerID, readLocked));
+ mClientLayerManager->GetCompositorBridgeChild()
+ ->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
+
+ auto fenceFd = t.mTextureClient->GetInternalData()->GetAcquireFence();
+ if (fenceFd.IsValid()) {
+ mTxn->AddEdit(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpDeliverAcquireFence(nullptr, t.mTextureClient->GetIPDLActor(),
+ fenceFd)));
+ }
+ }
+ mTxn->AddEdit(CompositableOperation(aCompositable->GetIPCHandle(),
+ OpUseTexture(textures)));
+}
+
+void ShadowLayerForwarder::UseComponentAlphaTextures(
+ CompositableClient* aCompositable, TextureClient* aTextureOnBlack,
+ TextureClient* aTextureOnWhite) {
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ MOZ_ASSERT(aTextureOnWhite);
+ MOZ_ASSERT(aTextureOnBlack);
+ MOZ_ASSERT(aCompositable->GetIPCHandle());
+ MOZ_ASSERT(aTextureOnBlack->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
+ MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
+ MOZ_RELEASE_ASSERT(aTextureOnWhite->GetIPDLActor()->GetIPCChannel() ==
+ mShadowManager->GetIPCChannel());
+ MOZ_RELEASE_ASSERT(aTextureOnBlack->GetIPDLActor()->GetIPCChannel() ==
+ mShadowManager->GetIPCChannel());
+
+ bool readLockedB = aTextureOnBlack->OnForwardedToHost();
+ bool readLockedW = aTextureOnWhite->OnForwardedToHost();
+
+ mClientLayerManager->GetCompositorBridgeChild()
+ ->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack);
+ mClientLayerManager->GetCompositorBridgeChild()
+ ->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite);
+
+ auto fenceFdB = aTextureOnBlack->GetInternalData()->GetAcquireFence();
+ if (fenceFdB.IsValid()) {
+ mTxn->AddEdit(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpDeliverAcquireFence(nullptr, aTextureOnBlack->GetIPDLActor(),
+ fenceFdB)));
+ }
+
+ auto fenceFdW = aTextureOnWhite->GetInternalData()->GetAcquireFence();
+ if (fenceFdW.IsValid()) {
+ mTxn->AddEdit(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpDeliverAcquireFence(nullptr, aTextureOnWhite->GetIPDLActor(),
+ fenceFdW)));
+ }
+
+ mTxn->AddEdit(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpUseComponentAlphaTextures(nullptr, aTextureOnBlack->GetIPDLActor(),
+ nullptr, aTextureOnWhite->GetIPDLActor(),
+ readLockedB, readLockedW)));
+}
+
+static bool AddOpDestroy(Transaction* aTxn, const OpDestroy& op) {
+ if (!aTxn->Opened()) {
+ return false;
+ }
+
+ aTxn->mDestroyedActors.AppendElement(op);
+ return true;
+}
+
+bool ShadowLayerForwarder::DestroyInTransaction(PTextureChild* aTexture) {
+ return AddOpDestroy(mTxn, OpDestroy(aTexture));
+}
+
+bool ShadowLayerForwarder::DestroyInTransaction(
+ const CompositableHandle& aHandle) {
+ return AddOpDestroy(mTxn, OpDestroy(aHandle));
+}
+
+void ShadowLayerForwarder::RemoveTextureFromCompositable(
+ CompositableClient* aCompositable, TextureClient* aTexture) {
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->GetIPDLActor());
+ MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() ==
+ mShadowManager->GetIPCChannel());
+ if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) {
+ // We don't have an actor anymore, don't try to use it!
+ return;
+ }
+
+ mTxn->AddEdit(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
+}
+
+bool ShadowLayerForwarder::InWorkerThread() {
+ return GetTextureForwarder()->GetThread()->IsOnCurrentThread();
+}
+
+void ShadowLayerForwarder::StorePluginWidgetConfigurations(
+ const nsTArray<nsIWidget::Configuration>& aConfigurations) {
+ // Cache new plugin widget configs here until we call update, at which
+ // point this data will get shipped over to chrome.
+ mPluginWindowData.Clear();
+ for (uint32_t idx = 0; idx < aConfigurations.Length(); idx++) {
+ const nsIWidget::Configuration& configuration = aConfigurations[idx];
+ mPluginWindowData.AppendElement(
+ PluginWindowData(configuration.mWindowID, configuration.mClipRegion,
+ configuration.mBounds, configuration.mVisible));
+ }
+}
+
+void ShadowLayerForwarder::SendPaintTime(TransactionId aId,
+ TimeDuration aPaintTime) {
+ if (!IPCOpen() || !mShadowManager->SendPaintTime(aId, aPaintTime)) {
+ NS_WARNING("Could not send paint times over IPC");
+ }
+}
+
+bool ShadowLayerForwarder::EndTransaction(
+ const nsIntRegion& aRegionToClear, TransactionId aId,
+ bool aScheduleComposite, uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction, const mozilla::VsyncId& aVsyncId,
+ const mozilla::TimeStamp& aVsyncStart,
+ const mozilla::TimeStamp& aRefreshStart,
+ const mozilla::TimeStamp& aTransactionStart, bool aContainsSVG,
+ const nsCString& aURL, bool* aSent,
+ const nsTArray<CompositionPayload>& aPayload) {
+ *aSent = false;
+
+ TransactionInfo info;
+
+ MOZ_ASSERT(IPCOpen(), "no manager to forward to");
+ if (!IPCOpen()) {
+ return false;
+ }
+
+ Maybe<TimeStamp> startTime;
+ if (StaticPrefs::layers_acceleration_draw_fps()) {
+ startTime = Some(TimeStamp::Now());
+ }
+
+ GetCompositorBridgeChild()->WillEndTransaction();
+
+ MOZ_ASSERT(aId.IsValid());
+
+ AUTO_PROFILER_LABEL("ShadowLayerForwarder::EndTransaction", GRAPHICS);
+
+ RenderTraceScope rendertrace("Foward Transaction", "000091");
+ MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
+
+ DiagnosticTypes diagnostics =
+ gfxPlatform::GetPlatform()->GetLayerDiagnosticTypes();
+ if (mDiagnosticTypes != diagnostics) {
+ mDiagnosticTypes = diagnostics;
+ mTxn->AddEdit(OpSetDiagnosticTypes(diagnostics));
+ }
+
+ AutoTxnEnd _(mTxn);
+
+ if (mTxn->Empty() && !mTxn->RotationChanged()) {
+ MOZ_LAYERS_LOG(
+ ("[LayersForwarder] 0-length cset (?) and no rotation event, skipping "
+ "Update()"));
+ return true;
+ }
+
+ if (!mTxn->mPaints.IsEmpty()) {
+ // With some platforms, telling the drawing backend that there will be no
+ // more drawing for this frame helps with preventing command queues from
+ // spanning across multiple frames.
+ gfxPlatform::GetPlatform()->FlushContentDrawing();
+ }
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] destroying buffers..."));
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] building transaction..."));
+
+ nsTArray<OpSetSimpleLayerAttributes> setSimpleAttrs;
+ for (ShadowableLayerSet::Iterator it(&mTxn->mSimpleMutants); !it.Done();
+ it.Next()) {
+ ShadowableLayer* shadow = it.Get()->GetKey();
+ if (!shadow->HasShadow()) {
+ continue;
+ }
+
+ Layer* mutant = shadow->AsLayer();
+ setSimpleAttrs.AppendElement(OpSetSimpleLayerAttributes(
+ Shadow(shadow), mutant->GetSimpleAttributes()));
+ }
+
+ nsTArray<OpSetLayerAttributes> setAttrs;
+
+ // We purposely add attribute-change ops to the final changeset
+ // before we add paint ops. This allows layers to record the
+ // attribute changes before new pixels arrive, which can be useful
+ // for setting up back/front buffers.
+ RenderTraceScope rendertrace2("Foward Transaction", "000092");
+ for (ShadowableLayerSet::Iterator it(&mTxn->mMutants); !it.Done();
+ it.Next()) {
+ ShadowableLayer* shadow = it.Get()->GetKey();
+
+ if (!shadow->HasShadow()) {
+ continue;
+ }
+ Layer* mutant = shadow->AsLayer();
+ MOZ_ASSERT(!!mutant, "unshadowable layer?");
+
+ OpSetLayerAttributes op;
+ op.layer() = Shadow(shadow);
+
+ LayerAttributes& attrs = op.attrs();
+ CommonLayerAttributes& common = attrs.common();
+ common.visibleRegion() = mutant->GetVisibleRegion();
+ common.eventRegions() = mutant->GetEventRegions();
+ common.useClipRect() = !!mutant->GetClipRect();
+ common.clipRect() =
+ (common.useClipRect() ? *mutant->GetClipRect() : ParentLayerIntRect());
+ if (Layer* maskLayer = mutant->GetMaskLayer()) {
+ common.maskLayer() = Shadow(maskLayer->AsShadowableLayer());
+ } else {
+ common.maskLayer() = LayerHandle();
+ }
+ common.compositorAnimations().id() = mutant->GetCompositorAnimationsId();
+ common.compositorAnimations().animations() =
+ mutant->GetAnimations().Clone();
+ common.invalidRegion() = mutant->GetInvalidRegion().GetRegion();
+ common.scrollMetadata() = mutant->GetAllScrollMetadata().Clone();
+ for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) {
+ auto layer =
+ Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer());
+ common.ancestorMaskLayers().AppendElement(layer);
+ }
+ nsCString log;
+ mutant->GetDisplayListLog(log);
+ common.displayListLog() = log;
+
+ attrs.specific() = null_t();
+ mutant->FillSpecificAttributes(attrs.specific());
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant));
+
+ setAttrs.AppendElement(op);
+ }
+
+ if (mTxn->mCset.IsEmpty() && mTxn->mPaints.IsEmpty() && setAttrs.IsEmpty() &&
+ !mTxn->RotationChanged()) {
+ return true;
+ }
+
+ info.cset() = std::move(mTxn->mCset);
+ info.setSimpleAttrs() = std::move(setSimpleAttrs);
+ info.setAttrs() = std::move(setAttrs);
+ info.paints() = std::move(mTxn->mPaints);
+ info.toDestroy() = mTxn->mDestroyedActors.Clone();
+ info.fwdTransactionId() = GetFwdTransactionId();
+ info.id() = aId;
+ info.plugins() = mPluginWindowData.Clone();
+ info.isFirstPaint() = mIsFirstPaint;
+ info.focusTarget() = mFocusTarget;
+ info.scheduleComposite() = aScheduleComposite;
+ info.paintSequenceNumber() = aPaintSequenceNumber;
+ info.isRepeatTransaction() = aIsRepeatTransaction;
+ info.vsyncId() = aVsyncId;
+ info.vsyncStart() = aVsyncStart;
+ info.refreshStart() = aRefreshStart;
+ info.transactionStart() = aTransactionStart;
+ info.url() = aURL;
+ info.containsSVG() = aContainsSVG;
+#if defined(ENABLE_FRAME_LATENCY_LOG)
+ info.fwdTime() = TimeStamp::Now();
+#endif
+ info.payload() = aPayload.Clone();
+
+ TargetConfig targetConfig(mTxn->mTargetBounds, mTxn->mTargetRotation,
+ mTxn->mTargetOrientation, aRegionToClear);
+ info.targetConfig() = targetConfig;
+
+ if (!GetTextureForwarder()->IsSameProcess()) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send..."));
+ PlatformSyncBeforeUpdate();
+ }
+
+ if (startTime) {
+ mPaintTiming.serializeMs() =
+ (TimeStamp::Now() - startTime.value()).ToMilliseconds();
+ startTime = Some(TimeStamp::Now());
+ }
+
+ // We delay at the last possible minute, to give the paint thread a chance to
+ // finish. If it does we don't have to delay messages at all.
+ GetCompositorBridgeChild()->PostponeMessagesIfAsyncPainting();
+
+ MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction..."));
+ RenderTraceScope rendertrace3("Forward Transaction", "000093");
+ if (!mShadowManager->SendUpdate(info)) {
+ MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
+ return false;
+ }
+
+ if (startTime) {
+ mPaintTiming.sendMs() =
+ (TimeStamp::Now() - startTime.value()).ToMilliseconds();
+ mShadowManager->SendRecordPaintTimes(mPaintTiming);
+ }
+
+ *aSent = true;
+ mIsFirstPaint = false;
+ mFocusTarget = FocusTarget();
+ MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
+ return true;
+}
+
+RefPtr<CompositableClient> ShadowLayerForwarder::FindCompositable(
+ const CompositableHandle& aHandle) {
+ CompositableClient* client = nullptr;
+ if (!mCompositables.Get(aHandle.Value(), &client)) {
+ return nullptr;
+ }
+ return client;
+}
+
+void ShadowLayerForwarder::SetLayersObserverEpoch(LayersObserverEpoch aEpoch) {
+ if (!IPCOpen()) {
+ return;
+ }
+ Unused << mShadowManager->SendSetLayersObserverEpoch(aEpoch);
+}
+
+void ShadowLayerForwarder::UpdateTextureLocks() {
+#ifdef XP_DARWIN
+ if (!IPCOpen()) {
+ return;
+ }
+
+ auto compositorBridge = GetCompositorBridgeChild();
+ if (compositorBridge) {
+ auto pid = compositorBridge->OtherPid();
+ TextureSync::UpdateTextureLocks(pid);
+ }
+#endif
+}
+
+void ShadowLayerForwarder::SyncTextures(const nsTArray<uint64_t>& aSerials) {
+#ifdef XP_DARWIN
+ if (!IPCOpen()) {
+ return;
+ }
+
+ auto compositorBridge = GetCompositorBridgeChild();
+ if (compositorBridge) {
+ auto pid = compositorBridge->OtherPid();
+ TextureSync::WaitForTextures(pid, aSerials);
+ }
+#endif
+}
+
+void ShadowLayerForwarder::ReleaseLayer(const LayerHandle& aHandle) {
+ if (!IPCOpen()) {
+ return;
+ }
+ Unused << mShadowManager->SendReleaseLayer(aHandle);
+}
+
+bool ShadowLayerForwarder::IPCOpen() const {
+ return HasShadowManager() && mShadowManager->IPCOpen();
+}
+
+/**
+ * We bail out when we have no shadow manager. That can happen when the
+ * layer manager is created by the preallocated process.
+ * See bug 914843 for details.
+ */
+LayerHandle ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer) {
+ return LayerHandle(mNextLayerHandle++);
+}
+
+#if !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
+
+/*static*/
+void ShadowLayerForwarder::PlatformSyncBeforeUpdate() {}
+
+#endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
+
+void ShadowLayerForwarder::Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) {
+#ifdef GFX_COMPOSITOR_LOGGING
+ printf("ShadowLayerForwarder::Connect(Compositable)\n");
+#endif
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(mShadowManager);
+ if (!IPCOpen()) {
+ return;
+ }
+
+ static uint64_t sNextID = 1;
+ uint64_t id = sNextID++;
+
+ mCompositables.Put(id, aCompositable);
+
+ CompositableHandle handle(id);
+ aCompositable->InitIPDL(handle);
+ mShadowManager->SendNewCompositable(handle, aCompositable->GetTextureInfo());
+}
+
+void ShadowLayerForwarder::Attach(CompositableClient* aCompositable,
+ ShadowableLayer* aLayer) {
+ MOZ_ASSERT(aLayer);
+ MOZ_ASSERT(aCompositable);
+ mTxn->AddEdit(
+ OpAttachCompositable(Shadow(aLayer), aCompositable->GetIPCHandle()));
+}
+
+void ShadowLayerForwarder::AttachAsyncCompositable(
+ const CompositableHandle& aHandle, ShadowableLayer* aLayer) {
+ MOZ_ASSERT(aLayer);
+ MOZ_ASSERT(aHandle);
+ mTxn->AddEdit(OpAttachAsyncCompositable(Shadow(aLayer), aHandle));
+}
+
+void ShadowLayerForwarder::SetShadowManager(
+ PLayerTransactionChild* aShadowManager) {
+ mShadowManager = static_cast<LayerTransactionChild*>(aShadowManager);
+ mShadowManager->SetForwarder(this);
+}
+
+void ShadowLayerForwarder::StopReceiveAsyncParentMessge() {
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SetForwarder(nullptr);
+}
+
+void ShadowLayerForwarder::ClearCachedResources() {
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SendClearCachedResources();
+}
+
+void ShadowLayerForwarder::ScheduleComposite() {
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SendScheduleComposite();
+}
+
+bool IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface) {
+ return aSurface.type() != SurfaceDescriptor::T__None &&
+ aSurface.type() != SurfaceDescriptor::Tnull_t;
+}
+
+uint8_t* GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor) {
+ MOZ_ASSERT(IsSurfaceDescriptorValid(aDescriptor));
+ MOZ_RELEASE_ASSERT(
+ aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer,
+ "GFX: surface descriptor is not the right type.");
+
+ auto memOrShmem = aDescriptor.get_SurfaceDescriptorBuffer().data();
+ if (memOrShmem.type() == MemoryOrShmem::TShmem) {
+ return memOrShmem.get_Shmem().get<uint8_t>();
+ } else {
+ return reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
+ }
+}
+
+already_AddRefed<gfx::DataSourceSurface> GetSurfaceForDescriptor(
+ const SurfaceDescriptor& aDescriptor) {
+ if (aDescriptor.type() != SurfaceDescriptor::TSurfaceDescriptorBuffer) {
+ return nullptr;
+ }
+ uint8_t* data = GetAddressFromDescriptor(aDescriptor);
+ auto rgb =
+ aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor();
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ return gfx::Factory::CreateWrappingDataSourceSurface(data, stride, rgb.size(),
+ rgb.format());
+}
+
+already_AddRefed<gfx::DrawTarget> GetDrawTargetForDescriptor(
+ const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend) {
+ uint8_t* data = GetAddressFromDescriptor(aDescriptor);
+ auto rgb =
+ aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor();
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ return gfx::Factory::CreateDrawTargetForData(
+ gfx::BackendType::CAIRO, data, rgb.size(), stride, rgb.format());
+}
+
+void DestroySurfaceDescriptor(IShmemAllocator* aAllocator,
+ SurfaceDescriptor* aSurface) {
+ MOZ_ASSERT(aSurface);
+
+ SurfaceDescriptorBuffer& desc = aSurface->get_SurfaceDescriptorBuffer();
+ switch (desc.data().type()) {
+ case MemoryOrShmem::TShmem: {
+ aAllocator->DeallocShmem(desc.data().get_Shmem());
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ uint8_t* ptr = (uint8_t*)desc.data().get_uintptr_t();
+ GfxMemoryImageReporter::WillFree(ptr);
+ delete[] ptr;
+ break;
+ }
+ default:
+ MOZ_CRASH("surface type not implemented!");
+ }
+ *aSurface = SurfaceDescriptor();
+}
+
+bool ShadowLayerForwarder::AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer) {
+ if (!IPCOpen()) {
+ return false;
+ }
+ return AllocSurfaceDescriptorWithCaps(aSize, aContent, DEFAULT_BUFFER_CAPS,
+ aBuffer);
+}
+
+bool ShadowLayerForwarder::AllocSurfaceDescriptorWithCaps(
+ const gfx::IntSize& aSize, gfxContentType aContent, uint32_t aCaps,
+ SurfaceDescriptor* aBuffer) {
+ if (!IPCOpen()) {
+ return false;
+ }
+ gfx::SurfaceFormat format =
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aContent);
+ size_t size = ImageDataSerializer::ComputeRGBBufferSize(aSize, format);
+ if (!size) {
+ return false;
+ }
+
+ MemoryOrShmem bufferDesc;
+ if (GetTextureForwarder()->IsSameProcess()) {
+ uint8_t* data = new (std::nothrow) uint8_t[size];
+ if (!data) {
+ return false;
+ }
+ GfxMemoryImageReporter::DidAlloc(data);
+ memset(data, 0, size);
+ bufferDesc = reinterpret_cast<uintptr_t>(data);
+ } else {
+ mozilla::ipc::Shmem shmem;
+ if (!GetTextureForwarder()->AllocUnsafeShmem(size, OptimalShmemType(),
+ &shmem)) {
+ return false;
+ }
+
+ bufferDesc = std::move(shmem);
+ }
+
+ // Use an intermediate buffer by default. Skipping the intermediate buffer is
+ // only possible in certain configurations so let's keep it simple here for
+ // now.
+ const bool hasIntermediateBuffer = true;
+ *aBuffer = SurfaceDescriptorBuffer(
+ RGBDescriptor(aSize, format, hasIntermediateBuffer), bufferDesc);
+
+ return true;
+}
+
+/* static */
+bool ShadowLayerForwarder::IsShmem(SurfaceDescriptor* aSurface) {
+ return aSurface &&
+ (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer) &&
+ (aSurface->get_SurfaceDescriptorBuffer().data().type() ==
+ MemoryOrShmem::TShmem);
+}
+
+void ShadowLayerForwarder::DestroySurfaceDescriptor(
+ SurfaceDescriptor* aSurface) {
+ MOZ_ASSERT(aSurface);
+ MOZ_ASSERT(IPCOpen());
+ if (!IPCOpen() || !aSurface) {
+ return;
+ }
+
+ ::mozilla::layers::DestroySurfaceDescriptor(GetTextureForwarder(), aSurface);
+}
+
+void ShadowLayerForwarder::UpdateFwdTransactionId() {
+ auto compositorBridge = GetCompositorBridgeChild();
+ if (compositorBridge) {
+ compositorBridge->UpdateFwdTransactionId();
+ }
+}
+
+uint64_t ShadowLayerForwarder::GetFwdTransactionId() {
+ auto compositorBridge = GetCompositorBridgeChild();
+ MOZ_DIAGNOSTIC_ASSERT(compositorBridge);
+ return compositorBridge ? compositorBridge->GetFwdTransactionId() : 0;
+}
+
+CompositorBridgeChild* ShadowLayerForwarder::GetCompositorBridgeChild() {
+ if (mCompositorBridgeChild) {
+ return mCompositorBridgeChild;
+ }
+ if (!mShadowManager) {
+ return nullptr;
+ }
+ mCompositorBridgeChild =
+ static_cast<CompositorBridgeChild*>(mShadowManager->Manager());
+ return mCompositorBridgeChild;
+}
+
+void ShadowLayerForwarder::SyncWithCompositor() {
+ auto compositorBridge = GetCompositorBridgeChild();
+ if (compositorBridge && compositorBridge->IPCOpen()) {
+ compositorBridge->SendSyncWithCompositor();
+ }
+}
+
+void ShadowLayerForwarder::ReleaseCompositable(
+ const CompositableHandle& aHandle) {
+ AssertInForwarderThread();
+ if (!DestroyInTransaction(aHandle)) {
+ if (!IPCOpen()) {
+ return;
+ }
+ mShadowManager->SendReleaseCompositable(aHandle);
+ }
+ mCompositables.Remove(aHandle.Value());
+}
+
+void ShadowLayerForwarder::SynchronouslyShutdown() {
+ if (IPCOpen()) {
+ mShadowManager->SendShutdownSync();
+ mShadowManager->MarkDestroyed();
+ }
+}
+
+ShadowableLayer::~ShadowableLayer() {
+ if (mShadow) {
+ mForwarder->ReleaseLayer(GetShadow());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h
new file mode 100644
index 0000000000..055aba63de
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -0,0 +1,481 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ShadowLayers_h
+#define mozilla_layers_ShadowLayers_h 1
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/HalScreenConfiguration.h" // for ScreenOrientation
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/FocusTarget.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for OpenMode, etc
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArrayForwardDeclare.h" // for nsTArray
+#include "nsIWidget.h"
+#include <vector>
+
+namespace mozilla {
+namespace layers {
+
+class ClientLayerManager;
+class CompositorBridgeChild;
+class FixedSizeSmallShmemSectionAllocator;
+class ImageContainer;
+class Layer;
+class PLayerTransactionChild;
+class LayerTransactionChild;
+class ShadowableLayer;
+class SurfaceDescriptor;
+class TextureClient;
+class ThebesBuffer;
+class ThebesBufferData;
+class Transaction;
+
+/**
+ * We want to share layer trees across thread contexts and address
+ * spaces for several reasons; chief among them
+ *
+ * - a parent process can paint a child process's layer tree while
+ * the child process is blocked, say on content script. This is
+ * important on mobile devices where UI responsiveness is key.
+ *
+ * - a dedicated "compositor" process can asynchronously (wrt the
+ * browser process) composite and animate layer trees, allowing a
+ * form of pipeline parallelism between compositor/browser/content
+ *
+ * - a dedicated "compositor" process can take all responsibility for
+ * accessing the GPU, which is desirable on systems with
+ * buggy/leaky drivers because the compositor process can die while
+ * browser and content live on (and failover mechanisms can be
+ * installed to quickly bring up a replacement compositor)
+ *
+ * The Layers model has a crisply defined API, which makes it easy to
+ * safely "share" layer trees. The ShadowLayers API extends Layers to
+ * allow a remote, parent process to access a child process's layer
+ * tree.
+ *
+ * ShadowLayerForwarder publishes a child context's layer tree to a
+ * parent context. This comprises recording layer-tree modifications
+ * into atomic transactions and pushing them over IPC.
+ *
+ * LayerManagerComposite grafts layer subtrees published by child-context
+ * ShadowLayerForwarder(s) into a parent-context layer tree.
+ *
+ * (Advanced note: because our process tree may have a height >2, a
+ * non-leaf subprocess may both receive updates from child processes
+ * and publish them to parent processes. Put another way,
+ * LayerManagers may be both LayerManagerComposites and
+ * ShadowLayerForwarders.)
+ *
+ * There are only shadow types for layers that have different shadow
+ * vs. not-shadow behavior. ColorLayers and ContainerLayers behave
+ * the same way in both regimes (so far).
+ *
+ *
+ * The mecanism to shadow the layer tree on the compositor through IPC works as
+ * follows:
+ * The layer tree is managed on the content thread, and shadowed in the
+ * compositor thread. The shadow layer tree is only kept in sync with whatever
+ * happens in the content thread. To do this we use IPDL protocols. IPDL is a
+ * domain specific language that describes how two processes or thread should
+ * communicate. C++ code is generated from .ipdl files to implement the message
+ * passing, synchronization and serialization logic. To use the generated code
+ * we implement classes that inherit the generated IPDL actor. the ipdl actors
+ * of a protocol PX are PXChild or PXParent (the generated class), and we
+ * conventionally implement XChild and XParent. The Parent side of the protocol
+ * is the one that lives on the compositor thread. Think of IPDL actors as
+ * endpoints of communication. they are useful to send messages and also to
+ * dispatch the message to the right actor on the other side. One nice property
+ * of an IPDL actor is that when an actor, say PXChild is sent in a message, the
+ * PXParent comes out in the other side. we use this property a lot to dispatch
+ * messages to the right layers and compositable, each of which have their own
+ * ipdl actor on both side.
+ *
+ * Most of the synchronization logic happens in layer transactions and
+ * compositable transactions.
+ * A transaction is a set of changes to the layers and/or the compositables
+ * that are sent and applied together to the compositor thread to keep the
+ * LayerComposite in a coherent state.
+ * Layer transactions maintain the shape of the shadow layer tree, and
+ * synchronize the texture data held by compositables. Layer transactions
+ * are always between the content thread and the compositor thread.
+ * Compositable transactions are subset of a layer transaction with which only
+ * compositables and textures can be manipulated, and does not always originate
+ * from the content thread. (See CompositableForwarder.h and ImageBridgeChild.h)
+ */
+
+class ShadowLayerForwarder final : public LayersIPCActor,
+ public CompositableForwarder,
+ public LegacySurfaceDescriptorAllocator {
+ friend class ClientLayerManager;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ShadowLayerForwarder, override);
+
+ /**
+ * Setup the IPDL actor for aCompositable to be part of layers
+ * transactions.
+ */
+ void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) override;
+
+ /**
+ * Adds an edit in the layers transaction in order to attach
+ * the corresponding compositable and layer on the compositor side.
+ * Connect must have been called on aCompositable beforehand.
+ */
+ void Attach(CompositableClient* aCompositable, ShadowableLayer* aLayer);
+
+ /**
+ * Adds an edit in the transaction in order to attach a Compositable that
+ * is not managed by this ShadowLayerForwarder (for example, by ImageBridge
+ * in the case of async-video).
+ * Since the compositable is not managed by this forwarder, we can't use
+ * the compositable or it's IPDL actor here, so we use an ID instead, that
+ * is matched on the compositor side.
+ */
+ void AttachAsyncCompositable(const CompositableHandle& aHandle,
+ ShadowableLayer* aLayer);
+
+ /**
+ * Begin recording a transaction to be forwarded atomically to a
+ * LayerManagerComposite.
+ */
+ void BeginTransaction(const gfx::IntRect& aTargetBounds,
+ ScreenRotation aRotation,
+ hal::ScreenOrientation aOrientation);
+
+ /**
+ * The following methods may only be called after BeginTransaction()
+ * but before EndTransaction(). They mirror the LayerManager
+ * interface in Layers.h.
+ */
+
+ /**
+ * Notify the shadow manager that a new, "real" layer has been
+ * created, and a corresponding shadow layer should be created in
+ * the compositing process.
+ */
+ void CreatedPaintedLayer(ShadowableLayer* aThebes);
+ void CreatedContainerLayer(ShadowableLayer* aContainer);
+ void CreatedImageLayer(ShadowableLayer* aImage);
+ void CreatedColorLayer(ShadowableLayer* aColor);
+ void CreatedCanvasLayer(ShadowableLayer* aCanvas);
+ void CreatedRefLayer(ShadowableLayer* aRef);
+
+ /**
+ * At least one attribute of |aMutant| has changed, and |aMutant|
+ * needs to sync to its shadow layer. This initial implementation
+ * forwards all attributes when any of the appropriate attribute
+ * set is mutated.
+ */
+ void Mutated(ShadowableLayer* aMutant);
+ void MutatedSimple(ShadowableLayer* aMutant);
+
+ void SetRoot(ShadowableLayer* aRoot);
+ /**
+ * Insert |aChild| after |aAfter| in |aContainer|. |aAfter| can be
+ * nullptr to indicated that |aChild| should be appended to the end of
+ * |aContainer|'s child list.
+ */
+ void InsertAfter(ShadowableLayer* aContainer, ShadowableLayer* aChild,
+ ShadowableLayer* aAfter = nullptr);
+ void RemoveChild(ShadowableLayer* aContainer, ShadowableLayer* aChild);
+ void RepositionChild(ShadowableLayer* aContainer, ShadowableLayer* aChild,
+ ShadowableLayer* aAfter = nullptr);
+
+ /**
+ * Set aMaskLayer as the mask on aLayer.
+ * Note that only image layers are properly supported
+ * LayerTransactionParent::UpdateMask and accompanying ipdl
+ * will need changing to update properties for other kinds
+ * of mask layer.
+ */
+ void SetMask(ShadowableLayer* aLayer, ShadowableLayer* aMaskLayer);
+
+ /**
+ * See CompositableForwarder::UseTiledLayerBuffer
+ */
+ void UseTiledLayerBuffer(
+ CompositableClient* aCompositable,
+ const SurfaceDescriptorTiles& aTileLayerDescriptor) override;
+
+ void ReleaseCompositable(const CompositableHandle& aHandle) override;
+ bool DestroyInTransaction(PTextureChild* aTexture) override;
+ bool DestroyInTransaction(const CompositableHandle& aHandle);
+
+ void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) override;
+
+ /**
+ * Communicate to the compositor that aRegion in the texture identified by
+ * aLayer and aIdentifier has been updated to aThebesBuffer.
+ */
+ void UpdateTextureRegion(CompositableClient* aCompositable,
+ const ThebesBufferData& aThebesBufferData,
+ const nsIntRegion& aUpdatedRegion) override;
+
+ /**
+ * See CompositableForwarder::UseTextures
+ */
+ void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) override;
+ void UseComponentAlphaTextures(CompositableClient* aCompositable,
+ TextureClient* aClientOnBlack,
+ TextureClient* aClientOnWhite) override;
+
+ /**
+ * Used for debugging to tell the compositor how long this frame took to
+ * paint.
+ */
+ void SendPaintTime(TransactionId aId, TimeDuration aPaintTime);
+
+ /**
+ * End the current transaction and forward it to LayerManagerComposite.
+ * |aReplies| are directions from the LayerManagerComposite to the
+ * caller of EndTransaction().
+ */
+ bool EndTransaction(const nsIntRegion& aRegionToClear, TransactionId aId,
+ bool aScheduleComposite, uint32_t aPaintSequenceNumber,
+ bool aIsRepeatTransaction,
+ const mozilla::VsyncId& aVsyncId,
+ const mozilla::TimeStamp& aVsyncTime,
+ const mozilla::TimeStamp& aRefreshStart,
+ const mozilla::TimeStamp& aTransactionStart,
+ bool aContainsSVG, const nsCString& aURL, bool* aSent,
+ const nsTArray<CompositionPayload>& aPayload =
+ nsTArray<CompositionPayload>());
+
+ /**
+ * Set an actor through which layer updates will be pushed.
+ */
+ void SetShadowManager(PLayerTransactionChild* aShadowManager);
+
+ /**
+ * Layout calls here to cache current plugin widget configuration
+ * data. We ship this across with the rest of the layer updates when
+ * we update. Chrome handles applying these changes.
+ */
+ void StorePluginWidgetConfigurations(
+ const nsTArray<nsIWidget::Configuration>& aConfigurations);
+
+ void StopReceiveAsyncParentMessge();
+
+ void ClearCachedResources();
+
+ void ScheduleComposite();
+
+ /**
+ * True if this is forwarding to a LayerManagerComposite.
+ */
+ bool HasShadowManager() const { return !!mShadowManager; }
+ LayerTransactionChild* GetShadowManager() const {
+ return mShadowManager.get();
+ }
+
+ // Send a synchronous message asking the LayerTransactionParent in the
+ // compositor to shutdown.
+ void SynchronouslyShutdown();
+
+ /**
+ * The following Alloc/Open/Destroy interfaces abstract over the
+ * details of working with surfaces that are shared across
+ * processes. They provide the glue between C++ Layers and the
+ * LayerComposite IPC system.
+ *
+ * The basic lifecycle is
+ *
+ * - a Layer needs a buffer. Its ShadowableLayer subclass calls
+ * AllocBuffer(), then calls one of the Created*Buffer() methods
+ * above to transfer the (temporary) front buffer to its
+ * LayerComposite in the other process. The Layer needs a
+ * gfxASurface to paint, so the ShadowableLayer uses
+ * OpenDescriptor(backBuffer) to get that surface, and hands it
+ * out to the Layer.
+ *
+ * - a Layer has painted new pixels. Its ShadowableLayer calls one
+ * of the Painted*Buffer() methods above with the back buffer
+ * descriptor. This notification is forwarded to the LayerComposite,
+ * which uses OpenDescriptor() to access the newly-painted pixels.
+ * The LayerComposite then updates its front buffer in a Layer- and
+ * platform-dependent way, and sends a surface descriptor back to
+ * the ShadowableLayer that becomes its new back back buffer.
+ *
+ * - a Layer wants to destroy its buffers. Its ShadowableLayer
+ * calls Destroyed*Buffer(), which gives up control of the back
+ * buffer descriptor. The actual back buffer surface is then
+ * destroyed using DestroySharedSurface() just before notifying
+ * the parent process. When the parent process is notified, the
+ * LayerComposite also calls DestroySharedSurface() on its front
+ * buffer, and the double-buffer pair is gone.
+ */
+
+ bool IPCOpen() const override;
+
+ /**
+ * Construct a shadow of |aLayer| on the "other side", at the
+ * LayerManagerComposite.
+ */
+ LayerHandle ConstructShadowFor(ShadowableLayer* aLayer);
+
+ /**
+ * Flag the next paint as the first for a document.
+ */
+ void SetIsFirstPaint() { mIsFirstPaint = true; }
+ bool GetIsFirstPaint() const { return mIsFirstPaint; }
+
+ /**
+ * Set the current focus target to be sent with the next paint.
+ */
+ void SetFocusTarget(const FocusTarget& aFocusTarget) {
+ mFocusTarget = aFocusTarget;
+ }
+
+ void SetLayersObserverEpoch(LayersObserverEpoch aEpoch);
+
+ static void PlatformSyncBeforeUpdate();
+
+ bool AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer) override;
+
+ bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
+ gfxContentType aContent, uint32_t aCaps,
+ SurfaceDescriptor* aBuffer) override;
+
+ void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) override;
+
+ void UpdateFwdTransactionId() override;
+ uint64_t GetFwdTransactionId() override;
+
+ void UpdateTextureLocks();
+ void SyncTextures(const nsTArray<uint64_t>& aSerials);
+
+ void ReleaseLayer(const LayerHandle& aHandle);
+
+ bool InForwarderThread() override { return NS_IsMainThread(); }
+
+ PaintTiming& GetPaintTiming() { return mPaintTiming; }
+
+ ShadowLayerForwarder* AsLayerForwarder() override { return this; }
+
+ // Returns true if aSurface wraps a Shmem.
+ static bool IsShmem(SurfaceDescriptor* aSurface);
+
+ void SyncWithCompositor() override;
+
+ TextureForwarder* GetTextureForwarder() override {
+ return GetCompositorBridgeChild();
+ }
+ LayersIPCActor* GetLayersIPCActor() override { return this; }
+
+ ActiveResourceTracker* GetActiveResourceTracker() override {
+ return mActiveResourceTracker.get();
+ }
+
+ CompositorBridgeChild* GetCompositorBridgeChild();
+
+ nsISerialEventTarget* GetEventTarget() { return mEventTarget; };
+
+ bool IsThreadSafe() const override { return false; }
+
+ RefPtr<KnowsCompositor> GetForMedia() override;
+
+ protected:
+ virtual ~ShadowLayerForwarder();
+
+ explicit ShadowLayerForwarder(ClientLayerManager* aClientLayerManager);
+
+#ifdef DEBUG
+ void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const;
+#else
+ void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const {}
+#endif
+
+ RefPtr<CompositableClient> FindCompositable(
+ const CompositableHandle& aHandle);
+
+ bool InWorkerThread();
+
+ RefPtr<LayerTransactionChild> mShadowManager;
+ RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
+
+ private:
+ ClientLayerManager* mClientLayerManager;
+ Transaction* mTxn;
+ nsCOMPtr<nsISerialEventTarget> mThread;
+ DiagnosticTypes mDiagnosticTypes;
+ bool mIsFirstPaint;
+ FocusTarget mFocusTarget;
+ nsTArray<PluginWindowData> mPluginWindowData;
+ UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
+ uint64_t mNextLayerHandle;
+ nsDataHashtable<nsUint64HashKey, CompositableClient*> mCompositables;
+ PaintTiming mPaintTiming;
+ /**
+ * ShadowLayerForwarder might dispatch tasks to main while puppet widget and
+ * browserChild don't exist anymore; therefore we hold the event target since
+ * its lifecycle is independent of these objects.
+ */
+ nsCOMPtr<nsISerialEventTarget> mEventTarget;
+};
+
+class CompositableClient;
+
+/**
+ * A ShadowableLayer is a Layer can be shared with a parent context
+ * through a ShadowLayerForwarder. A ShadowableLayer maps to a
+ * Shadow*Layer in a parent context.
+ *
+ * Note that ShadowLayers can themselves be ShadowableLayers.
+ */
+class ShadowableLayer {
+ public:
+ virtual ~ShadowableLayer();
+
+ virtual Layer* AsLayer() = 0;
+
+ /**
+ * True if this layer has a shadow in a parent process.
+ */
+ bool HasShadow() { return mShadow.IsValid(); }
+
+ /**
+ * Return the IPC handle to a Shadow*Layer referring to this if one
+ * exists, nullptr if not.
+ */
+ const LayerHandle& GetShadow() { return mShadow; }
+
+ void SetShadow(ShadowLayerForwarder* aForwarder, const LayerHandle& aShadow) {
+ MOZ_ASSERT(!mShadow, "can't have two shadows (yet)");
+ mForwarder = aForwarder;
+ mShadow = aShadow;
+ }
+
+ virtual CompositableClient* GetCompositableClient() { return nullptr; }
+
+ protected:
+ ShadowableLayer() = default;
+
+ private:
+ RefPtr<ShadowLayerForwarder> mForwarder;
+ LayerHandle mShadow;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ShadowLayers_h
diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
new file mode 100644
index 0000000000..8beec0f02d
--- /dev/null
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedPlanarYCbCrImage.h"
+#include <stddef.h> // for size_t
+#include <stdio.h> // for printf
+#include "gfx2DGlue.h" // for Moz2D transition helpers
+#include "ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/Types.h" // for SurfaceFormat::SurfaceFormat::YUV
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsISupportsImpl.h" // for Image::AddRef
+#include "mozilla/ipc/Shmem.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+
+SharedPlanarYCbCrImage::SharedPlanarYCbCrImage(ImageClient* aCompositable)
+ : mCompositable(aCompositable) {
+ MOZ_COUNT_CTOR(SharedPlanarYCbCrImage);
+}
+
+SharedPlanarYCbCrImage::SharedPlanarYCbCrImage(
+ TextureClientRecycleAllocator* aRecycleAllocator)
+ : mRecycleAllocator(aRecycleAllocator) {
+ MOZ_COUNT_CTOR(SharedPlanarYCbCrImage);
+}
+
+SharedPlanarYCbCrImage::~SharedPlanarYCbCrImage() {
+ MOZ_COUNT_DTOR(SharedPlanarYCbCrImage);
+}
+
+TextureClientRecycleAllocator* SharedPlanarYCbCrImage::RecycleAllocator() {
+ static const uint32_t MAX_POOLED_VIDEO_COUNT = 5;
+
+ if (!mRecycleAllocator && mCompositable) {
+ if (!mCompositable->HasTextureClientRecycler()) {
+ // Initialize TextureClientRecycler
+ mCompositable->GetTextureClientRecycler()->SetMaxPoolSize(
+ MAX_POOLED_VIDEO_COUNT);
+ }
+ mRecycleAllocator = mCompositable->GetTextureClientRecycler();
+ }
+ return mRecycleAllocator;
+}
+
+size_t SharedPlanarYCbCrImage::SizeOfExcludingThis(
+ MallocSizeOf aMallocSizeOf) const {
+ // NB: Explicitly skipping mTextureClient, the memory is already reported
+ // at time of allocation in GfxMemoryImageReporter.
+ // Not owned:
+ // - mCompositable
+ return 0;
+}
+
+TextureClient* SharedPlanarYCbCrImage::GetTextureClient(
+ KnowsCompositor* aKnowsCompositor) {
+ return mTextureClient.get();
+}
+
+already_AddRefed<gfx::SourceSurface>
+SharedPlanarYCbCrImage::GetAsSourceSurface() {
+ if (!IsValid()) {
+ NS_WARNING("Can't get as surface");
+ return nullptr;
+ }
+ return PlanarYCbCrImage::GetAsSourceSurface();
+}
+
+bool SharedPlanarYCbCrImage::CopyData(const PlanarYCbCrData& aData) {
+ // If mTextureClient has not already been allocated (through Allocate(aData))
+ // allocate it. This code path is slower than the one used when Allocate has
+ // been called since it will trigger a full copy.
+ PlanarYCbCrData data = aData;
+ if (!mTextureClient && !Allocate(data)) {
+ return false;
+ }
+
+ TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ MOZ_ASSERT(false, "Failed to lock the texture.");
+ return false;
+ }
+
+ if (!UpdateYCbCrTextureClient(mTextureClient, aData)) {
+ MOZ_ASSERT(false, "Failed to copy YCbCr data into the TextureClient");
+ return false;
+ }
+ mTextureClient->MarkImmutable();
+ return true;
+}
+
+bool SharedPlanarYCbCrImage::AdoptData(const Data& aData) {
+ MOZ_ASSERT(false, "This shouldn't be used.");
+ return false;
+}
+
+bool SharedPlanarYCbCrImage::IsValid() const {
+ return mTextureClient && mTextureClient->IsValid();
+}
+
+bool SharedPlanarYCbCrImage::Allocate(PlanarYCbCrData& aData) {
+ MOZ_ASSERT(!mTextureClient, "This image already has allocated data");
+
+ TextureFlags flags =
+ mCompositable ? mCompositable->GetTextureFlags() : TextureFlags::DEFAULT;
+ {
+ YCbCrTextureClientAllocationHelper helper(aData, flags);
+ mTextureClient = RecycleAllocator()->CreateOrRecycle(helper);
+ }
+
+ if (!mTextureClient) {
+ NS_WARNING("SharedPlanarYCbCrImage::Allocate failed.");
+ return false;
+ }
+
+ MappedYCbCrTextureData mapped;
+ // The locking here is sort of a lie. The SharedPlanarYCbCrImage just pulls
+ // pointers out of the TextureClient and keeps them around, which works only
+ // because the underlyin BufferTextureData is always mapped in memory even
+ // outside of the lock/unlock interval. That's sad and new code should follow
+ // this example.
+ if (!mTextureClient->Lock(OpenMode::OPEN_READ) ||
+ !mTextureClient->BorrowMappedYCbCrData(mapped)) {
+ MOZ_CRASH("GFX: Cannot lock or borrow mapped YCbCr");
+ }
+
+ aData.mYChannel = mapped.y.data;
+ aData.mCbChannel = mapped.cb.data;
+ aData.mCrChannel = mapped.cr.data;
+
+ // copy some of aData's values in mData (most of them)
+ mData.mYChannel = aData.mYChannel;
+ mData.mCbChannel = aData.mCbChannel;
+ mData.mCrChannel = aData.mCrChannel;
+ mData.mYSize = aData.mYSize;
+ mData.mCbCrSize = aData.mCbCrSize;
+ mData.mPicX = aData.mPicX;
+ mData.mPicY = aData.mPicY;
+ mData.mPicSize = aData.mPicSize;
+ mData.mStereoMode = aData.mStereoMode;
+ mData.mYUVColorSpace = aData.mYUVColorSpace;
+ mData.mColorDepth = aData.mColorDepth;
+ // those members are not always equal to aData's, due to potentially different
+ // packing.
+ mData.mYSkip = 0;
+ mData.mCbSkip = 0;
+ mData.mCrSkip = 0;
+ mData.mYStride = aData.mYStride;
+ mData.mCbCrStride = aData.mCbCrStride;
+
+ // do not set mBuffer like in PlanarYCbCrImage because the later
+ // will try to manage this memory without knowing it belongs to a
+ // shmem.
+ mBufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(
+ mData.mYSize, mData.mYStride, mData.mCbCrSize, mData.mCbCrStride);
+ mSize = mData.mPicSize;
+ mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
+
+ mTextureClient->Unlock();
+
+ return mBufferSize > 0;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.h b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
new file mode 100644
index 0000000000..6e99322268
--- /dev/null
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h> // for uint8_t, uint32_t
+#include "ImageContainer.h" // for PlanarYCbCrImage, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR
+
+#ifndef MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H
+# define MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H
+
+namespace mozilla {
+namespace layers {
+
+class ImageClient;
+class TextureClient;
+class TextureClientRecycleAllocator;
+
+class SharedPlanarYCbCrImage : public PlanarYCbCrImage {
+ public:
+ explicit SharedPlanarYCbCrImage(ImageClient* aCompositable);
+ explicit SharedPlanarYCbCrImage(
+ TextureClientRecycleAllocator* aRecycleAllocator);
+
+ protected:
+ virtual ~SharedPlanarYCbCrImage();
+
+ public:
+ TextureClient* GetTextureClient(KnowsCompositor* aKnowsCompositor) override;
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ bool CopyData(const PlanarYCbCrData& aData) override;
+ bool AdoptData(const Data& aData) override;
+
+ bool Allocate(PlanarYCbCrData& aData);
+
+ bool IsValid() const override;
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
+
+ TextureClientRecycleAllocator* RecycleAllocator();
+
+ private:
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<ImageClient> mCompositable;
+ RefPtr<TextureClientRecycleAllocator> mRecycleAllocator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/SharedRGBImage.cpp b/gfx/layers/ipc/SharedRGBImage.cpp
new file mode 100644
index 0000000000..c7cc028109
--- /dev/null
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedRGBImage.h"
+#include "ImageTypes.h" // for ImageFormat::SHARED_RGB, etc
+#include "Shmem.h" // for Shmem
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "mozilla/gfx/Point.h" // for IntSIze
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureClient.h" // for BufferTextureClient, etc
+#include "mozilla/layers/TextureClientRecycleAllocator.h" // for ITextureClientAllocationHelper
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::AddRef, etc
+#include "nsProxyRelease.h"
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+// Just big enough for a 1080p RGBA32 frame
+#define MAX_FRAME_SIZE (16 * 1024 * 1024)
+
+namespace mozilla {
+namespace layers {
+
+class TextureClientForRawBufferAccessAllocationHelper
+ : public ITextureClientAllocationHelper {
+ public:
+ TextureClientForRawBufferAccessAllocationHelper(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aTextureFlags)
+ : ITextureClientAllocationHelper(aFormat, aSize, BackendSelector::Content,
+ aTextureFlags, ALLOC_DEFAULT) {}
+
+ bool IsCompatible(TextureClient* aTextureClient) override {
+ bool ret = aTextureClient->GetFormat() == mFormat &&
+ aTextureClient->GetSize() == mSize;
+ return ret;
+ }
+
+ already_AddRefed<TextureClient> Allocate(
+ KnowsCompositor* aAllocator) override {
+ return TextureClient::CreateForRawBufferAccess(
+ aAllocator, mFormat, mSize, gfx::BackendType::NONE, mTextureFlags);
+ }
+};
+
+SharedRGBImage::SharedRGBImage(ImageClient* aCompositable)
+ : Image(nullptr, ImageFormat::SHARED_RGB), mCompositable(aCompositable) {
+ MOZ_COUNT_CTOR(SharedRGBImage);
+}
+
+SharedRGBImage::SharedRGBImage(TextureClientRecycleAllocator* aRecycleAllocator)
+ : Image(nullptr, ImageFormat::SHARED_RGB),
+ mRecycleAllocator(aRecycleAllocator) {
+ MOZ_COUNT_CTOR(SharedRGBImage);
+}
+
+SharedRGBImage::~SharedRGBImage() {
+ MOZ_COUNT_DTOR(SharedRGBImage);
+ NS_ReleaseOnMainThread("SharedRGBImage::mSourceSurface",
+ mSourceSurface.forget());
+}
+
+TextureClientRecycleAllocator* SharedRGBImage::RecycleAllocator() {
+ static const uint32_t MAX_POOLED_VIDEO_COUNT = 5;
+
+ if (!mRecycleAllocator && mCompositable) {
+ if (!mCompositable->HasTextureClientRecycler()) {
+ // Initialize TextureClientRecycler
+ mCompositable->GetTextureClientRecycler()->SetMaxPoolSize(
+ MAX_POOLED_VIDEO_COUNT);
+ }
+ mRecycleAllocator = mCompositable->GetTextureClientRecycler();
+ }
+ return mRecycleAllocator;
+}
+
+bool SharedRGBImage::Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
+ mSize = aSize;
+
+ TextureFlags flags =
+ mCompositable ? mCompositable->GetTextureFlags() : TextureFlags::DEFAULT;
+ {
+ TextureClientForRawBufferAccessAllocationHelper helper(aFormat, aSize,
+ flags);
+ mTextureClient = RecycleAllocator()->CreateOrRecycle(helper);
+ }
+
+ return !!mTextureClient;
+}
+
+gfx::IntSize SharedRGBImage::GetSize() const { return mSize; }
+
+TextureClient* SharedRGBImage::GetTextureClient(
+ KnowsCompositor* aKnowsCompositor) {
+ return mTextureClient.get();
+}
+
+static void ReleaseTextureClient(void* aData) {
+ RELEASE_MANUALLY(static_cast<TextureClient*>(aData));
+}
+
+static gfx::UserDataKey sTextureClientKey;
+
+already_AddRefed<gfx::SourceSurface> SharedRGBImage::GetAsSourceSurface() {
+ NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
+
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ RefPtr<gfx::SourceSurface> surface;
+ {
+ // We are 'borrowing' the DrawTarget and retaining a permanent reference to
+ // the underlying data (via the surface). It is in this instance since we
+ // know that the TextureClient is always wrapping a BufferTextureData and
+ // therefore it won't go away underneath us.
+ BufferTextureData* decoded_buffer =
+ mTextureClient->GetInternalData()->AsBufferTextureData();
+ RefPtr<gfx::DrawTarget> drawTarget = decoded_buffer->BorrowDrawTarget();
+
+ if (!drawTarget) {
+ return nullptr;
+ }
+
+ surface = drawTarget->Snapshot();
+ if (!surface) {
+ return nullptr;
+ }
+
+ // The surface may outlive the owning TextureClient. So, we need to ensure
+ // that the surface keeps the TextureClient alive via a reference held in
+ // user data. The TextureClient's DrawTarget only has a weak reference to
+ // the surface, so we won't create any cycles by just referencing the
+ // TextureClient.
+ if (!surface->GetUserData(&sTextureClientKey)) {
+ surface->AddUserData(&sTextureClientKey, mTextureClient,
+ ReleaseTextureClient);
+ ADDREF_MANUALLY(mTextureClient);
+ }
+ }
+
+ mSourceSurface = surface;
+ return surface.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/SharedRGBImage.h b/gfx/layers/ipc/SharedRGBImage.h
new file mode 100644
index 0000000000..ddec2dc9aa
--- /dev/null
+++ b/gfx/layers/ipc/SharedRGBImage.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SHAREDRGBIMAGE_H_
+#define SHAREDRGBIMAGE_H_
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint8_t
+
+#include "ImageContainer.h" // for ISharedImage, Image, etc
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "nsCOMPtr.h" // for already_AddRefed
+
+namespace mozilla {
+namespace layers {
+
+class ImageClient;
+class TextureClient;
+
+/**
+ * Stores RGB data in shared memory
+ * It is assumed that the image width and stride are equal
+ */
+class SharedRGBImage : public Image {
+ public:
+ explicit SharedRGBImage(ImageClient* aCompositable);
+ explicit SharedRGBImage(TextureClientRecycleAllocator* aRecycleAllocator);
+
+ protected:
+ virtual ~SharedRGBImage();
+
+ public:
+ TextureClient* GetTextureClient(KnowsCompositor* aKnowsCompositor) override;
+
+ gfx::IntSize GetSize() const override;
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ bool Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+
+ private:
+ TextureClientRecycleAllocator* RecycleAllocator();
+
+ gfx::IntSize mSize;
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<ImageClient> mCompositable;
+ RefPtr<TextureClientRecycleAllocator> mRecycleAllocator;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/SharedSurfacesChild.cpp b/gfx/layers/ipc/SharedSurfacesChild.cpp
new file mode 100644
index 0000000000..55ece9abdf
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -0,0 +1,657 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurfacesChild.h"
+#include "SharedSurfacesParent.h"
+#include "CompositorManagerChild.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/layers/IpcResourceUpdateQueue.h"
+#include "mozilla/layers/SourceSurfaceSharedData.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/layers/RenderRootStateManager.h"
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/StaticPrefs_image.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+/* static */
+UserDataKey SharedSurfacesChild::sSharedKey;
+
+SharedSurfacesChild::ImageKeyData::ImageKeyData(
+ RenderRootStateManager* aManager, const wr::ImageKey& aImageKey)
+ : mManager(aManager), mImageKey(aImageKey) {}
+
+SharedSurfacesChild::ImageKeyData::ImageKeyData(
+ SharedSurfacesChild::ImageKeyData&& aOther)
+ : mManager(std::move(aOther.mManager)),
+ mDirtyRect(std::move(aOther.mDirtyRect)),
+ mImageKey(aOther.mImageKey) {}
+
+SharedSurfacesChild::ImageKeyData& SharedSurfacesChild::ImageKeyData::operator=(
+ SharedSurfacesChild::ImageKeyData&& aOther) {
+ mManager = std::move(aOther.mManager);
+ mDirtyRect = std::move(aOther.mDirtyRect);
+ mImageKey = aOther.mImageKey;
+ return *this;
+}
+
+SharedSurfacesChild::ImageKeyData::~ImageKeyData() = default;
+
+void SharedSurfacesChild::ImageKeyData::MergeDirtyRect(
+ const Maybe<IntRect>& aDirtyRect) {
+ if (mDirtyRect) {
+ if (aDirtyRect) {
+ mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect.ref());
+ }
+ } else {
+ mDirtyRect = aDirtyRect;
+ }
+}
+
+SharedSurfacesChild::SharedUserData::SharedUserData(
+ const wr::ExternalImageId& aId)
+ : Runnable("SharedSurfacesChild::SharedUserData"),
+ mId(aId),
+ mShared(false) {}
+
+SharedSurfacesChild::SharedUserData::~SharedUserData() {
+ // We may fail to dispatch during shutdown, and since it would be issued on
+ // the main thread, it releases the runnable instead of leaking it.
+ if (mShared || !mKeys.IsEmpty()) {
+ if (NS_IsMainThread()) {
+ SharedSurfacesChild::Unshare(mId, mShared, mKeys);
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Shared resources not released!");
+ }
+ }
+}
+
+/* static */
+void SharedSurfacesChild::SharedUserData::Destroy(void* aClosure) {
+ MOZ_ASSERT(aClosure);
+ RefPtr<SharedUserData> data =
+ dont_AddRef(static_cast<SharedUserData*>(aClosure));
+ if (data->mShared || !data->mKeys.IsEmpty()) {
+ SchedulerGroup::Dispatch(TaskCategory::Other, data.forget());
+ }
+}
+
+NS_IMETHODIMP SharedSurfacesChild::SharedUserData::Run() {
+ SharedSurfacesChild::Unshare(mId, mShared, mKeys);
+ mShared = false;
+ mKeys.Clear();
+ return NS_OK;
+}
+
+wr::ImageKey SharedSurfacesChild::SharedUserData::UpdateKey(
+ RenderRootStateManager* aManager, wr::IpcResourceUpdateQueue& aResources,
+ const Maybe<IntRect>& aDirtyRect) {
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(!aManager->IsDestroyed());
+
+ // We iterate through all of the items to ensure we clean up the old
+ // RenderRootStateManager references. Most of the time there will be few
+ // entries and this should not be particularly expensive compared to the
+ // cost of duplicating image keys. In an ideal world, we would generate a
+ // single key for the surface, and it would be usable on all of the
+ // renderer instances. For now, we must allocate a key for each WR bridge.
+ wr::ImageKey key;
+ bool found = false;
+ auto i = mKeys.Length();
+ while (i > 0) {
+ --i;
+ ImageKeyData& entry = mKeys[i];
+ if (entry.mManager->IsDestroyed()) {
+ mKeys.RemoveElementAt(i);
+ } else if (entry.mManager == aManager) {
+ WebRenderBridgeChild* wrBridge = aManager->WrBridge();
+ MOZ_ASSERT(wrBridge);
+
+ // Even if the manager is the same, its underlying WebRenderBridgeChild
+ // can change state. If our namespace differs, then our old key has
+ // already been discarded.
+ bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace;
+ if (!ownsKey) {
+ entry.mImageKey = wrBridge->GetNextImageKey();
+ entry.TakeDirtyRect();
+ aResources.AddSharedExternalImage(mId, entry.mImageKey);
+ } else {
+ entry.MergeDirtyRect(aDirtyRect);
+ Maybe<IntRect> dirtyRect = entry.TakeDirtyRect();
+ if (dirtyRect) {
+ MOZ_ASSERT(mShared);
+ aResources.UpdateSharedExternalImage(
+ mId, entry.mImageKey, ViewAs<ImagePixel>(dirtyRect.ref()));
+ }
+ }
+
+ key = entry.mImageKey;
+ found = true;
+ } else {
+ // We don't have the resource update queue for this manager, so just
+ // accumulate the dirty rects until it is requested.
+ entry.MergeDirtyRect(aDirtyRect);
+ }
+ }
+
+ if (!found) {
+ key = aManager->WrBridge()->GetNextImageKey();
+ ImageKeyData data(aManager, key);
+ mKeys.AppendElement(std::move(data));
+ aResources.AddSharedExternalImage(mId, key);
+ }
+
+ return key;
+}
+
+/* static */
+SourceSurfaceSharedData* SharedSurfacesChild::AsSourceSurfaceSharedData(
+ SourceSurface* aSurface) {
+ MOZ_ASSERT(aSurface);
+ switch (aSurface->GetType()) {
+ case SurfaceType::DATA_SHARED:
+ case SurfaceType::DATA_RECYCLING_SHARED:
+ return static_cast<SourceSurfaceSharedData*>(aSurface);
+ default:
+ return nullptr;
+ }
+}
+
+/* static */
+nsresult SharedSurfacesChild::ShareInternal(SourceSurfaceSharedData* aSurface,
+ SharedUserData** aUserData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+ MOZ_ASSERT(aUserData);
+
+ CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
+ if (NS_WARN_IF(!manager || !manager->CanSend() || !gfxVars::UseWebRender())) {
+ // We cannot try to share the surface, most likely because the GPU process
+ // crashed. Ideally, we would retry when it is ready, but the handles may be
+ // a scarce resource, which can cause much more serious problems if we run
+ // out. Better to copy into a fresh buffer later.
+ aSurface->FinishedSharing();
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ SharedUserData* data =
+ static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey));
+ if (!data) {
+ data =
+ MakeAndAddRef<SharedUserData>(manager->GetNextExternalImageId()).take();
+ aSurface->AddUserData(&sSharedKey, data, SharedUserData::Destroy);
+ } else if (!manager->OwnsExternalImageId(data->Id())) {
+ // If the id isn't owned by us, that means the bridge was reinitialized, due
+ // to the GPU process crashing. All previous mappings have been released.
+ data->SetId(manager->GetNextExternalImageId());
+ } else if (data->IsShared()) {
+ // It has already been shared with the GPU process.
+ *aUserData = data;
+ return NS_OK;
+ }
+
+ // Ensure that the handle doesn't get released until after we have finished
+ // sending the buffer to the GPU process and/or reallocating it.
+ // FinishedSharing is not a sufficient condition because another thread may
+ // decide we are done while we are in the processing of sharing our newly
+ // reallocated handle. Once it goes out of scope, it may release the handle.
+ SourceSurfaceSharedData::HandleLock lock(aSurface);
+
+ // If we live in the same process, then it is a simple matter of directly
+ // asking the parent instance to store a pointer to the same data, no need
+ // to map the data into our memory space twice.
+ auto pid = manager->OtherPid();
+ if (pid == base::GetCurrentProcId()) {
+ SharedSurfacesParent::AddSameProcess(data->Id(), aSurface);
+ data->MarkShared();
+ *aUserData = data;
+ return NS_OK;
+ }
+
+ // Attempt to share a handle with the GPU process. The handle may or may not
+ // be available -- it will only be available if it is either not yet finalized
+ // and/or if it has been finalized but never used for drawing in process.
+ ipc::SharedMemoryBasic::Handle handle = ipc::SharedMemoryBasic::NULLHandle();
+ nsresult rv = aSurface->ShareToProcess(pid, handle);
+ if (rv == NS_ERROR_NOT_AVAILABLE) {
+ // It is at least as expensive to copy the image to the GPU process if we
+ // have already closed the handle necessary to share, but if we reallocate
+ // the shared buffer to get a new handle, we can save some memory.
+ if (NS_WARN_IF(!aSurface->ReallocHandle())) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Reattempt the sharing of the handle to the GPU process.
+ rv = aSurface->ShareToProcess(pid, handle);
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ MOZ_ASSERT(rv != NS_ERROR_NOT_AVAILABLE);
+ return rv;
+ }
+
+ SurfaceFormat format = aSurface->GetFormat();
+ MOZ_RELEASE_ASSERT(
+ format == SurfaceFormat::B8G8R8X8 || format == SurfaceFormat::B8G8R8A8,
+ "bad format");
+
+ data->MarkShared();
+ manager->SendAddSharedSurface(
+ data->Id(), SurfaceDescriptorShared(aSurface->GetSize(),
+ aSurface->Stride(), format, handle));
+ *aUserData = data;
+ return NS_OK;
+}
+
+/* static */
+void SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface) {
+ MOZ_ASSERT(aSurface);
+
+ // The IPDL actor to do sharing can only be accessed on the main thread so we
+ // need to dispatch if off the main thread. However there is no real danger if
+ // we end up racing because if it is already shared, this method will do
+ // nothing.
+ if (!NS_IsMainThread()) {
+ class ShareRunnable final : public Runnable {
+ public:
+ explicit ShareRunnable(SourceSurfaceSharedData* aSurface)
+ : Runnable("SharedSurfacesChild::Share"), mSurface(aSurface) {}
+
+ NS_IMETHOD Run() override {
+ SharedUserData* unused = nullptr;
+ SharedSurfacesChild::ShareInternal(mSurface, &unused);
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<SourceSurfaceSharedData> mSurface;
+ };
+
+ SchedulerGroup::Dispatch(TaskCategory::Other,
+ MakeAndAddRef<ShareRunnable>(aSurface));
+ return;
+ }
+
+ SharedUserData* unused = nullptr;
+ SharedSurfacesChild::ShareInternal(aSurface, &unused);
+}
+
+/* static */
+nsresult SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+ MOZ_ASSERT(aManager);
+
+ // Each time the surface changes, the producers of SourceSurfaceSharedData
+ // surfaces promise to increment the invalidation counter each time the
+ // surface has changed. We can use this counter to determine whether or not
+ // we should update our paired ImageKey.
+ Maybe<IntRect> dirtyRect = aSurface->TakeDirtyRect();
+ SharedUserData* data = nullptr;
+ nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data);
+ if (NS_SUCCEEDED(rv)) {
+ MOZ_ASSERT(data);
+ aKey = data->UpdateKey(aManager, aResources, dirtyRect);
+ }
+
+ return rv;
+}
+
+/* static */
+nsresult SharedSurfacesChild::Share(SourceSurface* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+ MOZ_ASSERT(aManager);
+
+ auto sharedSurface = AsSourceSurfaceSharedData(aSurface);
+ if (!sharedSurface) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ return Share(sharedSurface, aManager, aResources, aKey);
+}
+
+/* static */
+nsresult SharedSurfacesChild::Share(ImageContainer* aContainer,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey,
+ ContainerProducerID aProducerId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aContainer);
+ MOZ_ASSERT(aManager);
+
+ if (aContainer->IsAsync()) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ AutoTArray<ImageContainer::OwningImage, 4> images;
+ aContainer->GetCurrentImages(&images);
+ if (images.IsEmpty()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (aProducerId != kContainerProducerID_Invalid &&
+ images[0].mProducerID != aProducerId) {
+ // If the producer ID of the surface in the container does not match the
+ // expected producer ID, then we do not want to proceed with sharing. This
+ // is useful for when callers are unsure if given container is for the same
+ // producer / underlying image request.
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface();
+ if (!surface) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ auto sharedSurface = AsSourceSurfaceSharedData(surface);
+ if (!sharedSurface) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ SharedSurfacesAnimation* anim = aContainer->GetSharedSurfacesAnimation();
+ if (anim) {
+ return anim->UpdateKey(sharedSurface, aManager, aResources, aKey);
+ }
+
+ return Share(sharedSurface, aManager, aResources, aKey);
+}
+
+/* static */
+nsresult SharedSurfacesChild::Share(SourceSurface* aSurface,
+ wr::ExternalImageId& aId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+
+ auto sharedSurface = AsSourceSurfaceSharedData(aSurface);
+ if (!sharedSurface) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ // The external image ID does not change with the invalidation counter. The
+ // caller of this should be aware of the invalidations of the surface through
+ // another mechanism (e.g. imgRequestProxy listener notifications).
+ SharedUserData* data = nullptr;
+ nsresult rv = ShareInternal(sharedSurface, &data);
+ if (NS_SUCCEEDED(rv)) {
+ MOZ_ASSERT(data);
+ aId = data->Id();
+ }
+
+ return rv;
+}
+
+/* static */
+void SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId,
+ bool aReleaseId,
+ nsTArray<ImageKeyData>& aKeys) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ for (const auto& entry : aKeys) {
+ if (!entry.mManager->IsDestroyed()) {
+ entry.mManager->AddImageKeyForDiscard(entry.mImageKey);
+ }
+ }
+
+ if (!aReleaseId) {
+ // We don't own the external image ID itself.
+ return;
+ }
+
+ CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
+ if (MOZ_UNLIKELY(!manager || !manager->CanSend())) {
+ return;
+ }
+
+ if (manager->OwnsExternalImageId(aId)) {
+ // Only attempt to release current mappings in the compositor process. It is
+ // possible we had a surface that was previously shared, the compositor
+ // process crashed / was restarted, and then we freed the surface. In that
+ // case we know the mapping has already been freed.
+ manager->SendRemoveSharedSurface(aId);
+ }
+}
+
+/* static */ Maybe<wr::ExternalImageId> SharedSurfacesChild::GetExternalId(
+ const SourceSurfaceSharedData* aSurface) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+
+ SharedUserData* data =
+ static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey));
+ if (!data || !data->IsShared()) {
+ return Nothing();
+ }
+
+ return Some(data->Id());
+}
+
+/* static */
+nsresult SharedSurfacesChild::UpdateAnimation(ImageContainer* aContainer,
+ SourceSurface* aSurface,
+ const IntRect& aDirtyRect) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aContainer);
+ MOZ_ASSERT(!aContainer->IsAsync());
+ MOZ_ASSERT(aSurface);
+
+ // If we aren't using shared surfaces, then is nothing to do.
+ auto sharedSurface = SharedSurfacesChild::AsSourceSurfaceSharedData(aSurface);
+ if (!sharedSurface) {
+ MOZ_ASSERT(!aContainer->GetSharedSurfacesAnimation());
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ SharedSurfacesAnimation* anim = aContainer->EnsureSharedSurfacesAnimation();
+ MOZ_ASSERT(anim);
+
+ return anim->SetCurrentFrame(sharedSurface, aDirtyRect);
+}
+
+AnimationImageKeyData::AnimationImageKeyData(RenderRootStateManager* aManager,
+ const wr::ImageKey& aImageKey)
+ : SharedSurfacesChild::ImageKeyData(aManager, aImageKey) {}
+
+AnimationImageKeyData::AnimationImageKeyData(AnimationImageKeyData&& aOther)
+ : SharedSurfacesChild::ImageKeyData(std::move(aOther)),
+ mPendingRelease(std::move(aOther.mPendingRelease)) {}
+
+AnimationImageKeyData& AnimationImageKeyData::operator=(
+ AnimationImageKeyData&& aOther) {
+ mPendingRelease = std::move(aOther.mPendingRelease);
+ SharedSurfacesChild::ImageKeyData::operator=(std::move(aOther));
+ return *this;
+}
+
+AnimationImageKeyData::~AnimationImageKeyData() = default;
+
+SharedSurfacesAnimation::~SharedSurfacesAnimation() {
+ MOZ_ASSERT(mKeys.IsEmpty());
+}
+
+void SharedSurfacesAnimation::Destroy() {
+ if (!NS_IsMainThread()) {
+ nsCOMPtr<nsIRunnable> task =
+ NewRunnableMethod("SharedSurfacesAnimation::Destroy", this,
+ &SharedSurfacesAnimation::Destroy);
+ SchedulerGroup::Dispatch(TaskCategory::Other, task.forget());
+ return;
+ }
+
+ if (mKeys.IsEmpty()) {
+ return;
+ }
+
+ for (const auto& entry : mKeys) {
+ MOZ_ASSERT(!entry.mManager->IsDestroyed());
+ if (StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()) {
+ entry.mManager->DeregisterAsyncAnimation(entry.mImageKey);
+ }
+ entry.mManager->AddImageKeyForDiscard(entry.mImageKey);
+ }
+
+ mKeys.Clear();
+}
+
+void SharedSurfacesAnimation::HoldSurfaceForRecycling(
+ AnimationImageKeyData& aEntry, SourceSurfaceSharedData* aSurface) {
+ if (aSurface->GetType() != SurfaceType::DATA_RECYCLING_SHARED) {
+ return;
+ }
+
+ MOZ_ASSERT(StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup());
+ aEntry.mPendingRelease.AppendElement(aSurface);
+}
+
+nsresult SharedSurfacesAnimation::SetCurrentFrame(
+ SourceSurfaceSharedData* aSurface, const gfx::IntRect& aDirtyRect) {
+ MOZ_ASSERT(aSurface);
+
+ SharedSurfacesChild::SharedUserData* data = nullptr;
+ nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ MOZ_ASSERT(data);
+ mId = data->Id();
+
+ auto i = mKeys.Length();
+ while (i > 0) {
+ --i;
+ AnimationImageKeyData& entry = mKeys[i];
+ MOZ_ASSERT(!entry.mManager->IsDestroyed());
+
+ entry.MergeDirtyRect(Some(aDirtyRect));
+ Maybe<IntRect> dirtyRect = entry.TakeDirtyRect();
+ if (dirtyRect) {
+ HoldSurfaceForRecycling(entry, aSurface);
+ auto& resourceUpdates = entry.mManager->AsyncResourceUpdates();
+ resourceUpdates.UpdateSharedExternalImage(
+ mId, entry.mImageKey, ViewAs<ImagePixel>(dirtyRect.ref()));
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult SharedSurfacesAnimation::UpdateKey(
+ SourceSurfaceSharedData* aSurface, RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
+ SharedSurfacesChild::SharedUserData* data = nullptr;
+ nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ MOZ_ASSERT(data);
+ if (wr::AsUint64(mId) != wr::AsUint64(data->Id())) {
+ mKeys.Clear();
+ mId = data->Id();
+ }
+
+ // We iterate through all of the items to ensure we clean up the old
+ // RenderRootStateManager references. Most of the time there will be few
+ // entries and this should not be particularly expensive compared to the
+ // cost of duplicating image keys. In an ideal world, we would generate a
+ // single key for the surface, and it would be usable on all of the
+ // renderer instances. For now, we must allocate a key for each WR bridge.
+ bool found = false;
+ auto i = mKeys.Length();
+ while (i > 0) {
+ --i;
+ AnimationImageKeyData& entry = mKeys[i];
+ MOZ_ASSERT(!entry.mManager->IsDestroyed());
+ if (entry.mManager == aManager) {
+ WebRenderBridgeChild* wrBridge = aManager->WrBridge();
+ MOZ_ASSERT(wrBridge);
+
+ // Even if the manager is the same, its underlying WebRenderBridgeChild
+ // can change state. If our namespace differs, then our old key has
+ // already been discarded.
+ bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace;
+ if (!ownsKey) {
+ entry.mImageKey = wrBridge->GetNextImageKey();
+ HoldSurfaceForRecycling(entry, aSurface);
+ aResources.AddSharedExternalImage(mId, entry.mImageKey);
+ } else {
+ MOZ_ASSERT(entry.mDirtyRect.isNothing());
+ }
+
+ aKey = entry.mImageKey;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ aKey = aManager->WrBridge()->GetNextImageKey();
+ if (StaticPrefs::image_animated_decode_on_demand_recycle_AtStartup()) {
+ aManager->RegisterAsyncAnimation(aKey, this);
+ }
+
+ AnimationImageKeyData data(aManager, aKey);
+ HoldSurfaceForRecycling(data, aSurface);
+ mKeys.AppendElement(std::move(data));
+ aResources.AddSharedExternalImage(mId, aKey);
+ }
+
+ return NS_OK;
+}
+
+void SharedSurfacesAnimation::ReleasePreviousFrame(
+ RenderRootStateManager* aManager, const wr::ExternalImageId& aId) {
+ MOZ_ASSERT(aManager);
+
+ auto i = mKeys.Length();
+ while (i > 0) {
+ --i;
+ AnimationImageKeyData& entry = mKeys[i];
+ MOZ_ASSERT(!entry.mManager->IsDestroyed());
+ if (entry.mManager == aManager) {
+ size_t k;
+ for (k = 0; k < entry.mPendingRelease.Length(); ++k) {
+ Maybe<wr::ExternalImageId> extId =
+ SharedSurfacesChild::GetExternalId(entry.mPendingRelease[k]);
+ if (extId && extId.ref() == aId) {
+ break;
+ }
+ }
+
+ if (k == entry.mPendingRelease.Length()) {
+ continue;
+ }
+
+ entry.mPendingRelease.RemoveElementsAt(0, k + 1);
+ break;
+ }
+ }
+}
+
+void SharedSurfacesAnimation::Invalidate(RenderRootStateManager* aManager) {
+ auto i = mKeys.Length();
+ while (i > 0) {
+ --i;
+ AnimationImageKeyData& entry = mKeys[i];
+ if (entry.mManager == aManager) {
+ mKeys.RemoveElementAt(i);
+ break;
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/SharedSurfacesChild.h b/gfx/layers/ipc/SharedSurfacesChild.h
new file mode 100644
index 0000000000..cc0f2fc2f5
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesChild.h
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SHAREDSURFACESCHILD_H
+#define MOZILLA_GFX_SHAREDSURFACESCHILD_H
+
+#include <stdint.h> // for uint32_t, uint64_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Maybe.h" // for Maybe
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/gfx/UserData.h" // for UserDataKey
+#include "mozilla/webrender/WebRenderTypes.h" // for wr::ImageKey
+#include "nsTArray.h" // for AutoTArray
+#include "nsThreadUtils.h" // for Runnable
+#include "ImageTypes.h" // for ContainerProducerID
+
+namespace mozilla {
+namespace layers {
+class AnimationImageKeyData;
+} // namespace layers
+} // namespace mozilla
+
+template <>
+struct nsTArray_RelocationStrategy<mozilla::layers::AnimationImageKeyData> {
+ typedef nsTArray_RelocateUsingMoveConstructor<
+ mozilla::layers::AnimationImageKeyData>
+ Type;
+};
+
+namespace mozilla {
+namespace gfx {
+class SourceSurface;
+class SourceSurfaceSharedData;
+} // namespace gfx
+
+namespace wr {
+class IpcResourceUpdateQueue;
+} // namespace wr
+
+namespace layers {
+
+class CompositorManagerChild;
+class ImageContainer;
+class RenderRootStateManager;
+
+class SharedSurfacesChild {
+ public:
+ /**
+ * Request that the surface be mapped into the compositor thread's memory
+ * space. This is useful for when the caller itself has no present need for
+ * the surface to be mapped, but knows there will be such a need in the
+ * future. This may be called from any thread, but it may cause a dispatch to
+ * the main thread.
+ */
+ static void Share(gfx::SourceSurfaceSharedData* aSurface);
+
+ /**
+ * Request that the surface be mapped into the compositor thread's memory
+ * space, and a valid ExternalImageId be generated for it for use with
+ * WebRender. This must be called from the main thread.
+ */
+ static nsresult Share(gfx::SourceSurface* aSurface, wr::ExternalImageId& aId);
+
+ /**
+ * Request that the surface be mapped into the compositor thread's memory
+ * space, and a valid ImageKey be generated for it for use with WebRender.
+ * This must be called from the main thread.
+ */
+ static nsresult Share(gfx::SourceSurfaceSharedData* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey);
+
+ /**
+ * Request that the surface be mapped into the compositor thread's memory
+ * space, and a valid ImageKey be generated for it for use with WebRender.
+ * This must be called from the main thread.
+ */
+ static nsresult Share(gfx::SourceSurface* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey);
+
+ /**
+ * Request that the first surface in the image container's current images be
+ * mapped into the compositor thread's memory space, and a valid ImageKey be
+ * generated for it for use with WebRender. If a different method should be
+ * used to share the image data for this particular container, it will return
+ * NS_ERROR_NOT_IMPLEMENTED. This must be called from the main thread.
+ */
+ static nsresult Share(ImageContainer* aContainer,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey, ContainerProducerID aProducerId);
+
+ /**
+ * Get the external ID, if any, bound to the shared surface. Used for memory
+ * reporting purposes.
+ */
+ static Maybe<wr::ExternalImageId> GetExternalId(
+ const gfx::SourceSurfaceSharedData* aSurface);
+
+ /**
+ * Get the surface (or its underlying surface) as a SourceSurfaceSharedData
+ * pointer, if valid.
+ */
+ static gfx::SourceSurfaceSharedData* AsSourceSurfaceSharedData(
+ gfx::SourceSurface* aSurface);
+
+ static nsresult UpdateAnimation(ImageContainer* aContainer,
+ gfx::SourceSurface* aSurface,
+ const gfx::IntRect& aDirtyRect);
+
+ class ImageKeyData {
+ public:
+ ImageKeyData(RenderRootStateManager* aManager,
+ const wr::ImageKey& aImageKey);
+ virtual ~ImageKeyData();
+
+ ImageKeyData(ImageKeyData&& aOther);
+ ImageKeyData& operator=(ImageKeyData&& aOther);
+ ImageKeyData(const ImageKeyData&) = delete;
+ ImageKeyData& operator=(const ImageKeyData&) = delete;
+
+ void MergeDirtyRect(const Maybe<gfx::IntRect>& aDirtyRect);
+
+ Maybe<gfx::IntRect> TakeDirtyRect() { return std::move(mDirtyRect); }
+
+ RefPtr<RenderRootStateManager> mManager;
+ Maybe<gfx::IntRect> mDirtyRect;
+ wr::ImageKey mImageKey;
+ };
+
+ private:
+ SharedSurfacesChild() = delete;
+ ~SharedSurfacesChild() = delete;
+
+ friend class SharedSurfacesAnimation;
+
+ class SharedUserData final : public Runnable {
+ public:
+ explicit SharedUserData(const wr::ExternalImageId& aId);
+ virtual ~SharedUserData();
+
+ SharedUserData(const SharedUserData& aOther) = delete;
+ SharedUserData& operator=(const SharedUserData& aOther) = delete;
+
+ SharedUserData(SharedUserData&& aOther) = delete;
+ SharedUserData& operator=(SharedUserData&& aOther) = delete;
+
+ static void Destroy(void* aClosure);
+
+ NS_IMETHOD Run() override;
+
+ const wr::ExternalImageId& Id() const { return mId; }
+
+ void SetId(const wr::ExternalImageId& aId) {
+ mId = aId;
+ mKeys.Clear();
+ mShared = false;
+ }
+
+ bool IsShared() const { return mShared; }
+
+ void MarkShared() {
+ MOZ_ASSERT(!mShared);
+ mShared = true;
+ }
+
+ wr::ImageKey UpdateKey(RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ const Maybe<gfx::IntRect>& aDirtyRect);
+
+ protected:
+ AutoTArray<ImageKeyData, 1> mKeys;
+ wr::ExternalImageId mId;
+ bool mShared : 1;
+ };
+
+ static nsresult ShareInternal(gfx::SourceSurfaceSharedData* aSurface,
+ SharedUserData** aUserData);
+
+ static void Unshare(const wr::ExternalImageId& aId, bool aReleaseId,
+ nsTArray<ImageKeyData>& aKeys);
+
+ static void DestroySharedUserData(void* aClosure);
+
+ static gfx::UserDataKey sSharedKey;
+};
+
+class AnimationImageKeyData final : public SharedSurfacesChild::ImageKeyData {
+ public:
+ AnimationImageKeyData(RenderRootStateManager* aManager,
+ const wr::ImageKey& aImageKey);
+
+ virtual ~AnimationImageKeyData();
+
+ AnimationImageKeyData(AnimationImageKeyData&& aOther);
+ AnimationImageKeyData& operator=(AnimationImageKeyData&& aOther);
+
+ AutoTArray<RefPtr<gfx::SourceSurfaceSharedData>, 2> mPendingRelease;
+};
+
+/**
+ * This helper class owns a single ImageKey which will map to different external
+ * image IDs representing different frames in an animation.
+ */
+class SharedSurfacesAnimation final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedSurfacesAnimation)
+
+ SharedSurfacesAnimation() = default;
+
+ void Destroy();
+
+ /**
+ * Set the animation to display the given frame.
+ * @param aSurface The current frame.
+ * @param aDirtyRect Dirty rect representing the change between the new frame
+ * and the previous frame. We will request only the delta
+ * be reuploaded by WebRender.
+ */
+ nsresult SetCurrentFrame(gfx::SourceSurfaceSharedData* aSurface,
+ const gfx::IntRect& aDirtyRect);
+
+ /**
+ * Generate an ImageKey for the given frame.
+ * @param aSurface The current frame. This should match what was cached via
+ * SetCurrentFrame, but if it does not, it will need to
+ * regenerate the cached ImageKey.
+ */
+ nsresult UpdateKey(gfx::SourceSurfaceSharedData* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey);
+
+ /**
+ * Release our reference to all frames up to and including the frame which
+ * has an external image ID which matches aId.
+ */
+ void ReleasePreviousFrame(RenderRootStateManager* aManager,
+ const wr::ExternalImageId& aId);
+
+ /**
+ * Destroy any state information bound for the given layer manager. Any
+ * image keys are already invalid.
+ */
+ void Invalidate(RenderRootStateManager* aManager);
+
+ private:
+ ~SharedSurfacesAnimation();
+
+ void HoldSurfaceForRecycling(AnimationImageKeyData& aEntry,
+ gfx::SourceSurfaceSharedData* aSurface);
+
+ AutoTArray<AnimationImageKeyData, 1> mKeys;
+ wr::ExternalImageId mId;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/SharedSurfacesMemoryReport.h b/gfx/layers/ipc/SharedSurfacesMemoryReport.h
new file mode 100644
index 0000000000..57d9edddb7
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesMemoryReport.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SHAREDSURFACESMEMORYREPORT_H
+#define MOZILLA_GFX_SHAREDSURFACESMEMORYREPORT_H
+
+#include <cstdint> // for uint32_t
+#include <unordered_map>
+#include "base/process.h"
+#include "ipc/IPCMessageUtils.h"
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+
+namespace mozilla {
+namespace layers {
+
+class SharedSurfacesMemoryReport final {
+ public:
+ class SurfaceEntry final {
+ public:
+ base::ProcessId mCreatorPid;
+ gfx::IntSize mSize;
+ int32_t mStride;
+ uint32_t mConsumers;
+ bool mCreatorRef;
+ };
+
+ std::unordered_map<uint64_t, SurfaceEntry> mSurfaces;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::SharedSurfacesMemoryReport> {
+ typedef mozilla::layers::SharedSurfacesMemoryReport paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mSurfaces);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mSurfaces);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::SharedSurfacesMemoryReport::SurfaceEntry>
+ : public PlainOldDataSerializer<
+ mozilla::layers::SharedSurfacesMemoryReport::SurfaceEntry> {};
+
+} // namespace IPC
+
+#endif
diff --git a/gfx/layers/ipc/SharedSurfacesParent.cpp b/gfx/layers/ipc/SharedSurfacesParent.cpp
new file mode 100644
index 0000000000..844e3d83b3
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesParent.cpp
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurfacesParent.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/layers/SharedSurfacesMemoryReport.h"
+#include "mozilla/layers/SourceSurfaceSharedData.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/webrender/RenderSharedSurfaceTextureHost.h"
+#include "mozilla/webrender/RenderSharedSurfaceTextureHostSWGL.h"
+#include "mozilla/webrender/RenderThread.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+StaticMutex SharedSurfacesParent::sMutex;
+StaticAutoPtr<SharedSurfacesParent> SharedSurfacesParent::sInstance;
+
+SharedSurfacesParent::SharedSurfacesParent() = default;
+
+SharedSurfacesParent::~SharedSurfacesParent() {
+ for (auto i = mSurfaces.Iter(); !i.Done(); i.Next()) {
+ // There may be lingering consumers of the surfaces that didn't get shutdown
+ // yet but since we are here, we know the render thread is finished and we
+ // can unregister everything.
+ wr::RenderThread::Get()->UnregisterExternalImageDuringShutdown(i.Key());
+ }
+}
+
+/* static */
+void SharedSurfacesParent::Initialize() {
+ MOZ_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ sInstance = new SharedSurfacesParent();
+ }
+}
+
+/* static */
+void SharedSurfacesParent::Shutdown() {
+ // The main thread should blocked on waiting for the render thread to
+ // complete so this should be safe to release off the main thread.
+ MOZ_ASSERT(wr::RenderThread::IsInRenderThread());
+ StaticMutexAutoLock lock(sMutex);
+ sInstance = nullptr;
+}
+
+/* static */
+already_AddRefed<DataSourceSurface> SharedSurfacesParent::Get(
+ const wr::ExternalImageId& aId) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd";
+ return nullptr;
+ }
+
+ RefPtr<SourceSurfaceSharedDataWrapper> surface;
+ sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface));
+ return surface.forget();
+}
+
+/* static */
+already_AddRefed<DataSourceSurface> SharedSurfacesParent::Acquire(
+ const wr::ExternalImageId& aId) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ gfxCriticalNote << "SSP:Acq " << wr::AsUint64(aId) << " shtd";
+ return nullptr;
+ }
+
+ RefPtr<SourceSurfaceSharedDataWrapper> surface;
+ sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface));
+
+ if (surface) {
+ DebugOnly<bool> rv = surface->AddConsumer();
+ MOZ_ASSERT(!rv);
+ }
+ return surface.forget();
+}
+
+/* static */
+bool SharedSurfacesParent::Release(const wr::ExternalImageId& aId,
+ bool aForCreator) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return false;
+ }
+
+ uint64_t id = wr::AsUint64(aId);
+ RefPtr<SourceSurfaceSharedDataWrapper> surface;
+ sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface));
+ if (!surface) {
+ return false;
+ }
+
+ if (surface->RemoveConsumer(aForCreator)) {
+ wr::RenderThread::Get()->UnregisterExternalImage(id);
+ sInstance->mSurfaces.Remove(id);
+ }
+
+ return true;
+}
+
+/* static */
+void SharedSurfacesParent::AddSameProcess(const wr::ExternalImageId& aId,
+ SourceSurfaceSharedData* aSurface) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ gfxCriticalNote << "SSP:Ads " << wr::AsUint64(aId) << " shtd";
+ return;
+ }
+
+ // If the child bridge detects it is in the combined UI/GPU process, then it
+ // will insert a wrapper surface holding the shared memory buffer directly.
+ // This is good because we avoid mapping the same shared memory twice, but
+ // still allow the original surface to be freed and remove the wrapper from
+ // the table when it is no longer needed.
+ RefPtr<SourceSurfaceSharedDataWrapper> surface =
+ new SourceSurfaceSharedDataWrapper();
+ surface->Init(aSurface);
+
+ uint64_t id = wr::AsUint64(aId);
+ MOZ_ASSERT(!sInstance->mSurfaces.Contains(id));
+
+ RefPtr<wr::RenderTextureHost> texture;
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ texture = new wr::RenderSharedSurfaceTextureHostSWGL(surface);
+ } else {
+ texture = new wr::RenderSharedSurfaceTextureHost(surface);
+ }
+ wr::RenderThread::Get()->RegisterExternalImage(id, texture.forget());
+
+ surface->AddConsumer();
+ sInstance->mSurfaces.Put(id, std::move(surface));
+}
+
+/* static */
+void SharedSurfacesParent::DestroyProcess(base::ProcessId aPid) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return;
+ }
+
+ // Note that the destruction of a parent may not be cheap if it still has a
+ // lot of surfaces still bound that require unmapping.
+ for (auto i = sInstance->mSurfaces.Iter(); !i.Done(); i.Next()) {
+ SourceSurfaceSharedDataWrapper* surface = i.Data();
+ if (surface->GetCreatorPid() == aPid && surface->HasCreatorRef() &&
+ surface->RemoveConsumer(/* aForCreator */ true)) {
+ wr::RenderThread::Get()->UnregisterExternalImage(i.Key());
+ i.Remove();
+ }
+ }
+}
+
+/* static */
+void SharedSurfacesParent::Add(const wr::ExternalImageId& aId,
+ const SurfaceDescriptorShared& aDesc,
+ base::ProcessId aPid) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(aPid != base::GetCurrentProcId());
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " shtd";
+ return;
+ }
+
+ // Note that the surface wrapper maps in the given handle as read only.
+ RefPtr<SourceSurfaceSharedDataWrapper> surface =
+ new SourceSurfaceSharedDataWrapper();
+ if (NS_WARN_IF(!surface->Init(aDesc.size(), aDesc.stride(), aDesc.format(),
+ aDesc.handle(), aPid))) {
+ gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " init";
+ return;
+ }
+
+ uint64_t id = wr::AsUint64(aId);
+ MOZ_ASSERT(!sInstance->mSurfaces.Contains(id));
+
+ RefPtr<wr::RenderTextureHost> texture;
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ texture = new wr::RenderSharedSurfaceTextureHostSWGL(surface);
+ } else {
+ texture = new wr::RenderSharedSurfaceTextureHost(surface);
+ }
+ wr::RenderThread::Get()->RegisterExternalImage(id, texture.forget());
+
+ surface->AddConsumer();
+ sInstance->mSurfaces.Put(id, std::move(surface));
+}
+
+/* static */
+void SharedSurfacesParent::Remove(const wr::ExternalImageId& aId) {
+ DebugOnly<bool> rv = Release(aId, /* aForCreator */ true);
+ MOZ_ASSERT(rv);
+}
+
+/* static */
+void SharedSurfacesParent::AccumulateMemoryReport(
+ base::ProcessId aPid, SharedSurfacesMemoryReport& aReport) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return;
+ }
+
+ for (auto i = sInstance->mSurfaces.ConstIter(); !i.Done(); i.Next()) {
+ SourceSurfaceSharedDataWrapper* surface = i.Data();
+ if (surface->GetCreatorPid() == aPid) {
+ aReport.mSurfaces.insert(std::make_pair(
+ i.Key(), SharedSurfacesMemoryReport::SurfaceEntry{
+ aPid, surface->GetSize(), surface->Stride(),
+ surface->GetConsumers(), surface->HasCreatorRef()}));
+ }
+ }
+}
+
+/* static */
+bool SharedSurfacesParent::AccumulateMemoryReport(
+ SharedSurfacesMemoryReport& aReport) {
+ if (XRE_IsParentProcess()) {
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+ if (!gpm || gpm->GPUProcessPid() != -1) {
+ return false;
+ }
+ } else if (!XRE_IsGPUProcess()) {
+ return false;
+ }
+
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return true;
+ }
+
+ for (auto i = sInstance->mSurfaces.ConstIter(); !i.Done(); i.Next()) {
+ SourceSurfaceSharedDataWrapper* surface = i.Data();
+ aReport.mSurfaces.insert(std::make_pair(
+ i.Key(),
+ SharedSurfacesMemoryReport::SurfaceEntry{
+ surface->GetCreatorPid(), surface->GetSize(), surface->Stride(),
+ surface->GetConsumers(), surface->HasCreatorRef()}));
+ }
+
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/SharedSurfacesParent.h b/gfx/layers/ipc/SharedSurfacesParent.h
new file mode 100644
index 0000000000..a990c1b19d
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesParent.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SHAREDSURFACESPARENT_H
+#define MOZILLA_GFX_SHAREDSURFACESPARENT_H
+
+#include <stdint.h> // for uint32_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/StaticMutex.h" // for StaticMutex
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/gfx/2D.h" // for SurfaceFormat
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptorShared
+#include "mozilla/webrender/WebRenderTypes.h" // for wr::ExternalImageId
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+class SourceSurfaceSharedData;
+class SourceSurfaceSharedDataWrapper;
+} // namespace gfx
+
+namespace layers {
+
+class SharedSurfacesChild;
+class SharedSurfacesMemoryReport;
+
+class SharedSurfacesParent final {
+ public:
+ static void Initialize();
+ static void Shutdown();
+
+ // Get without increasing the consumer count.
+ static already_AddRefed<gfx::DataSourceSurface> Get(
+ const wr::ExternalImageId& aId);
+
+ // Get but also increase the consumer count. Must call Release after finished.
+ static already_AddRefed<gfx::DataSourceSurface> Acquire(
+ const wr::ExternalImageId& aId);
+
+ static bool Release(const wr::ExternalImageId& aId, bool aForCreator = false);
+
+ static void Add(const wr::ExternalImageId& aId,
+ const SurfaceDescriptorShared& aDesc, base::ProcessId aPid);
+
+ static void Remove(const wr::ExternalImageId& aId);
+
+ static void DestroyProcess(base::ProcessId aPid);
+
+ static void AccumulateMemoryReport(base::ProcessId aPid,
+ SharedSurfacesMemoryReport& aReport);
+
+ static bool AccumulateMemoryReport(SharedSurfacesMemoryReport& aReport);
+
+ ~SharedSurfacesParent();
+
+ private:
+ friend class SharedSurfacesChild;
+
+ SharedSurfacesParent();
+
+ static void AddSameProcess(const wr::ExternalImageId& aId,
+ gfx::SourceSurfaceSharedData* aSurface);
+
+ static StaticMutex sMutex;
+
+ static StaticAutoPtr<SharedSurfacesParent> sInstance;
+
+ nsRefPtrHashtable<nsUint64HashKey, gfx::SourceSurfaceSharedDataWrapper>
+ mSurfaces;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/SurfaceDescriptor.h b/gfx/layers/ipc/SurfaceDescriptor.h
new file mode 100644
index 0000000000..17ee00c6ae
--- /dev/null
+++ b/gfx/layers/ipc/SurfaceDescriptor.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_SurfaceDescriptor_h
+#define IPC_SurfaceDescriptor_h
+
+#if defined(XP_MACOSX)
+# define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+#endif
+
+#if defined(MOZ_X11)
+# include "mozilla/AlreadyAddRefed.h"
+# include "mozilla/gfx/Point.h"
+
+# define MOZ_HAVE_SURFACEDESCRIPTORX11
+# define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+
+typedef unsigned long XID;
+typedef XID Drawable;
+
+class gfxXlibSurface;
+
+namespace mozilla {
+namespace layers {
+
+struct SurfaceDescriptorX11 {
+ SurfaceDescriptorX11() = default;
+
+ explicit SurfaceDescriptorX11(gfxXlibSurface* aSurf,
+ bool aForwardGLX = false);
+
+ SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID,
+ const gfx::IntSize& aSize);
+
+ // Default copy ctor and operator= are OK
+
+ bool operator==(const SurfaceDescriptorX11& aOther) const {
+ // Define == as two descriptors having the same XID for now,
+ // ignoring size and render format. If the two indeed refer to
+ // the same valid XID, then size/format are "actually" the same
+ // anyway, regardless of the values of the fields in
+ // SurfaceDescriptorX11.
+ return mId == aOther.mId;
+ }
+
+ already_AddRefed<gfxXlibSurface> OpenForeign() const;
+
+ MOZ_INIT_OUTSIDE_CTOR Drawable mId;
+ MOZ_INIT_OUTSIDE_CTOR XID mFormat; // either a PictFormat or VisualID
+ MOZ_INIT_OUTSIDE_CTOR gfx::IntSize mSize;
+ MOZ_INIT_OUTSIDE_CTOR Drawable
+ mGLXPixmap; // used to prevent multiple bindings to the same GLXPixmap
+ // in-process
+};
+
+} // namespace layers
+} // namespace mozilla
+#else
+namespace mozilla {
+namespace layers {
+struct SurfaceDescriptorX11 {
+ bool operator==(const SurfaceDescriptorX11&) const { return false; }
+};
+} // namespace layers
+} // namespace mozilla
+#endif
+
+#endif // IPC_SurfaceDescriptor_h
diff --git a/gfx/layers/ipc/SynchronousTask.h b/gfx/layers/ipc/SynchronousTask.h
new file mode 100644
index 0000000000..fc6acda81b
--- /dev/null
+++ b/gfx/layers/ipc/SynchronousTask.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SYNCHRONOUSTASK_H
+#define MOZILLA_GFX_SYNCHRONOUSTASK_H
+
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+
+namespace mozilla {
+namespace layers {
+
+// Helper that creates a monitor and a "done" flag, then enters the monitor.
+class MOZ_STACK_CLASS SynchronousTask {
+ friend class AutoCompleteTask;
+
+ public:
+ explicit SynchronousTask(const char* name)
+ : mMonitor(name), mAutoEnter(mMonitor), mDone(false) {}
+
+ void Wait() {
+ while (!mDone) {
+ mMonitor.Wait();
+ }
+ }
+
+ private:
+ void Complete() {
+ mDone = true;
+ mMonitor.NotifyAll();
+ }
+
+ private:
+ ReentrantMonitor mMonitor;
+ ReentrantMonitorAutoEnter mAutoEnter;
+ bool mDone;
+};
+
+class MOZ_STACK_CLASS AutoCompleteTask final {
+ public:
+ explicit AutoCompleteTask(SynchronousTask* aTask)
+ : mTask(aTask), mAutoEnter(aTask->mMonitor) {}
+ ~AutoCompleteTask() { mTask->Complete(); }
+
+ private:
+ SynchronousTask* mTask;
+ ReentrantMonitorAutoEnter mAutoEnter;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/TextureForwarder.h b/gfx/layers/ipc/TextureForwarder.h
new file mode 100644
index 0000000000..bbd9b72153
--- /dev/null
+++ b/gfx/layers/ipc/TextureForwarder.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_TEXTUREFORWARDER
+#define MOZILLA_LAYERS_TEXTUREFORWARDER
+
+#include <stdint.h> // for int32_t, uint64_t
+#include "gfxTypes.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/LayersMessages.h" // for Edit, etc
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/KnowsCompositor.h"
+#include "nsISerialEventTarget.h"
+
+namespace mozilla {
+namespace layers {
+class CanvasChild;
+
+/**
+ * An abstract interface for classes that implement the autogenerated
+ * IPDL actor class. Lets us check if they are still valid for IPC.
+ */
+class LayersIPCActor {
+ public:
+ virtual bool IPCOpen() const { return true; }
+};
+
+/**
+ * An abstract interface for LayersIPCActors that implement a top-level
+ * IPDL protocol so also have their own channel.
+ * Has their own MessageLoop for message dispatch, and can allocate
+ * shmem.
+ */
+class LayersIPCChannel : public LayersIPCActor,
+ public mozilla::ipc::IShmemAllocator {
+ public:
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ virtual bool IsSameProcess() const = 0;
+
+ virtual bool UsesImageBridge() const { return false; }
+
+ virtual base::ProcessId GetParentPid() const = 0;
+
+ virtual nsISerialEventTarget* GetThread() const = 0;
+
+ virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() {
+ return nullptr;
+ }
+
+ virtual void CancelWaitForNotifyNotUsed(uint64_t aTextureId) = 0;
+
+ virtual wr::MaybeExternalImageId GetNextExternalImageId() {
+ return Nothing();
+ }
+
+ protected:
+ virtual ~LayersIPCChannel() = default;
+};
+
+/**
+ * An abstract interface for classes that can allocate PTexture objects
+ * across IPDL. Currently a sub-class of LayersIPCChannel for simplicity
+ * since all our implementations use both, but could be independant if needed.
+ */
+class TextureForwarder : public LayersIPCChannel {
+ public:
+ /**
+ * Create a TextureChild/Parent pair as as well as the TextureHost on the
+ * parent side.
+ */
+ virtual PTextureChild* CreateTexture(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId,
+ nsISerialEventTarget* aTarget = nullptr) = 0;
+
+ /**
+ * Returns the CanvasChild for this TextureForwarder.
+ */
+ virtual already_AddRefed<CanvasChild> GetCanvasChild() { return nullptr; };
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/UiCompositorControllerChild.cpp b/gfx/layers/ipc/UiCompositorControllerChild.cpp
new file mode 100644
index 0000000000..fc12c61641
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/UiCompositorControllerChild.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/UiCompositorControllerMessageTypes.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/StaticPtr.h"
+#include "nsBaseWidget.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+#if defined(MOZ_WIDGET_ANDROID)
+# include "mozilla/widget/AndroidUiThread.h"
+
+static RefPtr<nsThread> GetUiThread() { return mozilla::GetAndroidUiThread(); }
+#else
+static RefPtr<nsThread> GetUiThread() {
+ MOZ_CRASH("Platform does not support UiCompositorController");
+ return nullptr;
+}
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+static bool IsOnUiThread() { return GetUiThread()->IsOnCurrentThread(); }
+
+namespace mozilla {
+namespace layers {
+
+// public:
+/* static */
+RefPtr<UiCompositorControllerChild>
+UiCompositorControllerChild::CreateForSameProcess(
+ const LayersId& aRootLayerTreeId) {
+ RefPtr<UiCompositorControllerChild> child =
+ new UiCompositorControllerChild(0);
+ child->mParent = new UiCompositorControllerParent(aRootLayerTreeId);
+ GetUiThread()->Dispatch(
+ NewRunnableMethod(
+ "layers::UiCompositorControllerChild::OpenForSameProcess", child,
+ &UiCompositorControllerChild::OpenForSameProcess),
+ nsIThread::DISPATCH_NORMAL);
+ return child;
+}
+
+/* static */
+RefPtr<UiCompositorControllerChild>
+UiCompositorControllerChild::CreateForGPUProcess(
+ const uint64_t& aProcessToken,
+ Endpoint<PUiCompositorControllerChild>&& aEndpoint) {
+ RefPtr<UiCompositorControllerChild> child =
+ new UiCompositorControllerChild(aProcessToken);
+
+ RefPtr<nsIRunnable> task =
+ NewRunnableMethod<Endpoint<PUiCompositorControllerChild>&&>(
+ "layers::UiCompositorControllerChild::OpenForGPUProcess", child,
+ &UiCompositorControllerChild::OpenForGPUProcess,
+ std::move(aEndpoint));
+
+ GetUiThread()->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL);
+ return child;
+}
+
+bool UiCompositorControllerChild::Pause() {
+ if (!mIsOpen) {
+ return false;
+ }
+ return SendPause();
+}
+
+bool UiCompositorControllerChild::Resume() {
+ if (!mIsOpen) {
+ return false;
+ }
+ return SendResume();
+}
+
+bool UiCompositorControllerChild::ResumeAndResize(const int32_t& aX,
+ const int32_t& aY,
+ const int32_t& aWidth,
+ const int32_t& aHeight) {
+ if (!mIsOpen) {
+ mResize = Some(gfx::IntRect(aX, aY, aWidth, aHeight));
+ // Since we are caching these values, pretend the call succeeded.
+ return true;
+ }
+ return SendResumeAndResize(aX, aY, aWidth, aHeight);
+}
+
+bool UiCompositorControllerChild::InvalidateAndRender() {
+ if (!mIsOpen) {
+ return false;
+ }
+ return SendInvalidateAndRender();
+}
+
+bool UiCompositorControllerChild::SetMaxToolbarHeight(const int32_t& aHeight) {
+ if (!mIsOpen) {
+ mMaxToolbarHeight = Some(aHeight);
+ // Since we are caching this value, pretend the call succeeded.
+ return true;
+ }
+ return SendMaxToolbarHeight(aHeight);
+}
+
+bool UiCompositorControllerChild::SetFixedBottomOffset(int32_t aOffset) {
+ return SendFixedBottomOffset(aOffset);
+}
+
+bool UiCompositorControllerChild::ToolbarAnimatorMessageFromUI(
+ const int32_t& aMessage) {
+ if (!mIsOpen) {
+ return false;
+ }
+
+ if (aMessage == IS_COMPOSITOR_CONTROLLER_OPEN) {
+ RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN);
+ }
+
+ return true;
+}
+
+bool UiCompositorControllerChild::SetDefaultClearColor(const uint32_t& aColor) {
+ if (!mIsOpen) {
+ mDefaultClearColor = Some(aColor);
+ // Since we are caching this value, pretend the call succeeded.
+ return true;
+ }
+
+ return SendDefaultClearColor(aColor);
+}
+
+bool UiCompositorControllerChild::RequestScreenPixels() {
+ if (!mIsOpen) {
+ return false;
+ }
+
+ return SendRequestScreenPixels();
+}
+
+bool UiCompositorControllerChild::EnableLayerUpdateNotifications(
+ const bool& aEnable) {
+ if (!mIsOpen) {
+ mLayerUpdateEnabled = Some(aEnable);
+ // Since we are caching this value, pretend the call succeeded.
+ return true;
+ }
+
+ return SendEnableLayerUpdateNotifications(aEnable);
+}
+
+void UiCompositorControllerChild::Destroy() {
+ if (!IsOnUiThread()) {
+ GetUiThread()->Dispatch(
+ NewRunnableMethod("layers::UiCompositorControllerChild::Destroy", this,
+ &UiCompositorControllerChild::Destroy),
+ nsIThread::DISPATCH_SYNC);
+ return;
+ }
+
+ if (mWidget) {
+ // Dispatch mWidget to main thread to prevent it from being destructed by
+ // the ui thread.
+ RefPtr<nsIWidget> widget = std::move(mWidget);
+ NS_ReleaseOnMainThread("UiCompositorControllerChild::mWidget",
+ widget.forget());
+ }
+
+ if (mIsOpen) {
+ // Close the underlying IPC channel.
+ PUiCompositorControllerChild::Close();
+ mIsOpen = false;
+ }
+}
+
+void UiCompositorControllerChild::SetBaseWidget(nsBaseWidget* aWidget) {
+ mWidget = aWidget;
+}
+
+bool UiCompositorControllerChild::DeallocPixelBuffer(Shmem& aMem) {
+ return DeallocShmem(aMem);
+}
+
+// protected:
+void UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mIsOpen = false;
+ mParent = nullptr;
+
+ if (mProcessToken) {
+ gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+ mProcessToken = 0;
+ }
+}
+
+void UiCompositorControllerChild::ActorDealloc() {
+ if (mParent) {
+ mParent = nullptr;
+ }
+ Release();
+}
+
+void UiCompositorControllerChild::ProcessingError(Result aCode,
+ const char* aReason) {
+ if (aCode != MsgDropped) {
+ gfxDevCrash(gfx::LogReason::ProcessingError)
+ << "Processing error in UiCompositorControllerChild: " << int(aCode);
+ }
+}
+
+void UiCompositorControllerChild::HandleFatalError(const char* aMsg) const {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerChild::RecvToolbarAnimatorMessageFromCompositor(
+ const int32_t& aMessage) {
+#if defined(MOZ_WIDGET_ANDROID)
+ if (mWidget) {
+ mWidget->RecvToolbarAnimatorMessageFromCompositor(aMessage);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerChild::RecvRootFrameMetrics(
+ const ScreenPoint& aScrollOffset, const CSSToScreenScale& aZoom) {
+#if defined(MOZ_WIDGET_ANDROID)
+ if (mWidget) {
+ mWidget->UpdateRootFrameMetrics(aScrollOffset, aZoom);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerChild::RecvScreenPixels(
+ ipc::Shmem&& aMem, const ScreenIntSize& aSize, bool aNeedsYFlip) {
+#if defined(MOZ_WIDGET_ANDROID)
+ if (mWidget) {
+ mWidget->RecvScreenPixels(std::move(aMem), aSize, aNeedsYFlip);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+// private:
+UiCompositorControllerChild::UiCompositorControllerChild(
+ const uint64_t& aProcessToken)
+ : mIsOpen(false), mProcessToken(aProcessToken), mWidget(nullptr) {}
+
+UiCompositorControllerChild::~UiCompositorControllerChild() = default;
+
+void UiCompositorControllerChild::OpenForSameProcess() {
+ MOZ_ASSERT(IsOnUiThread());
+
+ mIsOpen = Open(mParent->GetIPCChannel(), mozilla::layers::CompositorThread(),
+ mozilla::ipc::ChildSide);
+
+ if (!mIsOpen) {
+ mParent = nullptr;
+ return;
+ }
+
+ mParent->InitializeForSameProcess();
+ AddRef();
+ SendCachedValues();
+ // Let Ui thread know the connection is open;
+ RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN);
+}
+
+void UiCompositorControllerChild::OpenForGPUProcess(
+ Endpoint<PUiCompositorControllerChild>&& aEndpoint) {
+ MOZ_ASSERT(IsOnUiThread());
+
+ mIsOpen = aEndpoint.Bind(this);
+
+ if (!mIsOpen) {
+ // The GPU Process Manager might be gone if we receive ActorDestroy very
+ // late in shutdown.
+ if (gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get()) {
+ gpm->NotifyRemoteActorDestroyed(mProcessToken);
+ }
+ return;
+ }
+
+ AddRef();
+ SendCachedValues();
+ // Let Ui thread know the connection is open;
+ RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN);
+}
+
+void UiCompositorControllerChild::SendCachedValues() {
+ MOZ_ASSERT(mIsOpen);
+ if (mResize) {
+ SendResumeAndResize(mResize.ref().x, mResize.ref().y, mResize.ref().width,
+ mResize.ref().height);
+ mResize.reset();
+ }
+ if (mMaxToolbarHeight) {
+ SendMaxToolbarHeight(mMaxToolbarHeight.ref());
+ mMaxToolbarHeight.reset();
+ }
+ if (mDefaultClearColor) {
+ SendDefaultClearColor(mDefaultClearColor.ref());
+ mDefaultClearColor.reset();
+ }
+ if (mLayerUpdateEnabled) {
+ SendEnableLayerUpdateNotifications(mLayerUpdateEnabled.ref());
+ mLayerUpdateEnabled.reset();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/UiCompositorControllerChild.h b/gfx/layers/ipc/UiCompositorControllerChild.h
new file mode 100644
index 0000000000..fb822eaddf
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerChild.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef include_gfx_ipc_UiCompositorControllerChild_h
+#define include_gfx_ipc_UiCompositorControllerChild_h
+
+#include "mozilla/layers/PUiCompositorControllerChild.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/RefPtr.h"
+#include "nsThread.h"
+
+class nsBaseWidget;
+
+namespace mozilla {
+namespace layers {
+
+class UiCompositorControllerChild final
+ : protected PUiCompositorControllerChild {
+ friend class PUiCompositorControllerChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerChild)
+
+ static RefPtr<UiCompositorControllerChild> CreateForSameProcess(
+ const LayersId& aRootLayerTreeId);
+ static RefPtr<UiCompositorControllerChild> CreateForGPUProcess(
+ const uint64_t& aProcessToken,
+ Endpoint<PUiCompositorControllerChild>&& aEndpoint);
+
+ bool Pause();
+ bool Resume();
+ bool ResumeAndResize(const int32_t& aX, const int32_t& aY,
+ const int32_t& aHeight, const int32_t& aWidth);
+ bool InvalidateAndRender();
+ bool SetMaxToolbarHeight(const int32_t& aHeight);
+ bool SetFixedBottomOffset(int32_t aOffset);
+ bool ToolbarAnimatorMessageFromUI(const int32_t& aMessage);
+ bool SetDefaultClearColor(const uint32_t& aColor);
+ bool RequestScreenPixels();
+ bool EnableLayerUpdateNotifications(const bool& aEnable);
+
+ void Destroy();
+
+ void SetBaseWidget(nsBaseWidget* aWidget);
+ bool DeallocPixelBuffer(Shmem& aMem);
+
+ protected:
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void ActorDealloc() override;
+ void ProcessingError(Result aCode, const char* aReason) override;
+ void HandleFatalError(const char* aMsg) const override;
+ mozilla::ipc::IPCResult RecvToolbarAnimatorMessageFromCompositor(
+ const int32_t& aMessage);
+ mozilla::ipc::IPCResult RecvRootFrameMetrics(const ScreenPoint& aScrollOffset,
+ const CSSToScreenScale& aZoom);
+ mozilla::ipc::IPCResult RecvScreenPixels(Shmem&& aMem,
+ const ScreenIntSize& aSize,
+ bool aNeedsYFlip);
+
+ private:
+ explicit UiCompositorControllerChild(const uint64_t& aProcessToken);
+ virtual ~UiCompositorControllerChild();
+ void OpenForSameProcess();
+ void OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint);
+ void SendCachedValues();
+
+ bool mIsOpen;
+ uint64_t mProcessToken;
+ Maybe<gfx::IntRect> mResize;
+ Maybe<int32_t> mMaxToolbarHeight;
+ Maybe<uint32_t> mDefaultClearColor;
+ Maybe<bool> mLayerUpdateEnabled;
+ RefPtr<nsBaseWidget> mWidget;
+ // Should only be set when compositor is in process.
+ RefPtr<UiCompositorControllerParent> mParent;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_gfx_ipc_UiCompositorControllerChild_h
diff --git a/gfx/layers/ipc/UiCompositorControllerMessageTypes.h b/gfx/layers/ipc/UiCompositorControllerMessageTypes.h
new file mode 100644
index 0000000000..3e68e7be31
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerMessageTypes.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef include_gfx_ipc_UiCompositorControllerMessageTypes_h
+#define include_gfx_ipc_UiCompositorControllerMessageTypes_h
+
+namespace mozilla {
+namespace layers {
+
+//
+// NOTE: These values are also defined in
+// mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+// and must be kept in sync. Any new message added here must also be added
+// there.
+//
+
+// clang-format off
+enum UiCompositorControllerMessageTypes {
+ FIRST_PAINT = 0, // Sent from compositor after first paint
+ LAYERS_UPDATED = 1, // Sent from the compositor when any layer has been updated
+ COMPOSITOR_CONTROLLER_OPEN = 2, // Compositor controller IPC is open
+ IS_COMPOSITOR_CONTROLLER_OPEN = 3 // Special message sent from controller to query if the compositor controller is open
+};
+// clang-format on
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_gfx_ipc_UiCompositorControllerMessageTypes_h
diff --git a/gfx/layers/ipc/UiCompositorControllerParent.cpp b/gfx/layers/ipc/UiCompositorControllerParent.cpp
new file mode 100644
index 0000000000..9cf60df6e1
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp
@@ -0,0 +1,305 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "UiCompositorControllerParent.h"
+
+#if defined(MOZ_WIDGET_ANDROID)
+# include "apz/src/APZCTreeManager.h"
+# include "mozilla/layers/AsyncCompositionManager.h"
+#endif
+#include <utility>
+
+#include "FrameMetrics.h"
+#include "SynchronousTask.h"
+#include "mozilla/Unused.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/UiCompositorControllerMessageTypes.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
+
+/* static */
+RefPtr<UiCompositorControllerParent>
+UiCompositorControllerParent::GetFromRootLayerTreeId(
+ const LayersId& aRootLayerTreeId) {
+ RefPtr<UiCompositorControllerParent> controller;
+ CompositorBridgeParent::CallWithIndirectShadowTree(
+ aRootLayerTreeId, [&](LayerTreeState& aState) -> void {
+ controller = aState.mUiControllerParent;
+ });
+ return controller;
+}
+
+/* static */
+RefPtr<UiCompositorControllerParent> UiCompositorControllerParent::Start(
+ const LayersId& aRootLayerTreeId,
+ Endpoint<PUiCompositorControllerParent>&& aEndpoint) {
+ RefPtr<UiCompositorControllerParent> parent =
+ new UiCompositorControllerParent(aRootLayerTreeId);
+
+ RefPtr<Runnable> task =
+ NewRunnableMethod<Endpoint<PUiCompositorControllerParent>&&>(
+ "layers::UiCompositorControllerParent::Open", parent,
+ &UiCompositorControllerParent::Open, std::move(aEndpoint));
+ CompositorThread()->Dispatch(task.forget());
+
+ return parent;
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvPause() {
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ parent->PauseComposition();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvResume() {
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ parent->ResumeComposition();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvResumeAndResize(
+ const int32_t& aX, const int32_t& aY, const int32_t& aWidth,
+ const int32_t& aHeight) {
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ // Front-end expects a first paint callback upon resume/resize.
+ parent->ForceIsFirstPaint();
+ parent->ResumeCompositionAndResize(aX, aY, aWidth, aHeight);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvInvalidateAndRender() {
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ parent->Invalidate();
+ parent->ScheduleComposition();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvMaxToolbarHeight(
+ const int32_t& aHeight) {
+ mMaxToolbarHeight = aHeight;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvFixedBottomOffset(
+ const int32_t& aOffset) {
+#if defined(MOZ_WIDGET_ANDROID)
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ parent->SetFixedLayerMargins(0, aOffset);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvDefaultClearColor(
+ const uint32_t& aColor) {
+ LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+
+ if (state && state->mLayerManager) {
+ Compositor* compositor = state->mLayerManager->GetCompositor();
+ if (compositor) {
+ // Android Color is ARGB which is apparently unusual.
+ compositor->SetDefaultClearColor(
+ gfx::DeviceColor::UnusualFromARGB(aColor));
+ }
+ } else if (state && state->mWrBridge) {
+ state->mWrBridge->SetClearColor(gfx::DeviceColor::UnusualFromARGB(aColor));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvRequestScreenPixels() {
+#if defined(MOZ_WIDGET_ANDROID)
+ LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+
+ if (state && state->mLayerManager && state->mParent) {
+ state->mLayerManager->RequestScreenPixels(this);
+ state->mParent->Invalidate();
+ state->mParent->ScheduleComposition();
+ } else if (state && state->mWrBridge) {
+ state->mWrBridge->RequestScreenPixels(this);
+ state->mWrBridge->ScheduleForcedGenerateFrame();
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvEnableLayerUpdateNotifications(
+ const bool& aEnable) {
+#if defined(MOZ_WIDGET_ANDROID)
+ // Layers updates are need by Robocop test which enables them
+ mCompositorLayersUpdateEnabled = aEnable;
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+void UiCompositorControllerParent::ActorDestroy(ActorDestroyReason aWhy) {}
+
+void UiCompositorControllerParent::ActorDealloc() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ Shutdown();
+ Release(); // For AddRef in Initialize()
+}
+
+void UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor(
+ int32_t aMessage) {
+ // This function can be call from ether compositor or controller thread.
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ CompositorThread()->Dispatch(NewRunnableMethod<int32_t>(
+ "layers::UiCompositorControllerParent::"
+ "ToolbarAnimatorMessageFromCompositor",
+ this,
+ &UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor,
+ aMessage));
+ return;
+ }
+
+ Unused << SendToolbarAnimatorMessageFromCompositor(aMessage);
+}
+
+bool UiCompositorControllerParent::AllocPixelBuffer(const int32_t aSize,
+ ipc::Shmem* aMem) {
+ MOZ_ASSERT(aSize > 0);
+ return AllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, aMem);
+}
+
+void UiCompositorControllerParent::NotifyLayersUpdated() {
+#ifdef MOZ_WIDGET_ANDROID
+ if (mCompositorLayersUpdateEnabled) {
+ ToolbarAnimatorMessageFromCompositor(LAYERS_UPDATED);
+ }
+#endif
+}
+
+void UiCompositorControllerParent::NotifyFirstPaint() {
+ ToolbarAnimatorMessageFromCompositor(FIRST_PAINT);
+}
+
+void UiCompositorControllerParent::NotifyUpdateScreenMetrics(
+ const GeckoViewMetrics& aMetrics) {
+#if defined(MOZ_WIDGET_ANDROID)
+ CSSToScreenScale scale = ViewTargetAs<ScreenPixel>(
+ aMetrics.mZoom.ToScaleFactor(),
+ PixelCastJustification::ScreenIsParentLayerForRoot);
+ ScreenPoint scrollOffset = aMetrics.mVisualScrollOffset * scale;
+ CompositorThread()->Dispatch(NewRunnableMethod<ScreenPoint, CSSToScreenScale>(
+ "UiCompositorControllerParent::SendRootFrameMetrics", this,
+ &UiCompositorControllerParent::SendRootFrameMetrics, scrollOffset,
+ scale));
+#endif
+}
+
+UiCompositorControllerParent::UiCompositorControllerParent(
+ const LayersId& aRootLayerTreeId)
+ : mRootLayerTreeId(aRootLayerTreeId)
+#ifdef MOZ_WIDGET_ANDROID
+ ,
+ mCompositorLayersUpdateEnabled(false)
+#endif
+ ,
+ mMaxToolbarHeight(0) {
+ MOZ_COUNT_CTOR(UiCompositorControllerParent);
+}
+
+UiCompositorControllerParent::~UiCompositorControllerParent() {
+ MOZ_COUNT_DTOR(UiCompositorControllerParent);
+}
+
+void UiCompositorControllerParent::InitializeForSameProcess() {
+ // This function is called by UiCompositorControllerChild in the main thread.
+ // So dispatch to the compositor thread to Initialize.
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ SynchronousTask task(
+ "UiCompositorControllerParent::InitializeForSameProcess");
+
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "UiCompositorControllerParent::InitializeForSameProcess", [&]() {
+ AutoCompleteTask complete(&task);
+ InitializeForSameProcess();
+ }));
+
+ task.Wait();
+ return;
+ }
+
+ Initialize();
+}
+
+void UiCompositorControllerParent::InitializeForOutOfProcess() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ Initialize();
+}
+
+void UiCompositorControllerParent::Initialize() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ AddRef();
+ LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+ MOZ_ASSERT(state);
+ MOZ_ASSERT(state->mParent);
+ if (!state || !state->mParent) {
+ return;
+ }
+ state->mUiControllerParent = this;
+}
+
+void UiCompositorControllerParent::Open(
+ Endpoint<PUiCompositorControllerParent>&& aEndpoint) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!aEndpoint.Bind(this)) {
+ // We can't recover from this.
+ MOZ_CRASH("Failed to bind UiCompositorControllerParent to endpoint");
+ }
+ InitializeForOutOfProcess();
+}
+
+void UiCompositorControllerParent::Shutdown() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+ if (state) {
+ state->mUiControllerParent = nullptr;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/UiCompositorControllerParent.h b/gfx/layers/ipc/UiCompositorControllerParent.h
new file mode 100644
index 0000000000..2093a92c10
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerParent.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef include_gfx_ipc_UiCompositorControllerParent_h
+#define include_gfx_ipc_UiCompositorControllerParent_h
+
+#include "mozilla/layers/PUiCompositorControllerParent.h"
+#include "mozilla/layers/APZUtils.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace layers {
+
+struct FrameMetrics;
+
+class UiCompositorControllerParent final
+ : public PUiCompositorControllerParent {
+ // UiCompositorControllerChild needs to call the private constructor when
+ // running in process.
+ friend class UiCompositorControllerChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerParent)
+
+ static RefPtr<UiCompositorControllerParent> GetFromRootLayerTreeId(
+ const LayersId& aRootLayerTreeId);
+ static RefPtr<UiCompositorControllerParent> Start(
+ const LayersId& aRootLayerTreeId,
+ Endpoint<PUiCompositorControllerParent>&& aEndpoint);
+
+ // PUiCompositorControllerParent functions
+ mozilla::ipc::IPCResult RecvPause();
+ mozilla::ipc::IPCResult RecvResume();
+ mozilla::ipc::IPCResult RecvResumeAndResize(const int32_t& aX,
+ const int32_t& aY,
+ const int32_t& aHeight,
+ const int32_t& aWidth);
+ mozilla::ipc::IPCResult RecvInvalidateAndRender();
+ mozilla::ipc::IPCResult RecvMaxToolbarHeight(const int32_t& aHeight);
+ mozilla::ipc::IPCResult RecvFixedBottomOffset(const int32_t& aOffset);
+ mozilla::ipc::IPCResult RecvDefaultClearColor(const uint32_t& aColor);
+ mozilla::ipc::IPCResult RecvRequestScreenPixels();
+ mozilla::ipc::IPCResult RecvEnableLayerUpdateNotifications(
+ const bool& aEnable);
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void ActorDealloc() override;
+
+ // Class specific functions
+ void ToolbarAnimatorMessageFromCompositor(int32_t aMessage);
+ bool AllocPixelBuffer(const int32_t aSize, Shmem* aMem);
+
+ // Called when a layer has been updated so the UI thread may be notified if
+ // necessary.
+ void NotifyLayersUpdated();
+ void NotifyFirstPaint();
+ void NotifyUpdateScreenMetrics(const GeckoViewMetrics& aMetrics);
+
+ private:
+ explicit UiCompositorControllerParent(const LayersId& aRootLayerTreeId);
+ virtual ~UiCompositorControllerParent();
+ void InitializeForSameProcess();
+ void InitializeForOutOfProcess();
+ void Initialize();
+ void Open(Endpoint<PUiCompositorControllerParent>&& aEndpoint);
+ void Shutdown();
+
+ LayersId mRootLayerTreeId;
+
+#if defined(MOZ_WIDGET_ANDROID)
+ bool mCompositorLayersUpdateEnabled; // Flag set to true when the UI thread
+ // is expecting to be notified when a
+ // layer has been updated
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ int32_t mMaxToolbarHeight;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_gfx_ipc_UiCompositorControllerParent_h
diff --git a/gfx/layers/ipc/VideoBridgeChild.cpp b/gfx/layers/ipc/VideoBridgeChild.cpp
new file mode 100644
index 0000000000..d417442919
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeChild.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VideoBridgeChild.h"
+#include "VideoBridgeParent.h"
+#include "CompositorThread.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "transport/runnable_utils.h"
+#include "SynchronousTask.h"
+
+namespace mozilla {
+namespace layers {
+
+StaticRefPtr<VideoBridgeChild> sVideoBridge;
+
+/* static */
+void VideoBridgeChild::StartupForGPUProcess() {
+ ipc::Endpoint<PVideoBridgeParent> parentPipe;
+ ipc::Endpoint<PVideoBridgeChild> childPipe;
+
+ PVideoBridge::CreateEndpoints(base::GetCurrentProcId(),
+ base::GetCurrentProcId(), &parentPipe,
+ &childPipe);
+
+ VideoBridgeChild::Open(std::move(childPipe));
+ VideoBridgeParent::Open(std::move(parentPipe), VideoBridgeSource::GpuProcess);
+}
+
+void VideoBridgeChild::Open(Endpoint<PVideoBridgeChild>&& aEndpoint) {
+ MOZ_ASSERT(!sVideoBridge || !sVideoBridge->CanSend());
+ sVideoBridge = new VideoBridgeChild();
+
+ if (!aEndpoint.Bind(sVideoBridge)) {
+ // We can't recover from this.
+ MOZ_CRASH("Failed to bind VideoBridgeChild to endpoint");
+ }
+}
+
+/* static */
+void VideoBridgeChild::Shutdown() {
+ if (sVideoBridge) {
+ sVideoBridge->Close();
+ sVideoBridge = nullptr;
+ }
+}
+
+VideoBridgeChild::VideoBridgeChild()
+ : mIPDLSelfRef(this),
+ mThread(GetCurrentSerialEventTarget()),
+ mCanSend(true) {}
+
+VideoBridgeChild::~VideoBridgeChild() = default;
+
+VideoBridgeChild* VideoBridgeChild::GetSingleton() { return sVideoBridge; }
+
+bool VideoBridgeChild::AllocUnsafeShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ if (!mThread->IsOnCurrentThread()) {
+ return DispatchAllocShmemInternal(aSize, aType, aShmem,
+ true); // true: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+
+ return PVideoBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool VideoBridgeChild::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ MOZ_ASSERT(CanSend());
+ return PVideoBridgeChild::AllocShmem(aSize, aType, aShmem);
+}
+
+void VideoBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem, bool aUnsafe,
+ bool* aSuccess) {
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ bool ok = false;
+ if (aUnsafe) {
+ ok = AllocUnsafeShmem(aSize, aType, aShmem);
+ } else {
+ ok = AllocShmem(aSize, aType, aShmem);
+ }
+ *aSuccess = ok;
+}
+
+bool VideoBridgeChild::DispatchAllocShmemInternal(
+ size_t aSize, SharedMemory::SharedMemoryType aType, ipc::Shmem* aShmem,
+ bool aUnsafe) {
+ SynchronousTask task("AllocatorProxy alloc");
+
+ bool success = false;
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<VideoBridgeChild>(this), &VideoBridgeChild::ProxyAllocShmemNow,
+ &task, aSize, aType, aShmem, aUnsafe, &success);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+
+ return success;
+}
+
+void VideoBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
+ ipc::Shmem* aShmem, bool* aResult) {
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+ *aResult = DeallocShmem(*aShmem);
+}
+
+bool VideoBridgeChild::DeallocShmem(ipc::Shmem& aShmem) {
+ if (GetThread()->IsOnCurrentThread()) {
+ if (!CanSend()) {
+ return false;
+ }
+ return PVideoBridgeChild::DeallocShmem(aShmem);
+ }
+
+ SynchronousTask task("AllocatorProxy Dealloc");
+ bool result = false;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<VideoBridgeChild>(this), &VideoBridgeChild::ProxyDeallocShmemNow,
+ &task, &aShmem, &result);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+ return result;
+}
+
+PTextureChild* VideoBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
+ const ReadLockDescriptor&,
+ const LayersBackend&,
+ const TextureFlags&,
+ const uint64_t& aSerial) {
+ MOZ_ASSERT(CanSend());
+ return TextureClient::CreateIPDLActor();
+}
+
+bool VideoBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+void VideoBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mCanSend = false;
+}
+
+void VideoBridgeChild::ActorDealloc() { mIPDLSelfRef = nullptr; }
+
+PTextureChild* VideoBridgeChild::CreateTexture(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) {
+ MOZ_ASSERT(CanSend());
+ return SendPTextureConstructor(aSharedData, aReadLock, aLayersBackend, aFlags,
+ aSerial);
+}
+
+bool VideoBridgeChild::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void VideoBridgeChild::HandleFatalError(const char* aMsg) const {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/VideoBridgeChild.h b/gfx/layers/ipc/VideoBridgeChild.h
new file mode 100644
index 0000000000..9370bab04e
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeChild.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_VIDEOBRIDGECHILD_H
+#define MOZILLA_GFX_VIDEOBRIDGECHILD_H
+
+#include "mozilla/layers/PVideoBridgeChild.h"
+#include "mozilla/layers/VideoBridgeUtils.h"
+#include "ISurfaceAllocator.h"
+#include "TextureForwarder.h"
+
+namespace mozilla {
+namespace layers {
+
+class SynchronousTask;
+
+class VideoBridgeChild final : public PVideoBridgeChild,
+ public TextureForwarder {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoBridgeChild, override);
+
+ static void StartupForGPUProcess();
+ static void Shutdown();
+
+ static VideoBridgeChild* GetSingleton();
+
+ // PVideoBridgeChild
+ PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial);
+ bool DeallocPTextureChild(PTextureChild* actor);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void ActorDealloc() override;
+
+ // ISurfaceAllocator
+ bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) override;
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ // TextureForwarder
+ PTextureChild* CreateTexture(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId,
+ nsISerialEventTarget* aTarget = nullptr) override;
+
+ // ClientIPCAllocator
+ base::ProcessId GetParentPid() const override { return OtherPid(); }
+ nsISerialEventTarget* GetThread() const override { return mThread; }
+ void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override {
+ MOZ_ASSERT(false, "NO RECYCLING HERE");
+ }
+
+ // ISurfaceAllocator
+ bool IsSameProcess() const override;
+
+ bool CanSend() { return mCanSend; }
+
+ static void Open(Endpoint<PVideoBridgeChild>&& aEndpoint);
+
+ protected:
+ void HandleFatalError(const char* aMsg) const override;
+ bool DispatchAllocShmemInternal(size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ mozilla::ipc::Shmem* aShmem, bool aUnsafe);
+ void ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ mozilla::ipc::Shmem* aShmem, bool aUnsafe,
+ bool* aSuccess);
+ void ProxyDeallocShmemNow(SynchronousTask* aTask, mozilla::ipc::Shmem* aShmem,
+ bool* aResult);
+
+ private:
+ VideoBridgeChild();
+ virtual ~VideoBridgeChild();
+
+ RefPtr<VideoBridgeChild> mIPDLSelfRef;
+ nsCOMPtr<nsISerialEventTarget> mThread;
+ bool mCanSend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/VideoBridgeParent.cpp b/gfx/layers/ipc/VideoBridgeParent.cpp
new file mode 100644
index 0000000000..89acfa0601
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeParent.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VideoBridgeParent.h"
+#include "CompositorThread.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/VideoBridgeUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+
+static VideoBridgeParent* sVideoBridgeFromRddProcess;
+static VideoBridgeParent* sVideoBridgeFromGpuProcess;
+
+VideoBridgeParent::VideoBridgeParent(VideoBridgeSource aSource)
+ : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()),
+ mClosed(false) {
+ mSelfRef = this;
+ switch (aSource) {
+ default:
+ MOZ_CRASH("Unhandled case");
+ case VideoBridgeSource::RddProcess:
+ sVideoBridgeFromRddProcess = this;
+ break;
+ case VideoBridgeSource::GpuProcess:
+ sVideoBridgeFromGpuProcess = this;
+ break;
+ }
+}
+
+VideoBridgeParent::~VideoBridgeParent() {
+ if (sVideoBridgeFromRddProcess == this) {
+ sVideoBridgeFromRddProcess = nullptr;
+ }
+ if (sVideoBridgeFromGpuProcess == this) {
+ sVideoBridgeFromGpuProcess = nullptr;
+ }
+}
+
+/* static */
+void VideoBridgeParent::Open(Endpoint<PVideoBridgeParent>&& aEndpoint,
+ VideoBridgeSource aSource) {
+ RefPtr<VideoBridgeParent> parent = new VideoBridgeParent(aSource);
+
+ CompositorThread()->Dispatch(
+ NewRunnableMethod<Endpoint<PVideoBridgeParent>&&>(
+ "gfx::layers::VideoBridgeParent::Bind", parent,
+ &VideoBridgeParent::Bind, std::move(aEndpoint)));
+}
+
+void VideoBridgeParent::Bind(Endpoint<PVideoBridgeParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ // We can't recover from this.
+ MOZ_CRASH("Failed to bind VideoBridgeParent to endpoint");
+ }
+}
+
+/* static */
+VideoBridgeParent* VideoBridgeParent::GetSingleton(
+ const Maybe<VideoBridgeSource>& aSource) {
+ MOZ_ASSERT(aSource.isSome());
+ switch (aSource.value()) {
+ default:
+ MOZ_CRASH("Unhandled case");
+ case VideoBridgeSource::RddProcess:
+ MOZ_ASSERT(sVideoBridgeFromRddProcess);
+ return sVideoBridgeFromRddProcess;
+ case VideoBridgeSource::GpuProcess:
+ MOZ_ASSERT(sVideoBridgeFromGpuProcess);
+ return sVideoBridgeFromGpuProcess;
+ }
+}
+
+TextureHost* VideoBridgeParent::LookupTexture(uint64_t aSerial) {
+ MOZ_DIAGNOSTIC_ASSERT(CompositorThread() &&
+ CompositorThread()->IsOnCurrentThread());
+ return TextureHost::AsTextureHost(mTextureMap[aSerial]);
+}
+
+void VideoBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+ // Can't alloc/dealloc shmems from now on.
+ mClosed = true;
+}
+
+void VideoBridgeParent::ActorDealloc() {
+ mCompositorThreadHolder = nullptr;
+ mSelfRef = nullptr;
+}
+
+PTextureParent* VideoBridgeParent::AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const uint64_t& aSerial) {
+ PTextureParent* parent = TextureHost::CreateIPDLActor(
+ this, aSharedData, aReadLock, aLayersBackend, aFlags, aSerial, Nothing());
+
+ if (!parent) {
+ return nullptr;
+ }
+
+ mTextureMap[aSerial] = parent;
+ return parent;
+}
+
+bool VideoBridgeParent::DeallocPTextureParent(PTextureParent* actor) {
+ mTextureMap.erase(TextureHost::GetTextureSerial(actor));
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+void VideoBridgeParent::SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) {
+ MOZ_ASSERT(false, "AsyncMessages not supported");
+}
+
+bool VideoBridgeParent::AllocShmem(size_t aSize,
+ ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ if (mClosed) {
+ return false;
+ }
+ return PVideoBridgeParent::AllocShmem(aSize, aType, aShmem);
+}
+
+bool VideoBridgeParent::AllocUnsafeShmem(
+ size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) {
+ if (mClosed) {
+ return false;
+ }
+ return PVideoBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
+}
+
+bool VideoBridgeParent::DeallocShmem(ipc::Shmem& aShmem) {
+ if (mClosed) {
+ return false;
+ }
+ return PVideoBridgeParent::DeallocShmem(aShmem);
+}
+
+bool VideoBridgeParent::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void VideoBridgeParent::NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) {}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/VideoBridgeParent.h b/gfx/layers/ipc/VideoBridgeParent.h
new file mode 100644
index 0000000000..855c018df3
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeParent.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfx_layers_ipc_VideoBridgeParent_h_
+#define gfx_layers_ipc_VideoBridgeParent_h_
+
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/PVideoBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+enum class VideoBridgeSource : uint8_t;
+class CompositorThreadHolder;
+
+class VideoBridgeParent final : public PVideoBridgeParent,
+ public HostIPCAllocator,
+ public mozilla::ipc::IShmemAllocator {
+ public:
+ ~VideoBridgeParent();
+
+ static VideoBridgeParent* GetSingleton(
+ const Maybe<VideoBridgeSource>& aSource);
+
+ static void Open(Endpoint<PVideoBridgeParent>&& aEndpoint,
+ VideoBridgeSource aSource);
+
+ TextureHost* LookupTexture(uint64_t aSerial);
+
+ // PVideoBridgeParent
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const uint64_t& aSerial);
+ bool DeallocPTextureParent(PTextureParent* actor);
+
+ // HostIPCAllocator
+ base::ProcessId GetChildProcessId() override { return OtherPid(); }
+ void NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) override;
+ void SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) override;
+
+ // ISurfaceAllocator
+ IShmemAllocator* AsShmemAllocator() override { return this; }
+ bool IsSameProcess() const override;
+ bool IPCOpen() const override { return !mClosed; }
+
+ // IShmemAllocator
+ bool AllocShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ bool AllocUnsafeShmem(size_t aSize, ipc::SharedMemory::SharedMemoryType aType,
+ ipc::Shmem* aShmem) override;
+
+ bool DeallocShmem(ipc::Shmem& aShmem) override;
+
+ private:
+ explicit VideoBridgeParent(VideoBridgeSource aSource);
+ void Bind(Endpoint<PVideoBridgeParent>&& aEndpoint);
+
+ void ActorDealloc() override;
+
+ // This keeps us alive until ActorDestroy(), at which point we do a
+ // deferred destruction of ourselves.
+ RefPtr<VideoBridgeParent> mSelfRef;
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+
+ std::map<uint64_t, PTextureParent*> mTextureMap;
+
+ bool mClosed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // gfx_layers_ipc_VideoBridgeParent_h_
diff --git a/gfx/layers/ipc/VideoBridgeUtils.h b/gfx/layers/ipc/VideoBridgeUtils.h
new file mode 100644
index 0000000000..e8e7090063
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeUtils.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_VideoBridgeUtils_h
+#define IPC_VideoBridgeUtils_h
+
+#include "ipc/EnumSerializer.h"
+
+namespace mozilla {
+namespace layers {
+
+enum class VideoBridgeSource : uint8_t {
+ RddProcess,
+ GpuProcess,
+ _Count,
+};
+
+typedef Maybe<VideoBridgeSource> MaybeVideoBridgeSource;
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::VideoBridgeSource>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::VideoBridgeSource,
+ mozilla::layers::VideoBridgeSource::RddProcess,
+ mozilla::layers::VideoBridgeSource::_Count> {};
+
+} // namespace IPC
+
+#endif // IPC_VideoBridgeUtils_h
diff --git a/gfx/layers/ipc/WebRenderMessages.ipdlh b/gfx/layers/ipc/WebRenderMessages.ipdlh
new file mode 100644
index 0000000000..dda247b9e0
--- /dev/null
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PTexture;
+
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::wr::LayoutSize from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::ImageDescriptor from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::ImageRendering from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::MixBlendMode from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MaybeFontInstanceOptions from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MaybeFontInstancePlatformOptions from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::FontInstanceKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::BlobImageKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::gfx::MaybeIntSize from "mozilla/gfx/Point.h";
+using mozilla::LayoutDeviceRect from "Units.h";
+using mozilla::LayoutDeviceSize from "Units.h";
+using mozilla::ImageIntRect from "Units.h";
+using mozilla::gfx::Rect from "mozilla/gfx/Rect.h";
+using mozilla::VideoInfo::Rotation from "MediaInfo.h";
+using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+namespace mozilla {
+namespace layers {
+
+struct RefCountedShmem {
+ Shmem buffer;
+};
+
+struct OpAddPrivateExternalImage {
+ ExternalImageId externalImageId;
+ ImageKey key;
+ ImageDescriptor descriptor;
+};
+
+struct OpAddSharedExternalImage {
+ ExternalImageId externalImageId;
+ ImageKey key;
+};
+
+struct OpPushExternalImageForTexture {
+ ExternalImageId externalImageId;
+ ImageKey key;
+ PTexture texture;
+ bool isUpdate;
+};
+
+struct OpAddCompositorAnimations {
+ CompositorAnimations data;
+};
+
+struct OpAddPipelineIdForCompositable {
+ PipelineId pipelineId;
+ CompositableHandle handle;
+ bool isAsync;
+};
+
+struct OpRemovePipelineIdForCompositable {
+ PipelineId pipelineId;
+};
+
+struct OpReleaseTextureOfImage {
+ ImageKey key;
+};
+
+struct OpUpdateAsyncImagePipeline {
+ PipelineId pipelineId;
+ LayoutDeviceRect scBounds;
+ Rotation rotation;
+ ImageRendering filter;
+ MixBlendMode mixBlendMode;
+};
+
+struct OpUpdatedAsyncImagePipeline {
+ PipelineId pipelineId;
+};
+
+union WebRenderParentCommand {
+ OpAddPipelineIdForCompositable;
+ OpRemovePipelineIdForCompositable;
+ OpReleaseTextureOfImage;
+ OpUpdateAsyncImagePipeline;
+ OpUpdatedAsyncImagePipeline;
+ CompositableOperation;
+ OpAddCompositorAnimations;
+};
+
+struct OffsetRange {
+ uint32_t source;
+ uint32_t start;
+ uint32_t length;
+};
+
+struct OpAddImage {
+ ImageDescriptor descriptor;
+ OffsetRange bytes;
+ uint16_t tiling;
+ ImageKey key;
+};
+
+struct OpAddBlobImage {
+ ImageDescriptor descriptor;
+ OffsetRange bytes;
+ ImageIntRect visibleRect;
+ uint16_t tiling;
+ BlobImageKey key;
+};
+
+struct OpUpdateImage {
+ ImageDescriptor descriptor;
+ OffsetRange bytes;
+ ImageKey key;
+};
+
+struct OpUpdateBlobImage {
+ ImageDescriptor descriptor;
+ OffsetRange bytes;
+ BlobImageKey key;
+ ImageIntRect visibleRect;
+ ImageIntRect dirtyRect;
+};
+
+struct OpSetBlobImageVisibleArea {
+ ImageIntRect area;
+ BlobImageKey key;
+};
+
+struct OpUpdatePrivateExternalImage {
+ ExternalImageId externalImageId;
+ ImageKey key;
+ ImageDescriptor descriptor;
+ ImageIntRect dirtyRect;
+};
+
+struct OpUpdateSharedExternalImage {
+ ExternalImageId externalImageId;
+ ImageKey key;
+ ImageIntRect dirtyRect;
+};
+
+struct OpDeleteImage {
+ ImageKey key;
+};
+
+struct OpDeleteBlobImage {
+ BlobImageKey key;
+};
+
+struct OpAddRawFont {
+ OffsetRange bytes;
+ uint32_t fontIndex;
+ FontKey key;
+};
+
+struct OpAddFontDescriptor {
+ OffsetRange bytes;
+ uint32_t fontIndex;
+ FontKey key;
+};
+
+struct OpDeleteFont {
+ FontKey key;
+};
+
+struct OpAddFontInstance {
+ MaybeFontInstanceOptions options;
+ MaybeFontInstancePlatformOptions platformOptions;
+ OffsetRange variations;
+ FontInstanceKey instanceKey;
+ FontKey fontKey;
+ float glyphSize;
+};
+
+struct OpDeleteFontInstance {
+ FontInstanceKey key;
+};
+
+union OpUpdateResource {
+ OpAddImage;
+ OpAddBlobImage;
+ OpUpdateImage;
+ OpUpdateBlobImage;
+ OpSetBlobImageVisibleArea;
+ OpDeleteImage;
+ OpDeleteBlobImage;
+ OpAddRawFont;
+ OpAddFontDescriptor;
+ OpDeleteFont;
+ OpAddFontInstance;
+ OpDeleteFontInstance;
+ OpAddPrivateExternalImage;
+ OpAddSharedExternalImage;
+ OpPushExternalImageForTexture;
+ OpUpdatePrivateExternalImage;
+ OpUpdateSharedExternalImage;
+};
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/ipc/fuzztest/compositor_manager_parent_ipc_libfuzz.cpp b/gfx/layers/ipc/fuzztest/compositor_manager_parent_ipc_libfuzz.cpp
new file mode 100644
index 0000000000..6e76035031
--- /dev/null
+++ b/gfx/layers/ipc/fuzztest/compositor_manager_parent_ipc_libfuzz.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "FuzzingInterface.h"
+#include "ProtocolFuzzer.h"
+
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+
+int FuzzingInitCompositorManagerParentIPC(int* argc, char*** argv) {
+ mozilla::ipc::ProtocolFuzzerHelper::CompositorBridgeParentSetup();
+ mozilla::layers::LayerTreeOwnerTracker::Initialize();
+ return 0;
+}
+
+static int RunCompositorManagerParentIPCFuzzing(const uint8_t* data,
+ size_t size) {
+ static mozilla::layers::CompositorManagerParent* p =
+ mozilla::layers::CompositorManagerParent::CreateSameProcess().take();
+
+ static nsTArray<nsCString> ignored = mozilla::ipc::LoadIPCMessageBlacklist(
+ getenv("MOZ_IPC_MESSAGE_FUZZ_BLACKLIST"));
+
+ mozilla::ipc::FuzzProtocol(p, data, size, ignored);
+
+ return 0;
+}
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitCompositorManagerParentIPC,
+ RunCompositorManagerParentIPCFuzzing,
+ CompositorManagerParentIPC);
diff --git a/gfx/layers/ipc/fuzztest/moz.build b/gfx/layers/ipc/fuzztest/moz.build
new file mode 100644
index 0000000000..a60293a520
--- /dev/null
+++ b/gfx/layers/ipc/fuzztest/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+Library("FuzzingCompositorManagerParentIPC")
+
+SOURCES += ["compositor_manager_parent_ipc_libfuzz.cpp"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")