summaryrefslogtreecommitdiffstats
path: root/gfx/layers/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/ipc')
-rw-r--r--gfx/layers/ipc/APZCTreeManagerChild.cpp230
-rw-r--r--gfx/layers/ipc/APZCTreeManagerChild.h105
-rw-r--r--gfx/layers/ipc/APZCTreeManagerParent.cpp199
-rw-r--r--gfx/layers/ipc/APZCTreeManagerParent.h81
-rw-r--r--gfx/layers/ipc/APZChild.cpp113
-rw-r--r--gfx/layers/ipc/APZChild.h79
-rw-r--r--gfx/layers/ipc/APZInputBridgeChild.cpp211
-rw-r--r--gfx/layers/ipc/APZInputBridgeChild.h62
-rw-r--r--gfx/layers/ipc/APZInputBridgeParent.cpp213
-rw-r--r--gfx/layers/ipc/APZInputBridgeParent.h75
-rw-r--r--gfx/layers/ipc/ActiveResource.h45
-rw-r--r--gfx/layers/ipc/CanvasChild.cpp547
-rw-r--r--gfx/layers/ipc/CanvasChild.h195
-rw-r--r--gfx/layers/ipc/CanvasTranslator.cpp1201
-rw-r--r--gfx/layers/ipc/CanvasTranslator.h395
-rw-r--r--gfx/layers/ipc/CompositableForwarder.cpp15
-rw-r--r--gfx/layers/ipc/CompositableForwarder.h193
-rw-r--r--gfx/layers/ipc/CompositableTransactionParent.cpp164
-rw-r--r--gfx/layers/ipc/CompositableTransactionParent.h64
-rw-r--r--gfx/layers/ipc/CompositorBench.cpp352
-rw-r--r--gfx/layers/ipc/CompositorBench.h30
-rw-r--r--gfx/layers/ipc/CompositorBridgeChild.cpp640
-rw-r--r--gfx/layers/ipc/CompositorBridgeChild.h252
-rw-r--r--gfx/layers/ipc/CompositorBridgeParent.cpp2008
-rw-r--r--gfx/layers/ipc/CompositorBridgeParent.h648
-rw-r--r--gfx/layers/ipc/CompositorManagerChild.cpp257
-rw-r--r--gfx/layers/ipc/CompositorManagerChild.h125
-rw-r--r--gfx/layers/ipc/CompositorManagerParent.cpp396
-rw-r--r--gfx/layers/ipc/CompositorManagerParent.h114
-rw-r--r--gfx/layers/ipc/CompositorThread.cpp195
-rw-r--r--gfx/layers/ipc/CompositorThread.h81
-rw-r--r--gfx/layers/ipc/CompositorVsyncScheduler.cpp382
-rw-r--r--gfx/layers/ipc/CompositorVsyncScheduler.h187
-rw-r--r--gfx/layers/ipc/CompositorVsyncSchedulerOwner.h34
-rw-r--r--gfx/layers/ipc/ContentCompositorBridgeParent.cpp438
-rw-r--r--gfx/layers/ipc/ContentCompositorBridgeParent.h175
-rw-r--r--gfx/layers/ipc/ISurfaceAllocator.cpp222
-rw-r--r--gfx/layers/ipc/ISurfaceAllocator.h287
-rw-r--r--gfx/layers/ipc/ImageBridgeChild.cpp975
-rw-r--r--gfx/layers/ipc/ImageBridgeChild.h380
-rw-r--r--gfx/layers/ipc/ImageBridgeParent.cpp440
-rw-r--r--gfx/layers/ipc/ImageBridgeParent.h154
-rw-r--r--gfx/layers/ipc/KnowsCompositor.cpp108
-rw-r--r--gfx/layers/ipc/KnowsCompositor.h233
-rw-r--r--gfx/layers/ipc/LayerTreeOwnerTracker.cpp68
-rw-r--r--gfx/layers/ipc/LayerTreeOwnerTracker.h73
-rw-r--r--gfx/layers/ipc/LayersMessageUtils.h1180
-rw-r--r--gfx/layers/ipc/LayersMessages.ipdlh373
-rw-r--r--gfx/layers/ipc/LayersSurfaces.ipdlh217
-rw-r--r--gfx/layers/ipc/PAPZ.ipdl81
-rw-r--r--gfx/layers/ipc/PAPZCTreeManager.ipdl97
-rw-r--r--gfx/layers/ipc/PAPZInputBridge.ipdl88
-rw-r--r--gfx/layers/ipc/PCanvas.ipdl93
-rw-r--r--gfx/layers/ipc/PCompositorBridge.ipdl209
-rw-r--r--gfx/layers/ipc/PCompositorBridgeTypes.ipdlh23
-rw-r--r--gfx/layers/ipc/PCompositorManager.ipdl96
-rw-r--r--gfx/layers/ipc/PImageBridge.ipdl65
-rw-r--r--gfx/layers/ipc/PTexture.ipdl40
-rw-r--r--gfx/layers/ipc/PUiCompositorController.ipdl48
-rw-r--r--gfx/layers/ipc/PVideoBridge.ipdl43
-rw-r--r--gfx/layers/ipc/PWebRenderBridge.ipdl137
-rw-r--r--gfx/layers/ipc/RefCountedShmem.cpp93
-rw-r--r--gfx/layers/ipc/RefCountedShmem.h48
-rw-r--r--gfx/layers/ipc/RemoteContentController.cpp526
-rw-r--r--gfx/layers/ipc/RemoteContentController.h126
-rw-r--r--gfx/layers/ipc/ShadowLayerUtils.h27
-rw-r--r--gfx/layers/ipc/SharedPlanarYCbCrImage.cpp193
-rw-r--r--gfx/layers/ipc/SharedPlanarYCbCrImage.h64
-rw-r--r--gfx/layers/ipc/SharedRGBImage.cpp157
-rw-r--r--gfx/layers/ipc/SharedRGBImage.h61
-rw-r--r--gfx/layers/ipc/SharedSurfacesChild.cpp656
-rw-r--r--gfx/layers/ipc/SharedSurfacesChild.h259
-rw-r--r--gfx/layers/ipc/SharedSurfacesMemoryReport.h59
-rw-r--r--gfx/layers/ipc/SharedSurfacesParent.cpp405
-rw-r--r--gfx/layers/ipc/SharedSurfacesParent.h162
-rw-r--r--gfx/layers/ipc/SurfaceDescriptor.h14
-rw-r--r--gfx/layers/ipc/SynchronousTask.h72
-rw-r--r--gfx/layers/ipc/TextureForwarder.h92
-rw-r--r--gfx/layers/ipc/UiCompositorControllerChild.cpp350
-rw-r--r--gfx/layers/ipc/UiCompositorControllerChild.h120
-rw-r--r--gfx/layers/ipc/UiCompositorControllerMessageTypes.h32
-rw-r--r--gfx/layers/ipc/UiCompositorControllerParent.cpp296
-rw-r--r--gfx/layers/ipc/UiCompositorControllerParent.h84
-rw-r--r--gfx/layers/ipc/VideoBridgeChild.cpp188
-rw-r--r--gfx/layers/ipc/VideoBridgeChild.h90
-rw-r--r--gfx/layers/ipc/VideoBridgeParent.cpp321
-rw-r--r--gfx/layers/ipc/VideoBridgeParent.h85
-rw-r--r--gfx/layers/ipc/VideoBridgeUtils.h38
-rw-r--r--gfx/layers/ipc/WebRenderMessages.ipdlh199
89 files changed, 21048 insertions, 0 deletions
diff --git a/gfx/layers/ipc/APZCTreeManagerChild.cpp b/gfx/layers/ipc/APZCTreeManagerChild.cpp
new file mode 100644
index 0000000000..b7f50c1c92
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp
@@ -0,0 +1,230 @@
+/* -*- 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/DoubleTapToZoom.h" // for DoubleTapToZoomMetrics
+#include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/jni/Utils.h" // for DispatchToGeckoPriorityQueue
+#endif
+
+namespace mozilla {
+namespace layers {
+
+APZCTreeManagerChild::APZCTreeManagerChild()
+ : mCompositorSession(nullptr), mIPCOpen(false) {}
+
+APZCTreeManagerChild::~APZCTreeManagerChild() = default;
+
+void APZCTreeManagerChild::SetCompositorSession(
+ RemoteCompositorSession* aSession) {
+ // Exactly one of mCompositorSession and aSession must be null (i.e. either
+ // we're setting mCompositorSession or we're clearing it).
+ MOZ_ASSERT(!mCompositorSession ^ !aSession);
+ mCompositorSession = aSession;
+}
+
+void APZCTreeManagerChild::SetInputBridge(APZInputBridgeChild* aInputBridge) {
+ // The input bridge only exists from the UI process to the GPU process.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(!mInputBridge);
+
+ mInputBridge = aInputBridge;
+}
+
+void APZCTreeManagerChild::Destroy() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mInputBridge) {
+ mInputBridge->Destroy();
+ mInputBridge = nullptr;
+ }
+}
+
+void APZCTreeManagerChild::SetKeyboardMap(const KeyboardMap& aKeyboardMap) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendSetKeyboardMap(aKeyboardMap);
+}
+
+void APZCTreeManagerChild::ZoomToRect(const ScrollableLayerGuid& aGuid,
+ const ZoomTarget& aZoomTarget,
+ const uint32_t aFlags) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendZoomToRect(aGuid, aZoomTarget, aFlags);
+}
+
+void APZCTreeManagerChild::ContentReceivedInputBlock(uint64_t aInputBlockId,
+ bool aPreventDefault) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+}
+
+void APZCTreeManagerChild::SetTargetAPZC(
+ uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendSetTargetAPZC(aInputBlockId, aTargets);
+}
+
+void APZCTreeManagerChild::UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mIPCOpen) {
+ SendUpdateZoomConstraints(aGuid, aConstraints);
+ }
+}
+
+void APZCTreeManagerChild::SetDPI(float aDpiValue) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendSetDPI(aDpiValue);
+}
+
+void APZCTreeManagerChild::SetAllowedTouchBehavior(
+ uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendSetAllowedTouchBehavior(aInputBlockId, aValues);
+}
+
+void APZCTreeManagerChild::SetBrowserGestureResponse(
+ uint64_t aInputBlockId, BrowserGestureResponse aResponse) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendSetBrowserGestureResponse(aInputBlockId, aResponse);
+}
+
+void APZCTreeManagerChild::StartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendStartScrollbarDrag(aGuid, aDragMetrics);
+}
+
+bool APZCTreeManagerChild::StartAutoscroll(const ScrollableLayerGuid& aGuid,
+ const ScreenPoint& aAnchorLocation) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return SendStartAutoscroll(aGuid, aAnchorLocation);
+}
+
+void APZCTreeManagerChild::StopAutoscroll(const ScrollableLayerGuid& aGuid) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendStopAutoscroll(aGuid);
+}
+
+void APZCTreeManagerChild::SetLongTapEnabled(bool aTapGestureEnabled) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SendSetLongTapEnabled(aTapGestureEnabled);
+}
+
+APZInputBridge* APZCTreeManagerChild::InputBridge() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(mInputBridge);
+
+ return mInputBridge.get();
+}
+
+void APZCTreeManagerChild::AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+}
+
+void APZCTreeManagerChild::ReleaseIPDLReference() {
+ mIPCOpen = false;
+ Release();
+}
+
+void APZCTreeManagerChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mIPCOpen = false;
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerChild::RecvHandleTap(
+ const TapType& aType, const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (mCompositorSession &&
+ mCompositorSession->RootLayerTreeId() == aGuid.mLayersId &&
+ mCompositorSession->GetContentController()) {
+ RefPtr<GeckoContentController> controller =
+ mCompositorSession->GetContentController();
+ controller->HandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId,
+ aDoubleTapToZoomMetrics);
+ return IPC_OK();
+ }
+ dom::BrowserParent* tab =
+ dom::BrowserParent::GetBrowserParentFromLayersId(aGuid.mLayersId);
+ if (tab) {
+#ifdef MOZ_WIDGET_ANDROID
+ // On Android, touch events are dispatched from the UI thread to the main
+ // thread using the Android priority queue. It is possible that this tap has
+ // made it to the GPU process and back before they have been processed. We
+ // must therefore dispatch this message to the same queue, otherwise the tab
+ // may receive the tap event before the touch events that synthesized it.
+ mozilla::jni::DispatchToGeckoPriorityQueue(
+ NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers,
+ ScrollableLayerGuid, uint64_t,
+ Maybe<DoubleTapToZoomMetrics>>(
+ "dom::BrowserParent::SendHandleTap", tab,
+ &dom::BrowserParent::SendHandleTap, aType, aPoint, aModifiers,
+ aGuid, aInputBlockId, aDoubleTapToZoomMetrics));
+#else
+ tab->SendHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId,
+ aDoubleTapToZoomMetrics);
+#endif
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerChild::RecvNotifyPinchGesture(
+ const PinchGestureType& aType, const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint, const LayoutDeviceCoord& aSpanChange,
+ const Modifiers& aModifiers) {
+ // This will only get sent from the GPU process to the parent process, so
+ // this function should never get called in the content process.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We want to handle it in this process regardless of what the target guid
+ // of the pinch is. This may change in the future.
+ if (mCompositorSession && mCompositorSession->GetWidget()) {
+ APZCCallbackHelper::NotifyPinchGesture(aType, aFocusPoint, aSpanChange,
+ aModifiers,
+ mCompositorSession->GetWidget());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerChild::RecvCancelAutoscroll(
+ const ScrollableLayerGuid::ViewID& aScrollId) {
+ // This will only get sent from the GPU process to the parent process, so
+ // this function should never get called in the content process.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ APZCCallbackHelper::CancelAutoscroll(aScrollId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerChild::RecvNotifyScaleGestureComplete(
+ const ScrollableLayerGuid::ViewID& aScrollId, float aScale) {
+ // This will only get sent from the GPU process to the parent process, so
+ // this function should never get called in the content process.
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mCompositorSession && mCompositorSession->GetWidget()) {
+ APZCCallbackHelper::NotifyScaleGestureComplete(
+ mCompositorSession->GetWidget(), aScale);
+ }
+ return IPC_OK();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZCTreeManagerChild.h b/gfx/layers/ipc/APZCTreeManagerChild.h
new file mode 100644
index 0000000000..756677e1e3
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerChild.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerChild_h
+#define mozilla_layers_APZCTreeManagerChild_h
+
+#include "mozilla/layers/APZInputBridge.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/PAPZCTreeManagerChild.h"
+
+#include <unordered_map>
+
+namespace mozilla {
+namespace layers {
+
+class APZInputBridgeChild;
+class RemoteCompositorSession;
+
+class APZCTreeManagerChild : public IAPZCTreeManager,
+ public PAPZCTreeManagerChild {
+ friend class PAPZCTreeManagerChild;
+ using TapType = GeckoContentController_TapType;
+
+ public:
+ APZCTreeManagerChild();
+
+ void SetCompositorSession(RemoteCompositorSession* aSession);
+ void SetInputBridge(APZInputBridgeChild* aInputBridge);
+ void Destroy();
+
+ void SetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
+
+ void ZoomToRect(const ScrollableLayerGuid& aGuid,
+ const ZoomTarget& aZoomTarget,
+ const uint32_t aFlags = DEFAULT_BEHAVIOR) override;
+
+ void ContentReceivedInputBlock(uint64_t aInputBlockId,
+ bool aPreventDefault) override;
+
+ void SetTargetAPZC(uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) override;
+
+ void UpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) override;
+
+ void SetDPI(float aDpiValue) override;
+
+ void SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aValues) override;
+
+ void SetBrowserGestureResponse(uint64_t aInputBlockId,
+ BrowserGestureResponse aResponse) override;
+
+ void StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
+ const AsyncDragMetrics& aDragMetrics) override;
+
+ bool StartAutoscroll(const ScrollableLayerGuid& aGuid,
+ const ScreenPoint& aAnchorLocation) override;
+
+ void StopAutoscroll(const ScrollableLayerGuid& aGuid) override;
+
+ void SetLongTapEnabled(bool aTapGestureEnabled) override;
+
+ APZInputBridge* InputBridge() override;
+
+ void AddIPDLReference();
+ void ReleaseIPDLReference();
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ protected:
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvHandleTap(
+ const TapType& aType, const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics);
+
+ mozilla::ipc::IPCResult RecvNotifyPinchGesture(
+ const PinchGestureType& aType, const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint,
+ const LayoutDeviceCoord& aSpanChange, const Modifiers& aModifiers);
+
+ mozilla::ipc::IPCResult RecvCancelAutoscroll(
+ const ScrollableLayerGuid::ViewID& aScrollId);
+
+ mozilla::ipc::IPCResult RecvNotifyScaleGestureComplete(
+ const ScrollableLayerGuid::ViewID& aScrollId, float aScale);
+
+ virtual ~APZCTreeManagerChild();
+
+ private:
+ MOZ_NON_OWNING_REF RemoteCompositorSession* mCompositorSession;
+ RefPtr<APZInputBridgeChild> mInputBridge;
+ bool mIPCOpen;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZCTreeManagerChild_h
diff --git a/gfx/layers/ipc/APZCTreeManagerParent.cpp b/gfx/layers/ipc/APZCTreeManagerParent.cpp
new file mode 100644
index 0000000000..ff05acd083
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZCTreeManagerParent.h"
+
+#include "apz/src/APZCTreeManager.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layers/APZUpdater.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+APZCTreeManagerParent::APZCTreeManagerParent(
+ LayersId aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager,
+ RefPtr<APZUpdater> aAPZUpdater)
+ : mLayersId(aLayersId),
+ mTreeManager(std::move(aAPZCTreeManager)),
+ mUpdater(std::move(aAPZUpdater)) {
+ MOZ_ASSERT(mTreeManager != nullptr);
+ MOZ_ASSERT(mUpdater != nullptr);
+ MOZ_ASSERT(mUpdater->HasTreeManager(mTreeManager));
+}
+
+APZCTreeManagerParent::~APZCTreeManagerParent() = default;
+
+void APZCTreeManagerParent::ChildAdopted(
+ RefPtr<APZCTreeManager> aAPZCTreeManager, RefPtr<APZUpdater> aAPZUpdater) {
+ MOZ_ASSERT(aAPZCTreeManager != nullptr);
+ MOZ_ASSERT(aAPZUpdater != nullptr);
+ MOZ_ASSERT(aAPZUpdater->HasTreeManager(aAPZCTreeManager));
+ mTreeManager = std::move(aAPZCTreeManager);
+ mUpdater = std::move(aAPZUpdater);
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetKeyboardMap(
+ const KeyboardMap& aKeyboardMap) {
+ mUpdater->RunOnUpdaterThread(
+ mLayersId, NewRunnableMethod<KeyboardMap>(
+ "layers::IAPZCTreeManager::SetKeyboardMap", mTreeManager,
+ &IAPZCTreeManager::SetKeyboardMap, aKeyboardMap));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvZoomToRect(
+ const ScrollableLayerGuid& aGuid, const ZoomTarget& aZoomTarget,
+ const uint32_t& aFlags) {
+ if (!IsGuidValid(aGuid)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mUpdater->RunOnUpdaterThread(
+ aGuid.mLayersId,
+ NewRunnableMethod<ScrollableLayerGuid, ZoomTarget, uint32_t>(
+ "layers::IAPZCTreeManager::ZoomToRect", mTreeManager,
+ &IAPZCTreeManager::ZoomToRect, aGuid, aZoomTarget, aFlags));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvContentReceivedInputBlock(
+ const uint64_t& aInputBlockId, const bool& aPreventDefault) {
+ mUpdater->RunOnUpdaterThread(
+ mLayersId, NewRunnableMethod<uint64_t, bool>(
+ "layers::IAPZCTreeManager::ContentReceivedInputBlock",
+ mTreeManager, &IAPZCTreeManager::ContentReceivedInputBlock,
+ aInputBlockId, aPreventDefault));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetTargetAPZC(
+ const uint64_t& aInputBlockId, nsTArray<ScrollableLayerGuid>&& aTargets) {
+ mUpdater->RunOnUpdaterThread(
+ mLayersId,
+ NewRunnableMethod<uint64_t,
+ StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
+ "layers::IAPZCTreeManager::SetTargetAPZC", mTreeManager,
+ &IAPZCTreeManager::SetTargetAPZC, aInputBlockId,
+ std::move(aTargets)));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvUpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints) {
+ if (!IsGuidValid(aGuid)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mTreeManager->UpdateZoomConstraints(aGuid, aConstraints);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetDPI(
+ const float& aDpiValue) {
+ mUpdater->RunOnUpdaterThread(
+ mLayersId,
+ NewRunnableMethod<float>("layers::IAPZCTreeManager::SetDPI", mTreeManager,
+ &IAPZCTreeManager::SetDPI, aDpiValue));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetAllowedTouchBehavior(
+ const uint64_t& aInputBlockId, nsTArray<TouchBehaviorFlags>&& aValues) {
+ mUpdater->RunOnUpdaterThread(
+ mLayersId,
+ NewRunnableMethod<uint64_t,
+ StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>(
+ "layers::IAPZCTreeManager::SetAllowedTouchBehavior", mTreeManager,
+ &IAPZCTreeManager::SetAllowedTouchBehavior, aInputBlockId,
+ std::move(aValues)));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetBrowserGestureResponse(
+ const uint64_t& aInputBlockId, const BrowserGestureResponse& aResponse) {
+ mUpdater->RunOnUpdaterThread(
+ mLayersId, NewRunnableMethod<uint64_t, BrowserGestureResponse>(
+ "layers::IAPZCTreeManager::SetBrowserGestureResponse",
+ mTreeManager, &IAPZCTreeManager::SetBrowserGestureResponse,
+ aInputBlockId, aResponse));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) {
+ if (!IsGuidValid(aGuid)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mUpdater->RunOnUpdaterThread(
+ aGuid.mLayersId,
+ NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
+ "layers::IAPZCTreeManager::StartScrollbarDrag", mTreeManager,
+ &IAPZCTreeManager::StartScrollbarDrag, aGuid, aDragMetrics));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartAutoscroll(
+ const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation) {
+ // Unlike RecvStartScrollbarDrag(), this message comes from the parent
+ // process (via nsBaseWidget::mAPZC) rather than from the child process
+ // (via BrowserChild::mApzcTreeManager), so there is no need to check the
+ // layers id against mLayersId (and in any case, it wouldn't match, because
+ // mLayersId stores the parent process's layers id, while nsBaseWidget is
+ // sending the child process's layers id).
+
+ mUpdater->RunOnControllerThread(
+ mLayersId,
+ NewRunnableMethod<ScrollableLayerGuid, ScreenPoint>(
+ "layers::IAPZCTreeManager::StartAutoscroll", mTreeManager,
+ &IAPZCTreeManager::StartAutoscroll, aGuid, aAnchorLocation));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStopAutoscroll(
+ const ScrollableLayerGuid& aGuid) {
+ // See RecvStartAutoscroll() for why we don't check the layers id.
+
+ mUpdater->RunOnControllerThread(
+ mLayersId, NewRunnableMethod<ScrollableLayerGuid>(
+ "layers::IAPZCTreeManager::StopAutoscroll", mTreeManager,
+ &IAPZCTreeManager::StopAutoscroll, aGuid));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetLongTapEnabled(
+ const bool& aLongTapEnabled) {
+ mUpdater->RunOnUpdaterThread(
+ mLayersId,
+ NewRunnableMethod<bool>(
+ "layers::IAPZCTreeManager::SetLongTapEnabled", mTreeManager,
+ &IAPZCTreeManager::SetLongTapEnabled, aLongTapEnabled));
+
+ return IPC_OK();
+}
+
+bool APZCTreeManagerParent::IsGuidValid(const ScrollableLayerGuid& aGuid) {
+ if (aGuid.mLayersId != mLayersId) {
+ NS_ERROR("Unexpected layers id");
+ return false;
+ }
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZCTreeManagerParent.h b/gfx/layers/ipc/APZCTreeManagerParent.h
new file mode 100644
index 0000000000..c75091c796
--- /dev/null
+++ b/gfx/layers/ipc/APZCTreeManagerParent.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZCTreeManagerParent_h
+#define mozilla_layers_APZCTreeManagerParent_h
+
+#include "mozilla/layers/PAPZCTreeManagerParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class APZCTreeManager;
+class APZUpdater;
+
+class APZCTreeManagerParent : public PAPZCTreeManagerParent {
+ public:
+ APZCTreeManagerParent(LayersId aLayersId,
+ RefPtr<APZCTreeManager> aAPZCTreeManager,
+ RefPtr<APZUpdater> mAPZUpdater);
+ virtual ~APZCTreeManagerParent();
+
+ LayersId GetLayersId() const { return mLayersId; }
+
+ /**
+ * Called when the layer tree that this protocol is connected to
+ * is adopted by another compositor, and we need to switch APZCTreeManagers.
+ */
+ void ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager,
+ RefPtr<APZUpdater> aAPZUpdater);
+
+ mozilla::ipc::IPCResult RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap);
+
+ mozilla::ipc::IPCResult RecvZoomToRect(const ScrollableLayerGuid& aGuid,
+ const ZoomTarget& aZoomTarget,
+ const uint32_t& aFlags);
+
+ mozilla::ipc::IPCResult RecvContentReceivedInputBlock(
+ const uint64_t& aInputBlockId, const bool& aPreventDefault);
+
+ mozilla::ipc::IPCResult RecvSetTargetAPZC(
+ const uint64_t& aInputBlockId, nsTArray<ScrollableLayerGuid>&& aTargets);
+
+ mozilla::ipc::IPCResult RecvUpdateZoomConstraints(
+ const ScrollableLayerGuid& aGuid,
+ const Maybe<ZoomConstraints>& aConstraints);
+
+ mozilla::ipc::IPCResult RecvSetDPI(const float& aDpiValue);
+
+ mozilla::ipc::IPCResult RecvSetAllowedTouchBehavior(
+ const uint64_t& aInputBlockId, nsTArray<TouchBehaviorFlags>&& aValues);
+
+ mozilla::ipc::IPCResult RecvSetBrowserGestureResponse(
+ const uint64_t& aInputBlockId, const BrowserGestureResponse& aResponse);
+
+ mozilla::ipc::IPCResult RecvStartScrollbarDrag(
+ const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics);
+
+ mozilla::ipc::IPCResult RecvStartAutoscroll(
+ const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation);
+
+ mozilla::ipc::IPCResult RecvStopAutoscroll(const ScrollableLayerGuid& aGuid);
+
+ mozilla::ipc::IPCResult RecvSetLongTapEnabled(const bool& aTapGestureEnabled);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override {}
+
+ private:
+ bool IsGuidValid(const ScrollableLayerGuid& aGuid);
+
+ LayersId mLayersId;
+ RefPtr<APZCTreeManager> mTreeManager;
+ RefPtr<APZUpdater> mUpdater;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZCTreeManagerParent_h
diff --git a/gfx/layers/ipc/APZChild.cpp b/gfx/layers/ipc/APZChild.cpp
new file mode 100644
index 0000000000..00146ac59a
--- /dev/null
+++ b/gfx/layers/ipc/APZChild.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/GeckoContentController.h"
+
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+
+#include "InputData.h" // for InputData
+
+namespace mozilla {
+namespace layers {
+
+APZChild::APZChild(RefPtr<GeckoContentController> aController)
+ : mController(aController) {
+ MOZ_ASSERT(mController);
+}
+
+APZChild::~APZChild() {
+ if (mAPZTaskRunnable) {
+ mAPZTaskRunnable->Revoke();
+ mAPZTaskRunnable = nullptr;
+ }
+ if (mController) {
+ mController->Destroy();
+ mController = nullptr;
+ }
+}
+
+mozilla::ipc::IPCResult APZChild::RecvLayerTransforms(
+ nsTArray<MatrixMessage>&& aTransforms) {
+ mController->NotifyLayerTransforms(std::move(aTransforms));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvRequestContentRepaint(
+ const RepaintRequest& aRequest) {
+ MOZ_ASSERT(mController->IsRepaintThread());
+
+ EnsureAPZTaskRunnable();
+
+ mAPZTaskRunnable->QueueRequest(aRequest);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvUpdateOverscrollVelocity(
+ const ScrollableLayerGuid& aGuid, const float& aX, const float& aY,
+ const bool& aIsRootContent) {
+ mController->UpdateOverscrollVelocity(aGuid, aX, aY, aIsRootContent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvUpdateOverscrollOffset(
+ const ScrollableLayerGuid& aGuid, const float& aX, const float& aY,
+ const bool& aIsRootContent) {
+ mController->UpdateOverscrollOffset(aGuid, aX, aY, aIsRootContent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyMozMouseScrollEvent(
+ const ViewID& aScrollId, const nsString& aEvent) {
+ mController->NotifyMozMouseScrollEvent(aScrollId, aEvent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyAPZStateChange(
+ const ScrollableLayerGuid& aGuid, const APZStateChange& aChange,
+ const int& aArg, Maybe<uint64_t> aInputBlockId) {
+ mController->NotifyAPZStateChange(aGuid, aChange, aArg, aInputBlockId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyFlushComplete() {
+ MOZ_ASSERT(mController->IsRepaintThread());
+ EnsureAPZTaskRunnable();
+
+ mAPZTaskRunnable->QueueFlushCompleteNotification();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyAsyncScrollbarDragInitiated(
+ const uint64_t& aDragBlockId, const ViewID& aScrollId,
+ const ScrollDirection& aDirection) {
+ mController->NotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId,
+ aDirection);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyAsyncScrollbarDragRejected(
+ const ViewID& aScrollId) {
+ mController->NotifyAsyncScrollbarDragRejected(aScrollId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvNotifyAsyncAutoscrollRejected(
+ const ViewID& aScrollId) {
+ mController->NotifyAsyncAutoscrollRejected(aScrollId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZChild::RecvDestroy() {
+ // mController->Destroy will be called in the destructor
+ PAPZChild::Send__delete__(this);
+ return IPC_OK();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZChild.h b/gfx/layers/ipc/APZChild.h
new file mode 100644
index 0000000000..dfb3216127
--- /dev/null
+++ b/gfx/layers/ipc/APZChild.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZChild_h
+#define mozilla_layers_APZChild_h
+
+#include "mozilla/layers/PAPZChild.h"
+#include "mozilla/layers/APZTaskRunnable.h"
+
+namespace mozilla {
+namespace layers {
+
+class GeckoContentController;
+
+/**
+ * APZChild implements PAPZChild and is used to remote a GeckoContentController
+ * that lives in a different process than where APZ lives.
+ */
+class APZChild final : public PAPZChild {
+ public:
+ using APZStateChange = GeckoContentController_APZStateChange;
+
+ explicit APZChild(RefPtr<GeckoContentController> aController);
+ virtual ~APZChild();
+
+ mozilla::ipc::IPCResult RecvLayerTransforms(
+ nsTArray<MatrixMessage>&& aTransforms);
+
+ mozilla::ipc::IPCResult RecvRequestContentRepaint(
+ const RepaintRequest& aRequest);
+
+ mozilla::ipc::IPCResult RecvUpdateOverscrollVelocity(
+ const ScrollableLayerGuid& aGuid, const float& aX, const float& aY,
+ const bool& aIsRootContent);
+
+ mozilla::ipc::IPCResult RecvUpdateOverscrollOffset(
+ const ScrollableLayerGuid& aGuid, const float& aX, const float& aY,
+ const bool& aIsRootContent);
+
+ mozilla::ipc::IPCResult RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId,
+ const nsString& aEvent);
+
+ mozilla::ipc::IPCResult RecvNotifyAPZStateChange(
+ const ScrollableLayerGuid& aGuid, const APZStateChange& aChange,
+ const int& aArg, Maybe<uint64_t> aInputBlockId);
+
+ mozilla::ipc::IPCResult RecvNotifyFlushComplete();
+
+ mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragInitiated(
+ const uint64_t& aDragBlockId, const ViewID& aScrollId,
+ const ScrollDirection& aDirection);
+ mozilla::ipc::IPCResult RecvNotifyAsyncScrollbarDragRejected(
+ const ViewID& aScrollId);
+
+ mozilla::ipc::IPCResult RecvNotifyAsyncAutoscrollRejected(
+ const ViewID& aScrollId);
+
+ mozilla::ipc::IPCResult RecvDestroy();
+
+ private:
+ void EnsureAPZTaskRunnable() {
+ if (!mAPZTaskRunnable) {
+ mAPZTaskRunnable = new APZTaskRunnable(mController);
+ }
+ }
+
+ RefPtr<GeckoContentController> mController;
+ // A runnable invoked in a nsRefreshDriver's tick to update multiple
+ // RepaintRequests and notify a "apz-repaints-flushed" at the same time.
+ RefPtr<APZTaskRunnable> mAPZTaskRunnable;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZChild_h
diff --git a/gfx/layers/ipc/APZInputBridgeChild.cpp b/gfx/layers/ipc/APZInputBridgeChild.cpp
new file mode 100644
index 0000000000..bf059143ec
--- /dev/null
+++ b/gfx/layers/ipc/APZInputBridgeChild.cpp
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZInputBridgeChild.h"
+
+#include "InputData.h" // for InputData, etc
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layers/SynchronousTask.h"
+
+namespace mozilla {
+namespace layers {
+
+/* static */
+RefPtr<APZInputBridgeChild> APZInputBridgeChild::Create(
+ const uint64_t& aProcessToken, Endpoint<PAPZInputBridgeChild>&& aEndpoint) {
+ RefPtr<APZInputBridgeChild> child = new APZInputBridgeChild(aProcessToken);
+
+ MOZ_ASSERT(APZThreadUtils::IsControllerThreadAlive());
+
+ APZThreadUtils::RunOnControllerThread(
+ NewRunnableMethod<Endpoint<PAPZInputBridgeChild>&&>(
+ "layers::APZInputBridgeChild::Open", child,
+ &APZInputBridgeChild::Open, std::move(aEndpoint)));
+
+ return child;
+}
+
+APZInputBridgeChild::APZInputBridgeChild(const uint64_t& aProcessToken)
+ : mIsOpen(false), mProcessToken(aProcessToken) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+APZInputBridgeChild::~APZInputBridgeChild() = default;
+
+void APZInputBridgeChild::Open(Endpoint<PAPZInputBridgeChild>&& aEndpoint) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ mIsOpen = aEndpoint.Bind(this);
+
+ if (!mIsOpen) {
+ // The GPU Process Manager might be gone if we receive ActorDestroy very
+ // late in shutdown.
+ if (gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get()) {
+ gpm->NotifyRemoteActorDestroyed(mProcessToken);
+ }
+ return;
+ }
+}
+
+void APZInputBridgeChild::Destroy() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Destroy will get called from the main thread, so we must synchronously
+ // dispatch to the controller thread to close the bridge.
+ layers::SynchronousTask task("layers::APZInputBridgeChild::Destroy");
+ APZThreadUtils::RunOnControllerThread(
+ NS_NewRunnableFunction("layers::APZInputBridgeChild::Destroy", [&]() {
+ APZThreadUtils::AssertOnControllerThread();
+ AutoCompleteTask complete(&task);
+
+ // Clear the process token so that we don't notify the GPUProcessManager
+ // about an abnormal shutdown, thereby tearing down the GPU process.
+ mProcessToken = 0;
+
+ if (mIsOpen) {
+ PAPZInputBridgeChild::Close();
+ mIsOpen = false;
+ }
+ }));
+
+ task.Wait();
+}
+
+void APZInputBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mIsOpen = false;
+
+ if (mProcessToken) {
+ gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+ mProcessToken = 0;
+ }
+}
+
+APZEventResult APZInputBridgeChild::ReceiveInputEvent(
+ InputData& aEvent, InputBlockCallback&& aCallback) {
+ MOZ_ASSERT(mIsOpen);
+ APZThreadUtils::AssertOnControllerThread();
+
+ APZEventResult res;
+ switch (aEvent.mInputType) {
+ case MULTITOUCH_INPUT: {
+ MultiTouchInput& event = aEvent.AsMultiTouchInput();
+ MultiTouchInput processedEvent;
+
+ SendReceiveMultiTouchInputEvent(event, !!aCallback, &res,
+ &processedEvent);
+
+ event = processedEvent;
+ break;
+ }
+ case MOUSE_INPUT: {
+ MouseInput& event = aEvent.AsMouseInput();
+ MouseInput processedEvent;
+
+ SendReceiveMouseInputEvent(event, !!aCallback, &res, &processedEvent);
+
+ event = processedEvent;
+ break;
+ }
+ case PANGESTURE_INPUT: {
+ PanGestureInput& event = aEvent.AsPanGestureInput();
+ PanGestureInput processedEvent;
+
+ SendReceivePanGestureInputEvent(event, !!aCallback, &res,
+ &processedEvent);
+
+ event = processedEvent;
+ break;
+ }
+ case PINCHGESTURE_INPUT: {
+ PinchGestureInput& event = aEvent.AsPinchGestureInput();
+ PinchGestureInput processedEvent;
+
+ SendReceivePinchGestureInputEvent(event, !!aCallback, &res,
+ &processedEvent);
+
+ event = processedEvent;
+ break;
+ }
+ case TAPGESTURE_INPUT: {
+ TapGestureInput& event = aEvent.AsTapGestureInput();
+ TapGestureInput processedEvent;
+
+ SendReceiveTapGestureInputEvent(event, !!aCallback, &res,
+ &processedEvent);
+
+ event = processedEvent;
+ break;
+ }
+ case SCROLLWHEEL_INPUT: {
+ ScrollWheelInput& event = aEvent.AsScrollWheelInput();
+ ScrollWheelInput processedEvent;
+
+ SendReceiveScrollWheelInputEvent(event, !!aCallback, &res,
+ &processedEvent);
+
+ event = processedEvent;
+ break;
+ }
+ case KEYBOARD_INPUT: {
+ KeyboardInput& event = aEvent.AsKeyboardInput();
+ KeyboardInput processedEvent;
+
+ SendReceiveKeyboardInputEvent(event, !!aCallback, &res, &processedEvent);
+
+ event = processedEvent;
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("Invalid InputData type.");
+ res.SetStatusAsConsumeNoDefault();
+ break;
+ }
+ }
+
+ if (aCallback && res.WillHaveDelayedResult()) {
+ mInputBlockCallbacks.emplace(res.mInputBlockId, std::move(aCallback));
+ }
+
+ return res;
+}
+
+mozilla::ipc::IPCResult APZInputBridgeChild::RecvCallInputBlockCallback(
+ uint64_t aInputBlockId, const APZHandledResult& aHandledResult) {
+ auto it = mInputBlockCallbacks.find(aInputBlockId);
+ if (it != mInputBlockCallbacks.end()) {
+ it->second(aInputBlockId, aHandledResult);
+ // The callback is one-shot; discard it after calling it.
+ mInputBlockCallbacks.erase(it);
+ }
+
+ return IPC_OK();
+}
+
+void APZInputBridgeChild::ProcessUnhandledEvent(
+ LayoutDeviceIntPoint* aRefPoint, ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutFocusSequenceNumber, LayersId* aOutLayersId) {
+ MOZ_ASSERT(mIsOpen);
+ APZThreadUtils::AssertOnControllerThread();
+
+ SendProcessUnhandledEvent(*aRefPoint, aRefPoint, aOutTargetGuid,
+ aOutFocusSequenceNumber, aOutLayersId);
+}
+
+void APZInputBridgeChild::UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage,
+ const Maybe<ScrollableLayerGuid>& aTargetGuid) {
+ MOZ_ASSERT(mIsOpen);
+ APZThreadUtils::AssertOnControllerThread();
+
+ SendUpdateWheelTransaction(aRefPoint, aEventMessage, aTargetGuid);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZInputBridgeChild.h b/gfx/layers/ipc/APZInputBridgeChild.h
new file mode 100644
index 0000000000..dd77d95f36
--- /dev/null
+++ b/gfx/layers/ipc/APZInputBridgeChild.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZInputBridgeChild_h
+#define mozilla_layers_APZInputBridgeChild_h
+
+#include "mozilla/layers/APZInputBridge.h"
+#include "mozilla/layers/PAPZInputBridgeChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class APZInputBridgeChild : public PAPZInputBridgeChild, public APZInputBridge {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZInputBridgeChild, final)
+
+ public:
+ static RefPtr<APZInputBridgeChild> Create(
+ const uint64_t& aProcessToken,
+ Endpoint<PAPZInputBridgeChild>&& aEndpoint);
+
+ void Destroy();
+
+ APZEventResult ReceiveInputEvent(
+ InputData& aEvent,
+ InputBlockCallback&& aCallback = InputBlockCallback()) override;
+
+ mozilla::ipc::IPCResult RecvCallInputBlockCallback(
+ uint64_t aInputBlockId, const APZHandledResult& handledResult);
+
+ protected:
+ void ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutFocusSequenceNumber,
+ LayersId* aOutLayersId) override;
+
+ void UpdateWheelTransaction(
+ LayoutDeviceIntPoint aRefPoint, EventMessage aEventMessage,
+ const Maybe<ScrollableLayerGuid>& aTargetGuid) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ explicit APZInputBridgeChild(const uint64_t& aProcessToken);
+ virtual ~APZInputBridgeChild();
+
+ private:
+ void Open(Endpoint<PAPZInputBridgeChild>&& aEndpoint);
+
+ bool mIsOpen;
+ uint64_t mProcessToken;
+
+ using InputBlockCallbackMap =
+ std::unordered_map<uint64_t, InputBlockCallback>;
+ InputBlockCallbackMap mInputBlockCallbacks;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZInputBridgeChild_h
diff --git a/gfx/layers/ipc/APZInputBridgeParent.cpp b/gfx/layers/ipc/APZInputBridgeParent.cpp
new file mode 100644
index 0000000000..fcc642ff7a
--- /dev/null
+++ b/gfx/layers/ipc/APZInputBridgeParent.cpp
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZInputBridgeParent.h"
+
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/APZInputBridge.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "InputData.h"
+
+namespace mozilla {
+namespace layers {
+
+/* static */
+RefPtr<APZInputBridgeParent> APZInputBridgeParent::Create(
+ const LayersId& aLayersId, Endpoint<PAPZInputBridgeParent>&& aEndpoint) {
+ RefPtr<APZInputBridgeParent> parent = new APZInputBridgeParent(aLayersId);
+ if (!aEndpoint.Bind(parent)) {
+ // We can't recover from this.
+ MOZ_CRASH("Failed to bind APZInputBridgeParent to endpoint");
+ }
+
+ return parent;
+}
+
+APZInputBridgeParent::APZInputBridgeParent(const LayersId& aLayersId) {
+ MOZ_ASSERT(XRE_IsGPUProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mTreeManager = CompositorBridgeParent::GetAPZCTreeManager(aLayersId);
+ MOZ_ASSERT(mTreeManager);
+}
+
+APZInputBridgeParent::~APZInputBridgeParent() = default;
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveMultiTouchInputEvent(
+ const MultiTouchInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, MultiTouchInput* aOutEvent) {
+ MultiTouchInput event = aEvent;
+
+ APZInputBridge::InputBlockCallback callback;
+ if (aWantsCallback) {
+ callback = [self = RefPtr<APZInputBridgeParent>(this)](
+ uint64_t aInputBlockId,
+ const APZHandledResult& aHandledResult) {
+ Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult);
+ };
+ }
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(
+ event, std::move(callback));
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveMouseInputEvent(
+ const MouseInput& aEvent, bool aWantsCallback, APZEventResult* aOutResult,
+ MouseInput* aOutEvent) {
+ MouseInput event = aEvent;
+
+ APZInputBridge::InputBlockCallback callback;
+ if (aWantsCallback) {
+ callback = [self = RefPtr<APZInputBridgeParent>(this)](
+ uint64_t aInputBlockId,
+ const APZHandledResult& aHandledResult) {
+ Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult);
+ };
+ }
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(
+ event, std::move(callback));
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceivePanGestureInputEvent(
+ const PanGestureInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, PanGestureInput* aOutEvent) {
+ PanGestureInput event = aEvent;
+
+ APZInputBridge::InputBlockCallback callback;
+ if (aWantsCallback) {
+ callback = [self = RefPtr<APZInputBridgeParent>(this)](
+ uint64_t aInputBlockId,
+ const APZHandledResult& aHandledResult) {
+ Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult);
+ };
+ }
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(
+ event, std::move(callback));
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceivePinchGestureInputEvent(
+ const PinchGestureInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, PinchGestureInput* aOutEvent) {
+ PinchGestureInput event = aEvent;
+
+ APZInputBridge::InputBlockCallback callback;
+ if (aWantsCallback) {
+ callback = [self = RefPtr<APZInputBridgeParent>(this)](
+ uint64_t aInputBlockId,
+ const APZHandledResult& aHandledResult) {
+ Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult);
+ };
+ }
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(
+ event, std::move(callback));
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveTapGestureInputEvent(
+ const TapGestureInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, TapGestureInput* aOutEvent) {
+ TapGestureInput event = aEvent;
+
+ APZInputBridge::InputBlockCallback callback;
+ if (aWantsCallback) {
+ callback = [self = RefPtr<APZInputBridgeParent>(this)](
+ uint64_t aInputBlockId,
+ const APZHandledResult& aHandledResult) {
+ Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult);
+ };
+ }
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(
+ event, std::move(callback));
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveScrollWheelInputEvent(
+ const ScrollWheelInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, ScrollWheelInput* aOutEvent) {
+ ScrollWheelInput event = aEvent;
+
+ APZInputBridge::InputBlockCallback callback;
+ if (aWantsCallback) {
+ callback = [self = RefPtr<APZInputBridgeParent>(this)](
+ uint64_t aInputBlockId,
+ const APZHandledResult& aHandledResult) {
+ Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult);
+ };
+ }
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(
+ event, std::move(callback));
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvReceiveKeyboardInputEvent(
+ const KeyboardInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, KeyboardInput* aOutEvent) {
+ KeyboardInput event = aEvent;
+
+ APZInputBridge::InputBlockCallback callback;
+ if (aWantsCallback) {
+ callback = [self = RefPtr<APZInputBridgeParent>(this)](
+ uint64_t aInputBlockId,
+ const APZHandledResult& aHandledResult) {
+ Unused << self->SendCallInputBlockCallback(aInputBlockId, aHandledResult);
+ };
+ }
+
+ *aOutResult = mTreeManager->InputBridge()->ReceiveInputEvent(
+ event, std::move(callback));
+ *aOutEvent = event;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvUpdateWheelTransaction(
+ const LayoutDeviceIntPoint& aRefPoint, const EventMessage& aEventMessage,
+ const Maybe<ScrollableLayerGuid>& aTargetGuid) {
+ mTreeManager->InputBridge()->UpdateWheelTransaction(aRefPoint, aEventMessage,
+ aTargetGuid);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult APZInputBridgeParent::RecvProcessUnhandledEvent(
+ const LayoutDeviceIntPoint& aRefPoint, LayoutDeviceIntPoint* aOutRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutFocusSequenceNumber,
+ LayersId* aOutLayersId) {
+ LayoutDeviceIntPoint refPoint = aRefPoint;
+ mTreeManager->InputBridge()->ProcessUnhandledEvent(
+ &refPoint, aOutTargetGuid, aOutFocusSequenceNumber, aOutLayersId);
+ *aOutRefPoint = refPoint;
+
+ return IPC_OK();
+}
+
+void APZInputBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+ // We shouldn't need it after this
+ mTreeManager = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/APZInputBridgeParent.h b/gfx/layers/ipc/APZInputBridgeParent.h
new file mode 100644
index 0000000000..71f43d092b
--- /dev/null
+++ b/gfx/layers/ipc/APZInputBridgeParent.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZInputBridgeParent_h
+#define mozilla_layers_APZInputBridgeParent_h
+
+#include "mozilla/layers/PAPZInputBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class IAPZCTreeManager;
+
+class APZInputBridgeParent : public PAPZInputBridgeParent {
+ NS_INLINE_DECL_REFCOUNTING(APZInputBridgeParent, final)
+
+ public:
+ static RefPtr<APZInputBridgeParent> Create(
+ const LayersId& aLayersId, Endpoint<PAPZInputBridgeParent>&& aEndpoint);
+
+ mozilla::ipc::IPCResult RecvReceiveMultiTouchInputEvent(
+ const MultiTouchInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, MultiTouchInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceiveMouseInputEvent(const MouseInput& aEvent,
+ bool aWantsCallback,
+ APZEventResult* aOutResult,
+ MouseInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceivePanGestureInputEvent(
+ const PanGestureInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, PanGestureInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceivePinchGestureInputEvent(
+ const PinchGestureInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, PinchGestureInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceiveTapGestureInputEvent(
+ const TapGestureInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, TapGestureInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceiveScrollWheelInputEvent(
+ const ScrollWheelInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, ScrollWheelInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvReceiveKeyboardInputEvent(
+ const KeyboardInput& aEvent, bool aWantsCallback,
+ APZEventResult* aOutResult, KeyboardInput* aOutEvent);
+
+ mozilla::ipc::IPCResult RecvUpdateWheelTransaction(
+ const LayoutDeviceIntPoint& aRefPoint, const EventMessage& aEventMessage,
+ const Maybe<ScrollableLayerGuid>& aTargetGuid);
+
+ mozilla::ipc::IPCResult RecvProcessUnhandledEvent(
+ const LayoutDeviceIntPoint& aRefPoint, LayoutDeviceIntPoint* aOutRefPoint,
+ ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutFocusSequenceNumber,
+ LayersId* aOutLayersId);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ protected:
+ explicit APZInputBridgeParent(const LayersId& aLayersId);
+ virtual ~APZInputBridgeParent();
+
+ private:
+ RefPtr<IAPZCTreeManager> mTreeManager;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZInputBridgeParent_h
diff --git a/gfx/layers/ipc/ActiveResource.h b/gfx/layers/ipc/ActiveResource.h
new file mode 100644
index 0000000000..6e7db60541
--- /dev/null
+++ b/gfx/layers/ipc/ActiveResource.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_ACTIVERESOURCE
+#define MOZILLA_LAYERS_ACTIVERESOURCE
+
+#include "nsExpirationTracker.h"
+
+namespace mozilla::layers {
+
+/**
+ * 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 final
+ : 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();
+ }
+};
+
+} // namespace mozilla::layers
+
+#endif
diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp
new file mode 100644
index 0000000000..553217d82b
--- /dev/null
+++ b/gfx/layers/ipc/CanvasChild.cpp
@@ -0,0 +1,547 @@
+/* -*- 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/CanvasManagerChild.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 "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/SourceSurfaceSharedData.h"
+#include "mozilla/Maybe.h"
+#include "nsIObserverService.h"
+#include "RecordedCanvasEventImpl.h"
+
+namespace mozilla {
+namespace layers {
+
+class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers {
+ public:
+ NS_DECL_OWNINGTHREAD
+
+ explicit RecorderHelpers(const RefPtr<CanvasChild>& aCanvasChild)
+ : mCanvasChild(aCanvasChild) {}
+
+ ~RecorderHelpers() override = default;
+
+ bool InitTranslator(TextureType aTextureType, gfx::BackendType aBackendType,
+ Handle&& aReadHandle, nsTArray<Handle>&& aBufferHandles,
+ uint64_t aBufferSize,
+ CrossProcessSemaphoreHandle&& aReaderSem,
+ CrossProcessSemaphoreHandle&& aWriterSem) override {
+ NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
+ if (NS_WARN_IF(!mCanvasChild)) {
+ return false;
+ }
+ return mCanvasChild->SendInitTranslator(
+ aTextureType, aBackendType, std::move(aReadHandle),
+ std::move(aBufferHandles), aBufferSize, std::move(aReaderSem),
+ std::move(aWriterSem));
+ }
+
+ bool AddBuffer(Handle&& aBufferHandle, uint64_t aBufferSize) override {
+ NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
+ if (!mCanvasChild) {
+ return false;
+ }
+ return mCanvasChild->SendAddBuffer(std::move(aBufferHandle), aBufferSize);
+ }
+
+ bool ReaderClosed() override {
+ NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
+ if (!mCanvasChild) {
+ return false;
+ }
+ return !mCanvasChild->CanSend() || ipc::ProcessChild::ExpectingShutdown();
+ }
+
+ bool RestartReader() override {
+ NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
+ if (!mCanvasChild) {
+ return false;
+ }
+ return mCanvasChild->SendRestartTranslation();
+ }
+
+ private:
+ const WeakPtr<CanvasChild> mCanvasChild;
+};
+
+class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
+ public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording, final)
+
+ SourceSurfaceCanvasRecording(
+ int64_t aTextureId, const RefPtr<gfx::SourceSurface>& aRecordedSuface,
+ CanvasChild* aCanvasChild,
+ const RefPtr<CanvasDrawEventRecorder>& aRecorder)
+ : mTextureId(aTextureId),
+ mRecordedSurface(aRecordedSuface),
+ mCanvasChild(aCanvasChild),
+ mRecorder(aRecorder) {
+ // It's important that AddStoredObject is called first because that will
+ // run any pending processing required by recorded objects that have been
+ // deleted off the main thread.
+ mRecorder->AddStoredObject(this);
+ mRecorder->RecordEvent(RecordedAddSurfaceAlias(this, aRecordedSuface));
+ }
+
+ ~SourceSurfaceCanvasRecording() {
+ ReferencePtr surfaceAlias = this;
+ if (NS_IsMainThread()) {
+ ReleaseOnMainThread(std::move(mRecorder), surfaceAlias,
+ std::move(mRecordedSurface), std::move(mCanvasChild));
+ return;
+ }
+
+ mRecorder->AddPendingDeletion(
+ [recorder = std::move(mRecorder), surfaceAlias,
+ aliasedSurface = std::move(mRecordedSurface),
+ canvasChild = std::move(mCanvasChild)]() mutable -> void {
+ ReleaseOnMainThread(std::move(recorder), surfaceAlias,
+ std::move(aliasedSurface),
+ std::move(canvasChild));
+ });
+ }
+
+ gfx::SurfaceType GetType() const final { return mRecordedSurface->GetType(); }
+
+ gfx::IntSize GetSize() const final { return mRecordedSurface->GetSize(); }
+
+ gfx::SurfaceFormat GetFormat() const final {
+ return mRecordedSurface->GetFormat();
+ }
+
+ already_AddRefed<gfx::DataSourceSurface> GetDataSurface() final {
+ EnsureDataSurfaceOnMainThread();
+ return do_AddRef(mDataSourceSurface);
+ }
+
+ void AttachSurface() { mDetached = false; }
+ void DetachSurface() { mDetached = true; }
+
+ already_AddRefed<gfx::SourceSurface> ExtractSubrect(
+ const gfx::IntRect& aRect) final {
+ return mRecordedSurface->ExtractSubrect(aRect);
+ }
+
+ private:
+ void EnsureDataSurfaceOnMainThread() {
+ // The data can only be retrieved on the main thread.
+ if (!mDataSourceSurface && NS_IsMainThread()) {
+ mDataSourceSurface =
+ mCanvasChild->GetDataSurface(mTextureId, mRecordedSurface, mDetached);
+ }
+ }
+
+ // Used to ensure that clean-up that requires it is done on the main thread.
+ static void ReleaseOnMainThread(RefPtr<CanvasDrawEventRecorder> aRecorder,
+ ReferencePtr aSurfaceAlias,
+ RefPtr<gfx::SourceSurface> aAliasedSurface,
+ RefPtr<CanvasChild> aCanvasChild) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ aRecorder->RemoveStoredObject(aSurfaceAlias);
+ aRecorder->RecordEvent(RecordedRemoveSurfaceAlias(aSurfaceAlias));
+ aAliasedSurface = nullptr;
+ aCanvasChild = nullptr;
+ aRecorder = nullptr;
+ }
+
+ int64_t mTextureId;
+ RefPtr<gfx::SourceSurface> mRecordedSurface;
+ RefPtr<CanvasChild> mCanvasChild;
+ RefPtr<CanvasDrawEventRecorder> mRecorder;
+ RefPtr<gfx::DataSourceSurface> mDataSourceSurface;
+ bool mDetached = false;
+};
+
+CanvasChild::CanvasChild() = default;
+
+CanvasChild::~CanvasChild() = default;
+
+static void NotifyCanvasDeviceReset() {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "canvas-device-reset", nullptr);
+ }
+}
+
+ipc::IPCResult CanvasChild::RecvNotifyDeviceChanged() {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ NotifyCanvasDeviceReset();
+ mRecorder->RecordEvent(RecordedDeviceChangeAcknowledged());
+ return IPC_OK();
+}
+
+/* static */ bool CanvasChild::mDeactivated = false;
+
+ipc::IPCResult CanvasChild::RecvDeactivate() {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ RefPtr<CanvasChild> self(this);
+ mDeactivated = true;
+ if (auto* cm = gfx::CanvasManagerChild::Get()) {
+ cm->DeactivateCanvas();
+ }
+ NotifyCanvasDeviceReset();
+ return IPC_OK();
+}
+
+ipc::IPCResult CanvasChild::RecvBlockCanvas() {
+ mBlocked = true;
+ if (auto* cm = gfx::CanvasManagerChild::Get()) {
+ cm->BlockCanvas();
+ }
+ return IPC_OK();
+}
+
+void CanvasChild::EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureType aTextureType) {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ if (!mRecorder) {
+ gfx::BackendType backendType =
+ gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+ auto recorder = MakeRefPtr<CanvasDrawEventRecorder>();
+ if (!recorder->Init(aTextureType, backendType,
+ MakeUnique<RecorderHelpers>(this))) {
+ return;
+ }
+
+ mRecorder = recorder.forget();
+ }
+
+ MOZ_RELEASE_ASSERT(mRecorder->GetTextureType() == aTextureType,
+ "We only support one remote TextureType currently.");
+
+ EnsureDataSurfaceShmem(aSize, aFormat);
+}
+
+void CanvasChild::ActorDestroy(ActorDestroyReason aWhy) {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ if (mRecorder) {
+ mRecorder->DetachResources();
+ }
+}
+
+void CanvasChild::Destroy() {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ if (CanSend()) {
+ Send__delete__(this);
+ }
+}
+
+bool CanvasChild::EnsureBeginTransaction() {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ if (!mIsInTransaction) {
+ RecordEvent(RecordedCanvasBeginTransaction());
+ mIsInTransaction = true;
+ }
+
+ return true;
+}
+
+void CanvasChild::EndTransaction() {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ if (mIsInTransaction) {
+ RecordEvent(RecordedCanvasEndTransaction());
+ mIsInTransaction = false;
+ mDormant = false;
+ } else if (mRecorder) {
+ // Schedule to drop free buffers if we have no non-empty transactions.
+ if (!mDormant) {
+ mDormant = true;
+ NS_DelayedDispatchToCurrentThread(
+ NewRunnableMethod("CanvasChild::DropFreeBuffersWhenDormant", this,
+ &CanvasChild::DropFreeBuffersWhenDormant),
+ StaticPrefs::gfx_canvas_remote_drop_buffer_milliseconds());
+ }
+ }
+
+ ++mTransactionsSinceGetDataSurface;
+}
+
+void CanvasChild::DropFreeBuffersWhenDormant() {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+ // Drop any free buffers if we have not had any non-empty transactions.
+ if (mDormant && mRecorder) {
+ mRecorder->DropFreeBuffers();
+ }
+}
+
+void CanvasChild::ClearCachedResources() {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+ if (mRecorder) {
+ mRecorder->DropFreeBuffers();
+ // Notify CanvasTranslator it is about to be minimized.
+ SendClearCachedResources();
+ }
+}
+
+bool CanvasChild::ShouldBeCleanedUp() const {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ // Always return true if we've been deactivated.
+ if (Deactivated()) {
+ return true;
+ }
+
+ // We can only be cleaned up if nothing else references our recorder.
+ return !mRecorder || mRecorder->hasOneRef();
+}
+
+already_AddRefed<gfx::DrawTargetRecording> CanvasChild::CreateDrawTarget(
+ int64_t aTextureId, const RemoteTextureOwnerId& aTextureOwnerId,
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ if (!mRecorder) {
+ return nullptr;
+ }
+
+ RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
+ gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
+ RefPtr<gfx::DrawTargetRecording> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
+ mRecorder, aTextureId, aTextureOwnerId, dummyDt, aSize);
+
+ mTextureInfo.insert({aTextureId, {}});
+
+ return dt.forget();
+}
+
+bool CanvasChild::EnsureDataSurfaceShmem(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat) {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ if (!mRecorder) {
+ return false;
+ }
+
+ size_t sizeRequired =
+ ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
+ if (!sizeRequired) {
+ return false;
+ }
+ sizeRequired = ipc::SharedMemory::PageAlignedSize(sizeRequired);
+
+ if (!mDataSurfaceShmemAvailable || mDataSurfaceShmem->Size() < sizeRequired) {
+ RecordEvent(RecordedPauseTranslation());
+ auto dataSurfaceShmem = MakeRefPtr<ipc::SharedMemoryBasic>();
+ if (!dataSurfaceShmem->Create(sizeRequired) ||
+ !dataSurfaceShmem->Map(sizeRequired)) {
+ return false;
+ }
+
+ auto shmemHandle = dataSurfaceShmem->TakeHandle();
+ if (!shmemHandle) {
+ return false;
+ }
+
+ if (!SendSetDataSurfaceBuffer(std::move(shmemHandle), sizeRequired)) {
+ return false;
+ }
+
+ mDataSurfaceShmem = dataSurfaceShmem.forget();
+ mDataSurfaceShmemAvailable = true;
+ }
+
+ MOZ_ASSERT(mDataSurfaceShmemAvailable);
+ return true;
+}
+
+void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ // We drop mRecorder in ActorDestroy to break the reference cycle.
+ if (!mRecorder) {
+ return;
+ }
+
+ mRecorder->RecordEvent(aEvent);
+}
+
+int64_t CanvasChild::CreateCheckpoint() {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+ return mRecorder->CreateCheckpoint();
+}
+
+already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
+ int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached) {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+ MOZ_ASSERT(aSurface);
+
+ // mTransactionsSinceGetDataSurface is used to determine if we want to prepare
+ // a DataSourceSurface in the GPU process up front at the end of the
+ // transaction, but that only makes sense if the canvas JS is requesting data
+ // in between transactions.
+ if (!mIsInTransaction) {
+ mTransactionsSinceGetDataSurface = 0;
+ }
+
+ if (!EnsureBeginTransaction()) {
+ return nullptr;
+ }
+
+ // Shmem is only valid if the surface is the latest snapshot (not detached).
+ if (!aDetached) {
+ // If there is a shmem associated with this snapshot id, then we want to try
+ // use that directly without having to allocate a new shmem for retrieval.
+ auto it = mTextureInfo.find(aTextureId);
+ if (it != mTextureInfo.end() && it->second.mSnapshotShmem) {
+ const auto shmemPtr =
+ reinterpret_cast<uint8_t*>(it->second.mSnapshotShmem->memory());
+ MOZ_ASSERT(shmemPtr);
+ mRecorder->RecordEvent(RecordedPrepareShmem(aTextureId));
+ auto checkpoint = CreateCheckpoint();
+ if (NS_WARN_IF(!mRecorder->WaitForCheckpoint(checkpoint))) {
+ return nullptr;
+ }
+ gfx::IntSize size = aSurface->GetSize();
+ gfx::SurfaceFormat format = aSurface->GetFormat();
+ auto stride = ImageDataSerializer::ComputeRGBStride(format, size.width);
+ RefPtr<gfx::DataSourceSurface> dataSurface =
+ gfx::Factory::CreateWrappingDataSourceSurface(shmemPtr, stride, size,
+ format);
+ return dataSurface.forget();
+ }
+ }
+
+ RecordEvent(RecordedPrepareDataForSurface(aSurface));
+
+ gfx::IntSize ssSize = aSurface->GetSize();
+ gfx::SurfaceFormat ssFormat = aSurface->GetFormat();
+ if (!EnsureDataSurfaceShmem(ssSize, ssFormat)) {
+ return nullptr;
+ }
+
+ RecordEvent(RecordedGetDataForSurface(aSurface));
+ auto checkpoint = CreateCheckpoint();
+ if (NS_WARN_IF(!mRecorder->WaitForCheckpoint(checkpoint))) {
+ return nullptr;
+ }
+
+ mDataSurfaceShmemAvailable = false;
+ struct DataShmemHolder {
+ RefPtr<ipc::SharedMemoryBasic> shmem;
+ RefPtr<CanvasChild> canvasChild;
+ };
+
+ auto* data = static_cast<uint8_t*>(mDataSurfaceShmem->memory());
+ auto* closure = new DataShmemHolder{do_AddRef(mDataSurfaceShmem), this};
+ auto stride = ImageDataSerializer::ComputeRGBStride(ssFormat, ssSize.width);
+
+ RefPtr<gfx::DataSourceSurface> dataSurface =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ data, stride, ssSize, ssFormat, ReleaseDataShmemHolder, closure);
+ return dataSurface.forget();
+}
+
+/* static */ void CanvasChild::ReleaseDataShmemHolder(void* aClosure) {
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "CanvasChild::ReleaseDataShmemHolder",
+ [aClosure]() { ReleaseDataShmemHolder(aClosure); }));
+ return;
+ }
+
+ auto* shmemHolder = static_cast<DataShmemHolder*>(aClosure);
+ shmemHolder->canvasChild->ReturnDataSurfaceShmem(shmemHolder->shmem.forget());
+ delete shmemHolder;
+}
+
+already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface(
+ const RefPtr<gfx::SourceSurface>& aSurface, int64_t aTextureId) {
+ NS_ASSERT_OWNINGTHREAD(CanvasChild);
+
+ if (!aSurface) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<SourceSurfaceCanvasRecording>(aTextureId, aSurface, this,
+ mRecorder);
+}
+
+void CanvasChild::ReturnDataSurfaceShmem(
+ already_AddRefed<ipc::SharedMemoryBasic> aDataSurfaceShmem) {
+ RefPtr<ipc::SharedMemoryBasic> data = aDataSurfaceShmem;
+ // We can only reuse the latest data surface shmem.
+ if (data == mDataSurfaceShmem) {
+ MOZ_ASSERT(!mDataSurfaceShmemAvailable);
+ mDataSurfaceShmemAvailable = true;
+ }
+}
+
+void CanvasChild::AttachSurface(const RefPtr<gfx::SourceSurface>& aSurface) {
+ if (auto* surface =
+ static_cast<SourceSurfaceCanvasRecording*>(aSurface.get())) {
+ surface->AttachSurface();
+ }
+}
+
+void CanvasChild::DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface) {
+ if (auto* surface =
+ static_cast<SourceSurfaceCanvasRecording*>(aSurface.get())) {
+ surface->DetachSurface();
+ }
+}
+
+ipc::IPCResult CanvasChild::RecvNotifyRequiresRefresh(int64_t aTextureId) {
+ auto it = mTextureInfo.find(aTextureId);
+ if (it != mTextureInfo.end()) {
+ it->second.mRequiresRefresh = true;
+ }
+ return IPC_OK();
+}
+
+bool CanvasChild::RequiresRefresh(int64_t aTextureId) const {
+ if (mBlocked) {
+ return true;
+ }
+ auto it = mTextureInfo.find(aTextureId);
+ if (it != mTextureInfo.end()) {
+ return it->second.mRequiresRefresh;
+ }
+ return false;
+}
+
+ipc::IPCResult CanvasChild::RecvSnapshotShmem(
+ int64_t aTextureId, Handle&& aShmemHandle, uint32_t aShmemSize,
+ SnapshotShmemResolver&& aResolve) {
+ auto it = mTextureInfo.find(aTextureId);
+ if (it != mTextureInfo.end()) {
+ auto shmem = MakeRefPtr<ipc::SharedMemoryBasic>();
+ if (NS_WARN_IF(!shmem->SetHandle(std::move(aShmemHandle),
+ ipc::SharedMemory::RightsReadOnly)) ||
+ NS_WARN_IF(!shmem->Map(aShmemSize))) {
+ shmem = nullptr;
+ } else {
+ it->second.mSnapshotShmem = std::move(shmem);
+ }
+ aResolve(true);
+ } else {
+ aResolve(false);
+ }
+ return IPC_OK();
+}
+
+void CanvasChild::CleanupTexture(int64_t aTextureId) {
+ mTextureInfo.erase(aTextureId);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CanvasChild.h b/gfx/layers/ipc/CanvasChild.h
new file mode 100644
index 0000000000..c99fe50bfb
--- /dev/null
+++ b/gfx/layers/ipc/CanvasChild.h
@@ -0,0 +1,195 @@
+/* -*- 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 "mozilla/WeakPtr.h"
+
+namespace mozilla {
+
+namespace gfx {
+class DrawTargetRecording;
+class SourceSurface;
+}
+
+namespace layers {
+class CanvasDrawEventRecorder;
+struct RemoteTextureOwnerId;
+
+class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(CanvasChild)
+
+ CanvasChild();
+
+ /**
+ * @returns true if remote canvas has been deactivated due to failure.
+ */
+ static bool Deactivated() { return mDeactivated; }
+
+ /**
+ * Release resources until they are next required.
+ */
+ void ClearCachedResources();
+
+ ipc::IPCResult RecvNotifyDeviceChanged();
+
+ ipc::IPCResult RecvDeactivate();
+
+ ipc::IPCResult RecvBlockCanvas();
+
+ ipc::IPCResult RecvNotifyRequiresRefresh(int64_t aTextureId);
+
+ ipc::IPCResult RecvSnapshotShmem(int64_t aTextureId, Handle&& aShmemHandle,
+ uint32_t aShmemSize,
+ SnapshotShmemResolver&& aResolve);
+
+ /**
+ * Ensures that the DrawEventRecorder has been created.
+ *
+ * @params aTextureType the TextureType to create in the CanvasTranslator.
+ */
+ void EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureType aTextureType);
+
+ /**
+ * Clean up IPDL actor.
+ */
+ void Destroy();
+
+ /**
+ * @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.
+ * @returns false on failure to begin transaction
+ */
+ bool 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 aTextureId the id of the new texture
+ * @param aSize size for the DrawTarget
+ * @param aFormat SurfaceFormat for the DrawTarget
+ * @returns newly created DrawTargetRecording
+ */
+ already_AddRefed<gfx::DrawTargetRecording> CreateDrawTarget(
+ int64_t aTextureId, const RemoteTextureOwnerId& aTextureOwnerId,
+ 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);
+
+ int64_t CreateCheckpoint();
+
+ /**
+ * Wrap the given surface, so that we can provide a DataSourceSurface if
+ * required.
+ * @param aSurface the SourceSurface to wrap
+ * @param aTextureId the texture id of the source TextureData
+ * @returns a SourceSurface that can provide a DataSourceSurface if required
+ */
+ already_AddRefed<gfx::SourceSurface> WrapSurface(
+ const RefPtr<gfx::SourceSurface>& aSurface, int64_t aTextureId);
+
+ /**
+ * The DrawTargetRecording backing the surface has not been modified since the
+ * previous use, so it is safe to reattach the snapshot for readback.
+ */
+ void AttachSurface(const RefPtr<gfx::SourceSurface>& aSurface);
+
+ /**
+ * The DrawTargetRecording is about to change, so detach the old snapshot.
+ */
+ void DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface);
+
+ /**
+ * Get DataSourceSurface from the translated equivalent version of aSurface in
+ * the GPU process.
+ * @param aTextureId the source TextureData to read from
+ * @param aSurface the SourceSurface in this process for which we need a
+ * DataSourceSurface
+ * @param aDetached whether the surface is old
+ * @returns a DataSourceSurface created from data for aSurface retrieve from
+ * GPU process
+ */
+ already_AddRefed<gfx::DataSourceSurface> GetDataSurface(
+ int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached);
+
+ bool RequiresRefresh(int64_t aTextureId) const;
+
+ void CleanupTexture(int64_t aTextureId);
+
+ protected:
+ void ActorDestroy(ActorDestroyReason aWhy) final;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CanvasChild);
+
+ ~CanvasChild() final;
+
+ bool EnsureDataSurfaceShmem(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+
+ void ReturnDataSurfaceShmem(
+ already_AddRefed<ipc::SharedMemoryBasic> aDataSurfaceShmem);
+
+ struct DataShmemHolder {
+ RefPtr<ipc::SharedMemoryBasic> shmem;
+ RefPtr<CanvasChild> canvasChild;
+ };
+
+ static void ReleaseDataShmemHolder(void* aClosure);
+
+ void DropFreeBuffersWhenDormant();
+
+ static const uint32_t kCacheDataSurfaceThreshold = 10;
+
+ static bool mDeactivated;
+
+ RefPtr<CanvasDrawEventRecorder> mRecorder;
+
+ RefPtr<ipc::SharedMemoryBasic> mDataSurfaceShmem;
+ bool mDataSurfaceShmemAvailable = false;
+ int64_t mLastWriteLockCheckpoint = 0;
+ uint32_t mTransactionsSinceGetDataSurface = kCacheDataSurfaceThreshold;
+ struct TextureInfo {
+ RefPtr<mozilla::ipc::SharedMemoryBasic> mSnapshotShmem;
+ bool mRequiresRefresh = false;
+ };
+ std::unordered_map<int64_t, TextureInfo> mTextureInfo;
+ bool mIsInTransaction = false;
+ bool mDormant = false;
+ bool mBlocked = false;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CanvasChild_h
diff --git a/gfx/layers/ipc/CanvasTranslator.cpp b/gfx/layers/ipc/CanvasTranslator.cpp
new file mode 100644
index 0000000000..08150d6952
--- /dev/null
+++ b/gfx/layers/ipc/CanvasTranslator.cpp
@@ -0,0 +1,1201 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "CanvasTranslator.h"
+
+#include "gfxGradientCache.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/CanvasManagerParent.h"
+#include "mozilla/gfx/CanvasRenderThread.h"
+#include "mozilla/gfx/DrawTargetWebgl.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/CanvasTranslator.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/SharedSurfacesParent.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/SyncRunnable.h"
+#include "mozilla/TaskQueue.h"
+#include "mozilla/Telemetry.h"
+#include "GLContext.h"
+#include "RecordedCanvasEventImpl.h"
+
+#if defined(XP_WIN)
+# include "mozilla/gfx/DeviceManagerDx.h"
+# include "mozilla/layers/TextureD3D11.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+UniquePtr<TextureData> CanvasTranslator::CreateTextureData(
+ const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, bool aClear) {
+ TextureData* textureData = nullptr;
+ TextureAllocationFlags allocFlags =
+ aClear ? ALLOC_CLEAR_BUFFER : ALLOC_DEFAULT;
+ switch (mTextureType) {
+#ifdef XP_WIN
+ case TextureType::D3D11: {
+ textureData =
+ D3D11TextureData::Create(aSize, aFormat, allocFlags, mDevice);
+ break;
+ }
+#endif
+ case TextureType::Unknown:
+ textureData = BufferTextureData::Create(
+ aSize, aFormat, gfx::BackendType::SKIA, LayersBackend::LAYERS_WR,
+ TextureFlags::DEALLOCATE_CLIENT | TextureFlags::REMOTE_TEXTURE,
+ allocFlags, nullptr);
+ break;
+ default:
+ textureData = TextureData::Create(mTextureType, aFormat, aSize,
+ allocFlags, mBackendType);
+ break;
+ }
+
+ return WrapUnique(textureData);
+}
+
+CanvasTranslator::CanvasTranslator(
+ layers::SharedSurfacesHolder* aSharedSurfacesHolder,
+ const dom::ContentParentId& aContentId, uint32_t aManagerId)
+ : mTranslationTaskQueue(gfx::CanvasRenderThread::CreateWorkerTaskQueue()),
+ mSharedSurfacesHolder(aSharedSurfacesHolder),
+ mMaxSpinCount(StaticPrefs::gfx_canvas_remote_max_spin_count()),
+ mContentId(aContentId),
+ mManagerId(aManagerId) {
+ mNextEventTimeout = TimeDuration::FromMilliseconds(
+ StaticPrefs::gfx_canvas_remote_event_timeout_ms());
+
+ // Track when remote canvas has been activated.
+ Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_CANVAS_REMOTE_ACTIVATED, 1);
+}
+
+CanvasTranslator::~CanvasTranslator() = default;
+
+void CanvasTranslator::DispatchToTaskQueue(
+ already_AddRefed<nsIRunnable> aRunnable) {
+ if (mTranslationTaskQueue) {
+ MOZ_ALWAYS_SUCCEEDS(mTranslationTaskQueue->Dispatch(std::move(aRunnable)));
+ } else {
+ gfx::CanvasRenderThread::Dispatch(std::move(aRunnable));
+ }
+}
+
+bool CanvasTranslator::IsInTaskQueue() const {
+ if (mTranslationTaskQueue) {
+ return mTranslationTaskQueue->IsCurrentThreadIn();
+ }
+ return gfx::CanvasRenderThread::IsInCanvasRenderThread();
+}
+
+static bool CreateAndMapShmem(RefPtr<ipc::SharedMemoryBasic>& aShmem,
+ Handle&& aHandle,
+ ipc::SharedMemory::OpenRights aOpenRights,
+ size_t aSize) {
+ auto shmem = MakeRefPtr<ipc::SharedMemoryBasic>();
+ if (!shmem->SetHandle(std::move(aHandle), aOpenRights) ||
+ !shmem->Map(aSize)) {
+ return false;
+ }
+
+ shmem->CloseHandle();
+ aShmem = shmem.forget();
+ return true;
+}
+
+bool CanvasTranslator::EnsureSharedContextWebgl() {
+ if (!mSharedContext || mSharedContext->IsContextLost()) {
+ if (mSharedContext) {
+ ForceDrawTargetWebglFallback();
+ if (mRemoteTextureOwner) {
+ // Ensure any shared surfaces referring to the old context go away.
+ mRemoteTextureOwner->ClearRecycledTextures();
+ }
+ }
+ mSharedContext = gfx::SharedContextWebgl::Create();
+ if (!mSharedContext || mSharedContext->IsContextLost()) {
+ mSharedContext = nullptr;
+ BlockCanvas();
+ return false;
+ }
+ }
+ return true;
+}
+
+mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
+ TextureType aTextureType, gfx::BackendType aBackendType,
+ Handle&& aReadHandle, nsTArray<Handle>&& aBufferHandles,
+ uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem,
+ CrossProcessSemaphoreHandle&& aWriterSem) {
+ if (mHeaderShmem) {
+ return IPC_FAIL(this, "RecvInitTranslator called twice.");
+ }
+
+ mTextureType = aTextureType;
+ mBackendType = aBackendType;
+ mOtherPid = OtherPid();
+
+ mHeaderShmem = MakeAndAddRef<ipc::SharedMemoryBasic>();
+ if (!CreateAndMapShmem(mHeaderShmem, std::move(aReadHandle),
+ ipc::SharedMemory::RightsReadWrite, sizeof(Header))) {
+ Deactivate();
+ return IPC_FAIL(this, "Failed to map canvas header shared memory.");
+ }
+
+ mHeader = static_cast<Header*>(mHeaderShmem->memory());
+
+ mWriterSemaphore.reset(CrossProcessSemaphore::Create(std::move(aWriterSem)));
+ mWriterSemaphore->CloseHandle();
+
+ mReaderSemaphore.reset(CrossProcessSemaphore::Create(std::move(aReaderSem)));
+ mReaderSemaphore->CloseHandle();
+
+ if (!CheckForFreshCanvasDevice(__LINE__)) {
+ gfxCriticalNote << "GFX: CanvasTranslator failed to get device";
+ return IPC_OK();
+ }
+
+ if (gfx::gfxVars::UseAcceleratedCanvas2D() && !EnsureSharedContextWebgl()) {
+ gfxCriticalNote
+ << "GFX: CanvasTranslator failed creating WebGL shared context";
+ }
+
+ // Use the first buffer as our current buffer.
+ mDefaultBufferSize = aBufferSize;
+ auto handleIter = aBufferHandles.begin();
+ if (!CreateAndMapShmem(mCurrentShmem.shmem, std::move(*handleIter),
+ ipc::SharedMemory::RightsReadOnly, aBufferSize)) {
+ Deactivate();
+ return IPC_FAIL(this, "Failed to map canvas buffer shared memory.");
+ }
+ mCurrentMemReader = mCurrentShmem.CreateMemReader();
+
+ // Add all other buffers to our recycled CanvasShmems.
+ for (handleIter++; handleIter < aBufferHandles.end(); handleIter++) {
+ CanvasShmem newShmem;
+ if (!CreateAndMapShmem(newShmem.shmem, std::move(*handleIter),
+ ipc::SharedMemory::RightsReadOnly, aBufferSize)) {
+ Deactivate();
+ return IPC_FAIL(this, "Failed to map canvas buffer shared memory.");
+ }
+ mCanvasShmems.emplace(std::move(newShmem));
+ }
+
+ DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::TranslateRecording",
+ this,
+ &CanvasTranslator::TranslateRecording));
+ return IPC_OK();
+}
+
+ipc::IPCResult CanvasTranslator::RecvRestartTranslation() {
+ if (mDeactivated) {
+ // The other side might have sent a message before we deactivated.
+ return IPC_OK();
+ }
+
+ DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::TranslateRecording",
+ this,
+ &CanvasTranslator::TranslateRecording));
+
+ return IPC_OK();
+}
+
+ipc::IPCResult CanvasTranslator::RecvAddBuffer(
+ ipc::SharedMemoryBasic::Handle&& aBufferHandle, uint64_t aBufferSize) {
+ if (mDeactivated) {
+ // The other side might have sent a resume message before we deactivated.
+ return IPC_OK();
+ }
+
+ DispatchToTaskQueue(
+ NewRunnableMethod<ipc::SharedMemoryBasic::Handle&&, size_t>(
+ "CanvasTranslator::AddBuffer", this, &CanvasTranslator::AddBuffer,
+ std::move(aBufferHandle), aBufferSize));
+
+ return IPC_OK();
+}
+
+void CanvasTranslator::AddBuffer(ipc::SharedMemoryBasic::Handle&& aBufferHandle,
+ size_t aBufferSize) {
+ MOZ_ASSERT(IsInTaskQueue());
+ if (mHeader->readerState == State::Failed) {
+ // We failed before we got to the pause event.
+ return;
+ }
+
+ if (mHeader->readerState != State::Paused) {
+ gfxCriticalNote << "CanvasTranslator::AddBuffer bad state "
+ << uint32_t(State(mHeader->readerState));
+ MOZ_DIAGNOSTIC_ASSERT(false, "mHeader->readerState == State::Paused");
+ Deactivate();
+ return;
+ }
+
+ MOZ_ASSERT(mDefaultBufferSize != 0);
+
+ // Check and signal the writer when we finish with a buffer, because it
+ // might have hit the buffer count limit and be waiting to use our old one.
+ CheckAndSignalWriter();
+
+ // Default sized buffers will have been queued for recycling.
+ if (mCurrentShmem.Size() == mDefaultBufferSize) {
+ mCanvasShmems.emplace(std::move(mCurrentShmem));
+ }
+
+ CanvasShmem newShmem;
+ if (!CreateAndMapShmem(newShmem.shmem, std::move(aBufferHandle),
+ ipc::SharedMemory::RightsReadOnly, aBufferSize)) {
+ return;
+ }
+
+ mCurrentShmem = std::move(newShmem);
+ mCurrentMemReader = mCurrentShmem.CreateMemReader();
+
+ TranslateRecording();
+}
+
+ipc::IPCResult CanvasTranslator::RecvSetDataSurfaceBuffer(
+ ipc::SharedMemoryBasic::Handle&& aBufferHandle, uint64_t aBufferSize) {
+ if (mDeactivated) {
+ // The other side might have sent a resume message before we deactivated.
+ return IPC_OK();
+ }
+
+ DispatchToTaskQueue(
+ NewRunnableMethod<ipc::SharedMemoryBasic::Handle&&, size_t>(
+ "CanvasTranslator::SetDataSurfaceBuffer", this,
+ &CanvasTranslator::SetDataSurfaceBuffer, std::move(aBufferHandle),
+ aBufferSize));
+
+ return IPC_OK();
+}
+
+void CanvasTranslator::SetDataSurfaceBuffer(
+ ipc::SharedMemoryBasic::Handle&& aBufferHandle, size_t aBufferSize) {
+ MOZ_ASSERT(IsInTaskQueue());
+ if (mHeader->readerState == State::Failed) {
+ // We failed before we got to the pause event.
+ return;
+ }
+
+ if (mHeader->readerState != State::Paused) {
+ gfxCriticalNote << "CanvasTranslator::SetDataSurfaceBuffer bad state "
+ << uint32_t(State(mHeader->readerState));
+ MOZ_DIAGNOSTIC_ASSERT(false, "mHeader->readerState == State::Paused");
+ Deactivate();
+ return;
+ }
+
+ if (!CreateAndMapShmem(mDataSurfaceShmem, std::move(aBufferHandle),
+ ipc::SharedMemory::RightsReadWrite, aBufferSize)) {
+ return;
+ }
+
+ TranslateRecording();
+}
+
+void CanvasTranslator::GetDataSurface(uint64_t aSurfaceRef) {
+ MOZ_ASSERT(IsInTaskQueue());
+
+ ReferencePtr surfaceRef = reinterpret_cast<void*>(aSurfaceRef);
+ gfx::SourceSurface* surface = LookupSourceSurface(surfaceRef);
+ if (!surface) {
+ return;
+ }
+
+ UniquePtr<gfx::DataSourceSurface::ScopedMap> map = GetPreparedMap(surfaceRef);
+ if (!map) {
+ return;
+ }
+
+ auto dstSize = surface->GetSize();
+ auto srcSize = map->GetSurface()->GetSize();
+ gfx::SurfaceFormat format = surface->GetFormat();
+ int32_t bpp = BytesPerPixel(format);
+ int32_t dataFormatWidth = dstSize.width * bpp;
+ int32_t srcStride = map->GetStride();
+ if (dataFormatWidth > srcStride || srcSize != dstSize) {
+ return;
+ }
+
+ int32_t dstStride =
+ ImageDataSerializer::ComputeRGBStride(format, dstSize.width);
+ auto requiredSize =
+ ImageDataSerializer::ComputeRGBBufferSize(dstSize, format);
+ if (requiredSize <= 0 || size_t(requiredSize) > mDataSurfaceShmem->Size()) {
+ return;
+ }
+
+ uint8_t* dst = static_cast<uint8_t*>(mDataSurfaceShmem->memory());
+ const uint8_t* src = map->GetData();
+ const uint8_t* endSrc = src + (srcSize.height * srcStride);
+ while (src < endSrc) {
+ memcpy(dst, src, dataFormatWidth);
+ src += srcStride;
+ dst += dstStride;
+ }
+}
+
+void CanvasTranslator::RecycleBuffer() {
+ mCanvasShmems.emplace(std::move(mCurrentShmem));
+ NextBuffer();
+}
+
+void CanvasTranslator::NextBuffer() {
+ // Check and signal the writer when we finish with a buffer, because it
+ // might have hit the buffer count limit and be waiting to use our old one.
+ CheckAndSignalWriter();
+
+ mCurrentShmem = std::move(mCanvasShmems.front());
+ mCanvasShmems.pop();
+ mCurrentMemReader = mCurrentShmem.CreateMemReader();
+}
+
+void CanvasTranslator::ActorDestroy(ActorDestroyReason why) {
+ MOZ_ASSERT(gfx::CanvasRenderThread::IsInCanvasRenderThread());
+
+ // Since we might need to access the actor status off the owning IPDL thread,
+ // we need to cache it here.
+ mIPDLClosed = true;
+
+ DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::ClearTextureInfo",
+ this,
+ &CanvasTranslator::ClearTextureInfo));
+
+ if (mTranslationTaskQueue) {
+ gfx::CanvasRenderThread::ShutdownWorkerTaskQueue(mTranslationTaskQueue);
+ return;
+ }
+}
+
+bool CanvasTranslator::CheckDeactivated() {
+ if (mDeactivated) {
+ return true;
+ }
+
+ if (NS_WARN_IF(!gfx::gfxVars::RemoteCanvasEnabled() &&
+ !gfx::gfxVars::UseAcceleratedCanvas2D())) {
+ Deactivate();
+ }
+
+ return mDeactivated;
+}
+
+void CanvasTranslator::Deactivate() {
+ if (mDeactivated) {
+ return;
+ }
+ mDeactivated = true;
+ if (mHeader) {
+ mHeader->readerState = State::Failed;
+ }
+
+ // 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.
+ gfx::CanvasRenderThread::Dispatch(
+ NewRunnableMethod("CanvasTranslator::SendDeactivate", this,
+ &CanvasTranslator::SendDeactivate));
+
+ // Disable remote canvas for all.
+ gfx::CanvasManagerParent::DisableRemoteCanvas();
+}
+
+inline gfx::DrawTargetWebgl* CanvasTranslator::TextureInfo::GetDrawTargetWebgl(
+ bool aCheckForFallback) const {
+ if ((!mTextureData || !aCheckForFallback) && mDrawTarget &&
+ mDrawTarget->GetBackendType() == gfx::BackendType::WEBGL) {
+ return static_cast<gfx::DrawTargetWebgl*>(mDrawTarget.get());
+ }
+ return nullptr;
+}
+
+bool CanvasTranslator::TryDrawTargetWebglFallback(
+ int64_t aTextureId, gfx::DrawTargetWebgl* aWebgl) {
+ NotifyRequiresRefresh(aTextureId);
+
+ // An existing data snapshot is required for fallback, as we have to avoid
+ // trying to touch the WebGL context, which is assumed to be invalid and not
+ // suitable for readback.
+ if (!aWebgl->HasDataSnapshot()) {
+ return false;
+ }
+
+ const auto& info = mTextureInfo[aTextureId];
+ if (RefPtr<gfx::DrawTarget> dt = CreateFallbackDrawTarget(
+ info.mRefPtr, aTextureId, info.mRemoteTextureOwnerId,
+ aWebgl->GetSize(), aWebgl->GetFormat())) {
+ aWebgl->CopyToFallback(dt);
+ AddDrawTarget(info.mRefPtr, dt);
+ return true;
+ }
+ return false;
+}
+
+void CanvasTranslator::ForceDrawTargetWebglFallback() {
+ // This looks for any DrawTargetWebgls that have a cached data snapshot that
+ // can be used to recover a fallback TextureData in the event of a context
+ // loss.
+ RemoteTextureOwnerIdSet lost;
+ for (const auto& entry : mTextureInfo) {
+ const auto& info = entry.second;
+ if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) {
+ if (!TryDrawTargetWebglFallback(entry.first, webgl)) {
+ // No fallback could be created, so we need to notify the compositor the
+ // texture won't be pushed.
+ if (mRemoteTextureOwner &&
+ mRemoteTextureOwner->IsRegistered(info.mRemoteTextureOwnerId)) {
+ lost.insert(info.mRemoteTextureOwnerId);
+ }
+ }
+ }
+ }
+ if (!lost.empty()) {
+ mRemoteTextureOwner->NotifyContextLost(&lost);
+ }
+}
+
+void CanvasTranslator::BlockCanvas() {
+ if (mDeactivated || mBlocked) {
+ return;
+ }
+ mBlocked = true;
+ gfx::CanvasRenderThread::Dispatch(
+ NewRunnableMethod("CanvasTranslator::SendBlockCanvas", this,
+ &CanvasTranslator::SendBlockCanvas));
+}
+
+void CanvasTranslator::CheckAndSignalWriter() {
+ do {
+ switch (mHeader->writerState) {
+ case State::Processing:
+ case State::Failed:
+ return;
+ case State::AboutToWait:
+ // The writer is making a decision about whether to wait. So, we must
+ // wait until it has decided to avoid races. Check if the writer is
+ // closed to avoid hangs.
+ if (mIPDLClosed) {
+ return;
+ }
+ continue;
+ case State::Waiting:
+ if (mHeader->processedCount >= mHeader->writerWaitCount) {
+ mHeader->writerState = State::Processing;
+ mWriterSemaphore->Signal();
+ }
+ return;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
+ return;
+ }
+ } while (true);
+}
+
+bool CanvasTranslator::HasPendingEvent() {
+ return mHeader->processedCount < mHeader->eventCount;
+}
+
+bool CanvasTranslator::ReadPendingEvent(EventType& aEventType) {
+ ReadElementConstrained(mCurrentMemReader, aEventType,
+ EventType::DRAWTARGETCREATION, LAST_CANVAS_EVENT_TYPE);
+ return mCurrentMemReader.good();
+}
+
+bool CanvasTranslator::ReadNextEvent(EventType& aEventType) {
+ if (mHeader->readerState == State::Paused) {
+ Flush();
+ return false;
+ }
+
+ uint32_t spinCount = mMaxSpinCount;
+ do {
+ if (HasPendingEvent()) {
+ return ReadPendingEvent(aEventType);
+ }
+ } while (--spinCount != 0);
+
+ Flush();
+ mHeader->readerState = State::AboutToWait;
+ if (HasPendingEvent()) {
+ mHeader->readerState = State::Processing;
+ return ReadPendingEvent(aEventType);
+ }
+
+ if (!mIsInTransaction) {
+ mHeader->readerState = State::Stopped;
+ return false;
+ }
+
+ // 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.
+ mHeader->readerState = State::Waiting;
+ if (mReaderSemaphore->Wait(Some(mNextEventTimeout))) {
+ MOZ_RELEASE_ASSERT(HasPendingEvent());
+ MOZ_RELEASE_ASSERT(mHeader->readerState == State::Processing);
+ return ReadPendingEvent(aEventType);
+ }
+
+ // We have to use compareExchange here because the writer can change our
+ // state if we are waiting.
+ if (!mHeader->readerState.compareExchange(State::Waiting, State::Stopped)) {
+ MOZ_RELEASE_ASSERT(HasPendingEvent());
+ MOZ_RELEASE_ASSERT(mHeader->readerState == State::Processing);
+ // The writer has just signaled us, so consume it before returning
+ MOZ_ALWAYS_TRUE(mReaderSemaphore->Wait());
+ return ReadPendingEvent(aEventType);
+ }
+
+ return false;
+}
+
+void CanvasTranslator::TranslateRecording() {
+ MOZ_ASSERT(IsInTaskQueue());
+
+ if (mSharedContext && EnsureSharedContextWebgl()) {
+ mSharedContext->EnterTlsScope();
+ }
+ auto exitTlsScope = MakeScopeExit([&] {
+ if (mSharedContext) {
+ mSharedContext->ExitTlsScope();
+ }
+ });
+
+ mHeader->readerState = State::Processing;
+ EventType eventType = EventType::INVALID;
+ while (ReadNextEvent(eventType)) {
+ bool success = RecordedEvent::DoWithEventFromReader(
+ mCurrentMemReader, eventType,
+ [&](RecordedEvent* recordedEvent) -> bool {
+ // Make sure that the whole event was read from the stream.
+ if (!mCurrentMemReader.good()) {
+ if (mIPDLClosed) {
+ // 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();
+ }
+ mHeader->readerState = State::Failed;
+ return false;
+ }
+
+ return recordedEvent->PlayEvent(this);
+ });
+
+ // Check the stream is good here or we will log the issue twice.
+ if (!mCurrentMemReader.good()) {
+ return;
+ }
+
+ 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;
+ }
+ mHeader->readerState = State::Failed;
+ }
+
+ mHeader->processedCount++;
+ }
+}
+
+#define READ_AND_PLAY_CANVAS_EVENT_TYPE(_typeenum, _class) \
+ case _typeenum: { \
+ auto e = _class(mCurrentMemReader); \
+ if (!mCurrentMemReader.good()) { \
+ if (mIPDLClosed) { \
+ /* The other side has closed only warn about read failure. */ \
+ gfxWarning() << "Failed to read event type: " << _typeenum; \
+ } else { \
+ gfxCriticalNote << "Failed to read event type: " << _typeenum; \
+ } \
+ return false; \
+ } \
+ return e.PlayCanvasEvent(this); \
+ }
+
+bool CanvasTranslator::HandleExtensionEvent(int32_t aType) {
+ // This is where we handle extensions to the Moz2D Recording events to handle
+ // canvas specific things.
+ switch (aType) {
+ FOR_EACH_CANVAS_EVENT(READ_AND_PLAY_CANVAS_EVENT_TYPE)
+ default:
+ return false;
+ }
+}
+
+void CanvasTranslator::BeginTransaction() { mIsInTransaction = true; }
+
+void CanvasTranslator::Flush() {
+#if defined(XP_WIN)
+ // We can end up without a device, due to a reset and failure to re-create.
+ if (!mDevice) {
+ return;
+ }
+
+ gfx::AutoSerializeWithMoz2D serializeWithMoz2D(mBackendType);
+ RefPtr<ID3D11DeviceContext> deviceContext;
+ mDevice->GetImmediateContext(getter_AddRefs(deviceContext));
+ deviceContext->Flush();
+#endif
+}
+
+void CanvasTranslator::EndTransaction() {
+ Flush();
+ // At the end of a transaction is a good time to check if a new canvas device
+ // has been created, even if a reset did not occur.
+ Unused << CheckForFreshCanvasDevice(__LINE__);
+ mIsInTransaction = false;
+}
+
+void CanvasTranslator::DeviceChangeAcknowledged() {
+ mDeviceResetInProgress = false;
+ if (mRemoteTextureOwner) {
+ mRemoteTextureOwner->NotifyContextRestored();
+ }
+}
+
+bool CanvasTranslator::CreateReferenceTexture() {
+ if (mReferenceTextureData) {
+ mReferenceTextureData->Unlock();
+ }
+
+ mReferenceTextureData =
+ CreateTextureData(gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8, true);
+ if (!mReferenceTextureData) {
+ Deactivate();
+ return false;
+ }
+
+ if (NS_WARN_IF(!mReferenceTextureData->Lock(OpenMode::OPEN_READ_WRITE))) {
+ gfxCriticalNote << "CanvasTranslator::CreateReferenceTexture lock failed";
+ mReferenceTextureData.reset();
+ Deactivate();
+ return false;
+ }
+
+ mBaseDT = mReferenceTextureData->BorrowDrawTarget();
+
+ if (!mBaseDT) {
+ // We might get a null draw target due to a device failure, deactivate and
+ // return false so that we can recover.
+ Deactivate();
+ return false;
+ }
+
+ return true;
+}
+
+bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) {
+ // If not on D3D11, we are not dependent on a fresh device for DT creation if
+ // one already exists.
+ if (mBaseDT && mTextureType != TextureType::D3D11) {
+ return false;
+ }
+
+#if defined(XP_WIN)
+ // If a new device has already been created, use that one.
+ RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetCanvasDevice();
+ if (device && device != mDevice) {
+ if (mDevice) {
+ // We already had a device, notify child of change.
+ NotifyDeviceChanged();
+ }
+ mDevice = device.forget();
+ return CreateReferenceTexture();
+ }
+
+ if (mDevice) {
+ if (mDevice->GetDeviceRemovedReason() == S_OK) {
+ return false;
+ }
+
+ gfxCriticalNote << "GFX: CanvasTranslator detected a device reset at "
+ << aLineNumber;
+ NotifyDeviceChanged();
+ }
+
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+ "CanvasTranslator NotifyDeviceReset",
+ []() { gfx::GPUParent::GetSingleton()->NotifyDeviceReset(); });
+
+ // It is safe to wait here because only the Compositor thread waits on us and
+ // the main thread doesn't wait on the compositor thread in the GPU process.
+ SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(), runnable,
+ /*aForceDispatch*/ true);
+
+ mDevice = gfx::DeviceManagerDx::Get()->GetCanvasDevice();
+ if (!mDevice) {
+ // We don't have a canvas device, we need to deactivate.
+ Telemetry::ScalarAdd(
+ Telemetry::ScalarID::GFX_CANVAS_REMOTE_DEACTIVATED_NO_DEVICE, 1);
+ Deactivate();
+ return false;
+ }
+#endif
+
+ return CreateReferenceTexture();
+}
+
+void CanvasTranslator::NotifyDeviceChanged() {
+ // Clear out any old recycled texture datas with the wrong device.
+ if (mRemoteTextureOwner) {
+ mRemoteTextureOwner->NotifyContextLost();
+ mRemoteTextureOwner->ClearRecycledTextures();
+ }
+ mDeviceResetInProgress = true;
+ gfx::CanvasRenderThread::Dispatch(
+ NewRunnableMethod("CanvasTranslator::SendNotifyDeviceChanged", this,
+ &CanvasTranslator::SendNotifyDeviceChanged));
+}
+
+gfx::DrawTargetWebgl* CanvasTranslator::GetDrawTargetWebgl(
+ int64_t aTextureId, bool aCheckForFallback) const {
+ auto result = mTextureInfo.find(aTextureId);
+ if (result != mTextureInfo.end()) {
+ return result->second.GetDrawTargetWebgl(aCheckForFallback);
+ }
+ return nullptr;
+}
+
+void CanvasTranslator::NotifyRequiresRefresh(int64_t aTextureId,
+ bool aDispatch) {
+ if (aDispatch) {
+ auto& info = mTextureInfo[aTextureId];
+ if (!info.mNotifiedRequiresRefresh) {
+ info.mNotifiedRequiresRefresh = true;
+ DispatchToTaskQueue(NewRunnableMethod<int64_t, bool>(
+ "CanvasTranslator::NotifyRequiresRefresh", this,
+ &CanvasTranslator::NotifyRequiresRefresh, aTextureId, false));
+ }
+ return;
+ }
+
+ if (mTextureInfo.find(aTextureId) != mTextureInfo.end()) {
+ Unused << SendNotifyRequiresRefresh(aTextureId);
+ }
+}
+
+void CanvasTranslator::CacheSnapshotShmem(int64_t aTextureId, bool aDispatch) {
+ if (aDispatch) {
+ DispatchToTaskQueue(NewRunnableMethod<int64_t, bool>(
+ "CanvasTranslator::CacheSnapshotShmem", this,
+ &CanvasTranslator::CacheSnapshotShmem, aTextureId, false));
+ return;
+ }
+
+ if (gfx::DrawTargetWebgl* webgl = GetDrawTargetWebgl(aTextureId)) {
+ if (auto shmemHandle = webgl->TakeShmemHandle()) {
+ // Lock the DT so that it doesn't get removed while shmem is in transit.
+ mTextureInfo[aTextureId].mLocked++;
+ nsCOMPtr<nsIThread> thread =
+ gfx::CanvasRenderThread::GetCanvasRenderThread();
+ RefPtr<CanvasTranslator> translator = this;
+ SendSnapshotShmem(aTextureId, std::move(shmemHandle),
+ webgl->GetShmemSize())
+ ->Then(
+ thread, __func__,
+ [=](bool) { translator->RemoveTexture(aTextureId); },
+ [=](ipc::ResponseRejectReason) {
+ translator->RemoveTexture(aTextureId);
+ });
+ }
+ }
+}
+
+void CanvasTranslator::PrepareShmem(int64_t aTextureId) {
+ if (gfx::DrawTargetWebgl* webgl = GetDrawTargetWebgl(aTextureId, false)) {
+ if (const auto& fallback = mTextureInfo[aTextureId].mTextureData) {
+ // If there was a fallback, copy the fallback to the software framebuffer
+ // shmem for reading.
+ if (RefPtr<gfx::DrawTarget> dt = fallback->BorrowDrawTarget()) {
+ if (RefPtr<gfx::SourceSurface> snapshot = dt->Snapshot()) {
+ webgl->CopySurface(snapshot, snapshot->GetRect(),
+ gfx::IntPoint(0, 0));
+ }
+ }
+ } else {
+ // Otherwise, just ensure the software framebuffer is up to date.
+ webgl->PrepareShmem();
+ }
+ }
+}
+
+void CanvasTranslator::ClearCachedResources() {
+ if (mSharedContext) {
+ // If there are any DrawTargetWebgls, then try to cache their framebuffers
+ // in software surfaces, just in case the GL context is lost. So long as
+ // there is a software copy of the framebuffer, it can be copied into a
+ // fallback TextureData later even if the GL context goes away.
+ mSharedContext->OnMemoryPressure();
+ for (auto const& entry : mTextureInfo) {
+ if (gfx::DrawTargetWebgl* webgl = entry.second.GetDrawTargetWebgl()) {
+ webgl->EnsureDataSnapshot();
+ }
+ }
+ }
+}
+
+ipc::IPCResult CanvasTranslator::RecvClearCachedResources() {
+ if (mDeactivated) {
+ // The other side might have sent a message before we deactivated.
+ return IPC_OK();
+ }
+
+ DispatchToTaskQueue(
+ NewRunnableMethod("CanvasTranslator::ClearCachedResources", this,
+ &CanvasTranslator::ClearCachedResources));
+ return IPC_OK();
+}
+
+static const OpenMode kInitMode = OpenMode::OPEN_READ_WRITE;
+
+already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateFallbackDrawTarget(
+ gfx::ReferencePtr aRefPtr, int64_t aTextureId,
+ RemoteTextureOwnerId aTextureOwnerId, const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat) {
+ RefPtr<gfx::DrawTarget> dt;
+ do {
+ UniquePtr<TextureData> textureData =
+ CreateOrRecycleTextureData(aSize, aFormat);
+ if (NS_WARN_IF(!textureData)) {
+ continue;
+ }
+
+ if (NS_WARN_IF(!textureData->Lock(kInitMode))) {
+ gfxCriticalNote << "CanvasTranslator::CreateDrawTarget lock failed";
+ continue;
+ }
+
+ dt = textureData->BorrowDrawTarget();
+ if (NS_WARN_IF(!dt)) {
+ textureData->Unlock();
+ continue;
+ }
+ // Recycled buffer contents may be uninitialized.
+ dt->ClearRect(gfx::Rect(dt->GetRect()));
+
+ TextureInfo& info = mTextureInfo[aTextureId];
+ info.mRefPtr = aRefPtr;
+ info.mTextureData = std::move(textureData);
+ info.mRemoteTextureOwnerId = aTextureOwnerId;
+ info.mTextureLockMode = kInitMode;
+ } while (!dt && CheckForFreshCanvasDevice(__LINE__));
+ return dt.forget();
+}
+
+already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget(
+ gfx::ReferencePtr aRefPtr, int64_t aTextureId,
+ RemoteTextureOwnerId aTextureOwnerId, const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat) {
+ if (aTextureId < 0) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "No texture ID set");
+ return nullptr;
+ }
+
+ if (!aTextureOwnerId.IsValid()) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "No texture owner set");
+ return nullptr;
+ }
+
+ RefPtr<gfx::DrawTarget> dt;
+ if (gfx::gfxVars::UseAcceleratedCanvas2D()) {
+ if (EnsureSharedContextWebgl()) {
+ mSharedContext->EnterTlsScope();
+ }
+ if (RefPtr<gfx::DrawTargetWebgl> webgl =
+ gfx::DrawTargetWebgl::Create(aSize, aFormat, mSharedContext)) {
+ webgl->BeginFrame(true);
+ dt = webgl.forget().downcast<gfx::DrawTarget>();
+ if (dt) {
+ TextureInfo& info = mTextureInfo[aTextureId];
+ info.mRefPtr = aRefPtr;
+ info.mDrawTarget = dt;
+ info.mRemoteTextureOwnerId = aTextureOwnerId;
+ info.mTextureLockMode = kInitMode;
+ CacheSnapshotShmem(aTextureId);
+ }
+ }
+ if (!dt) {
+ NotifyRequiresRefresh(aTextureId);
+ }
+ }
+
+ if (!dt) {
+ dt = CreateFallbackDrawTarget(aRefPtr, aTextureId, aTextureOwnerId, aSize,
+ aFormat);
+ }
+
+ AddDrawTarget(aRefPtr, dt);
+ return dt.forget();
+}
+
+already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget(
+ gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Unexpected CreateDrawTarget call!");
+ return nullptr;
+}
+
+void CanvasTranslator::RemoveTexture(int64_t aTextureId,
+ RemoteTextureTxnType aTxnType,
+ RemoteTextureTxnId aTxnId) {
+ // Don't erase the texture if still in use
+ auto result = mTextureInfo.find(aTextureId);
+ if (result == mTextureInfo.end()) {
+ return;
+ }
+ auto& info = result->second;
+ if (mRemoteTextureOwner && aTxnType && aTxnId) {
+ mRemoteTextureOwner->WaitForTxn(info.mRemoteTextureOwnerId, aTxnType,
+ aTxnId);
+ }
+ if (--info.mLocked > 0) {
+ return;
+ }
+ if (info.mTextureData) {
+ info.mTextureData->Unlock();
+ }
+ if (mRemoteTextureOwner) {
+ // If this texture id was manually registered as a remote texture owner,
+ // unregister it so it does not stick around after the texture id goes away.
+ RemoteTextureOwnerId owner = info.mRemoteTextureOwnerId;
+ if (owner.IsValid()) {
+ mRemoteTextureOwner->UnregisterTextureOwner(owner);
+ }
+ }
+ mTextureInfo.erase(result);
+}
+
+bool CanvasTranslator::LockTexture(int64_t aTextureId, OpenMode aMode,
+ bool aInvalidContents) {
+ if (aMode == OpenMode::OPEN_NONE) {
+ return false;
+ }
+ auto result = mTextureInfo.find(aTextureId);
+ if (result == mTextureInfo.end()) {
+ return false;
+ }
+ auto& info = result->second;
+ if (info.mTextureLockMode != OpenMode::OPEN_NONE) {
+ return (info.mTextureLockMode & aMode) == aMode;
+ }
+ if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) {
+ if (aMode & OpenMode::OPEN_WRITE) {
+ webgl->BeginFrame(aInvalidContents);
+ }
+ }
+ info.mTextureLockMode = aMode;
+ return true;
+}
+
+bool CanvasTranslator::UnlockTexture(int64_t aTextureId) {
+ auto result = mTextureInfo.find(aTextureId);
+ if (result == mTextureInfo.end()) {
+ return false;
+ }
+ auto& info = result->second;
+ if (info.mTextureLockMode == OpenMode::OPEN_NONE) {
+ return false;
+ }
+
+ if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) {
+ if (info.mTextureLockMode & OpenMode::OPEN_WRITE) {
+ webgl->EndFrame();
+ if (webgl->RequiresRefresh()) {
+ NotifyRequiresRefresh(aTextureId);
+ }
+ }
+ }
+ info.mTextureLockMode = OpenMode::OPEN_NONE;
+ return true;
+}
+
+bool CanvasTranslator::PresentTexture(int64_t aTextureId, RemoteTextureId aId) {
+ auto result = mTextureInfo.find(aTextureId);
+ if (result == mTextureInfo.end()) {
+ return false;
+ }
+ auto& info = result->second;
+ RemoteTextureOwnerId ownerId = info.mRemoteTextureOwnerId;
+ if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) {
+ EnsureRemoteTextureOwner(ownerId);
+ if (webgl->CopyToSwapChain(aId, ownerId, mRemoteTextureOwner)) {
+ return true;
+ }
+ if (mSharedContext && mSharedContext->IsContextLost()) {
+ // If the context was lost, try to create a fallback to push instead.
+ EnsureSharedContextWebgl();
+ } else {
+ // CopyToSwapChain failed for an unknown reason other than context loss.
+ // Try to read into fallback data if possible to recover, otherwise force
+ // the loss of the individual texture.
+ webgl->EnsureDataSnapshot();
+ if (!TryDrawTargetWebglFallback(aTextureId, webgl)) {
+ RemoteTextureOwnerIdSet lost = {info.mRemoteTextureOwnerId};
+ mRemoteTextureOwner->NotifyContextLost(&lost);
+ }
+ }
+ }
+ if (TextureData* data = info.mTextureData.get()) {
+ PushRemoteTexture(aTextureId, data, aId, ownerId);
+ }
+ return true;
+}
+
+void CanvasTranslator::EnsureRemoteTextureOwner(RemoteTextureOwnerId aOwnerId) {
+ if (!mRemoteTextureOwner) {
+ mRemoteTextureOwner = new RemoteTextureOwnerClient(mOtherPid);
+ }
+ if (aOwnerId.IsValid() && !mRemoteTextureOwner->IsRegistered(aOwnerId)) {
+ mRemoteTextureOwner->RegisterTextureOwner(aOwnerId,
+ /* aSharedRecycling */ true);
+ }
+}
+
+UniquePtr<TextureData> CanvasTranslator::CreateOrRecycleTextureData(
+ const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) {
+ if (mRemoteTextureOwner) {
+ if (mTextureType == TextureType::Unknown) {
+ return mRemoteTextureOwner->CreateOrRecycleBufferTextureData(aSize,
+ aFormat);
+ }
+ if (UniquePtr<TextureData> data =
+ mRemoteTextureOwner->GetRecycledTextureData(aSize, aFormat,
+ mTextureType)) {
+ return data;
+ }
+ }
+ return CreateTextureData(aSize, aFormat, false);
+}
+
+bool CanvasTranslator::PushRemoteTexture(int64_t aTextureId, TextureData* aData,
+ RemoteTextureId aId,
+ RemoteTextureOwnerId aOwnerId) {
+ EnsureRemoteTextureOwner(aOwnerId);
+ UniquePtr<TextureData> dstData;
+ if (!mDeviceResetInProgress) {
+ TextureData::Info info;
+ aData->FillInfo(info);
+ dstData = CreateOrRecycleTextureData(info.size, info.format);
+ }
+ bool success = false;
+ // Source data is already locked.
+ if (dstData) {
+ if (dstData->Lock(OpenMode::OPEN_WRITE)) {
+ if (RefPtr<gfx::DrawTarget> dstDT = dstData->BorrowDrawTarget()) {
+ if (RefPtr<gfx::DrawTarget> srcDT = aData->BorrowDrawTarget()) {
+ if (RefPtr<gfx::SourceSurface> snapshot = srcDT->Snapshot()) {
+ dstDT->CopySurface(snapshot, snapshot->GetRect(),
+ gfx::IntPoint(0, 0));
+ dstDT->Flush();
+ success = true;
+ }
+ }
+ }
+ dstData->Unlock();
+ } else {
+ gfxCriticalNote << "CanvasTranslator::PushRemoteTexture dst lock failed";
+ }
+ }
+ if (success) {
+ mRemoteTextureOwner->PushTexture(aId, aOwnerId, std::move(dstData));
+ } else {
+ mRemoteTextureOwner->PushDummyTexture(aId, aOwnerId);
+ }
+ return success;
+}
+
+void CanvasTranslator::ClearTextureInfo() {
+ for (auto const& entry : mTextureInfo) {
+ if (entry.second.mTextureData) {
+ entry.second.mTextureData->Unlock();
+ }
+ }
+ mTextureInfo.clear();
+ mDrawTargets.Clear();
+ mSharedContext = nullptr;
+ mBaseDT = nullptr;
+ if (mReferenceTextureData) {
+ mReferenceTextureData->Unlock();
+ }
+ if (mRemoteTextureOwner) {
+ mRemoteTextureOwner->UnregisterAllTextureOwners();
+ mRemoteTextureOwner = nullptr;
+ }
+ if (mTranslationTaskQueue) {
+ gfx::CanvasRenderThread::FinishShutdownWorkerTaskQueue(
+ mTranslationTaskQueue);
+ }
+}
+
+already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface(
+ uint64_t aKey) {
+ return mSharedSurfacesHolder->Get(wr::ToExternalImageId(aKey));
+}
+
+void CanvasTranslator::CheckpointReached() { CheckAndSignalWriter(); }
+
+void CanvasTranslator::PauseTranslation() {
+ mHeader->readerState = State::Paused;
+}
+
+already_AddRefed<gfx::GradientStops> CanvasTranslator::GetOrCreateGradientStops(
+ gfx::DrawTarget* aDrawTarget, gfx::GradientStop* aRawStops,
+ uint32_t aNumStops, gfx::ExtendMode aExtendMode) {
+ MOZ_ASSERT(aDrawTarget);
+ nsTArray<gfx::GradientStop> rawStopArray(aRawStops, aNumStops);
+ return gfx::gfxGradientCache::GetOrCreateGradientStops(
+ aDrawTarget, rawStopArray, aExtendMode);
+}
+
+gfx::DataSourceSurface* CanvasTranslator::LookupDataSurface(
+ gfx::ReferencePtr aRefPtr) {
+ return mDataSurfaces.GetWeak(aRefPtr);
+}
+
+void CanvasTranslator::AddDataSurface(
+ gfx::ReferencePtr aRefPtr, RefPtr<gfx::DataSourceSurface>&& aSurface) {
+ mDataSurfaces.InsertOrUpdate(aRefPtr, std::move(aSurface));
+}
+
+void CanvasTranslator::RemoveDataSurface(gfx::ReferencePtr aRefPtr) {
+ mDataSurfaces.Remove(aRefPtr);
+}
+
+void CanvasTranslator::SetPreparedMap(
+ gfx::ReferencePtr aSurface,
+ UniquePtr<gfx::DataSourceSurface::ScopedMap> aMap) {
+ mMappedSurface = aSurface;
+ mPreparedMap = std::move(aMap);
+}
+
+UniquePtr<gfx::DataSourceSurface::ScopedMap> CanvasTranslator::GetPreparedMap(
+ gfx::ReferencePtr aSurface) {
+ if (!mPreparedMap) {
+ // We might fail to set the map during, for example, device resets.
+ return nullptr;
+ }
+
+ MOZ_RELEASE_ASSERT(mMappedSurface == aSurface,
+ "aSurface must match previously stored surface.");
+
+ mMappedSurface = nullptr;
+ return std::move(mPreparedMap);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CanvasTranslator.h b/gfx/layers/ipc/CanvasTranslator.h
new file mode 100644
index 0000000000..cf87b7ab53
--- /dev/null
+++ b/gfx/layers/ipc/CanvasTranslator.h
@@ -0,0 +1,395 @@
+/* -*- 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/dom/ipc/IdType.h"
+#include "mozilla/gfx/InlineTranslator.h"
+#include "mozilla/gfx/RecordedEvent.h"
+#include "CanvasChild.h"
+#include "mozilla/layers/CanvasDrawEventRecorder.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/PCanvasParent.h"
+#include "mozilla/layers/RemoteTextureMap.h"
+#include "mozilla/ipc/CrossProcessSemaphore.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+
+using EventType = gfx::RecordedEvent::EventType;
+class TaskQueue;
+
+namespace gfx {
+class DrawTargetWebgl;
+class SharedContextWebgl;
+} // namespace gfx
+
+namespace layers {
+
+class SharedSurfacesHolder;
+class TextureData;
+
+class CanvasTranslator final : public gfx::InlineTranslator,
+ public PCanvasParent {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasTranslator)
+
+ friend class PProtocolParent;
+
+ CanvasTranslator(layers::SharedSurfacesHolder* aSharedSurfacesHolder,
+ const dom::ContentParentId& aContentId, uint32_t aManagerId);
+
+ const dom::ContentParentId& GetContentId() const { return mContentId; }
+
+ uint32_t GetManagerId() const { return mManagerId; }
+
+ /**
+ * Dispatches a runnable to the preferred task queue or thread.
+ *
+ * @param aRunnable the runnable to dispatch
+ */
+ void DispatchToTaskQueue(already_AddRefed<nsIRunnable> aRunnable);
+
+ /**
+ * @returns true if running in the preferred task queue or thread for
+ * translation.
+ */
+ bool IsInTaskQueue() const;
+
+ /**
+ * Initialize the canvas translator for a particular TextureType and
+ * CanvasEventRingBuffer.
+ *
+ * @param aTextureType the TextureType the translator will create
+ * @param aBackendType the BackendType for texture data
+ * @param aHeaderHandle handle for the control header
+ * @param aBufferHandles handles for the initial buffers for translation
+ * @param aBufferSize size of buffers and the default size
+ * @param aReaderSem reading blocked semaphore for the CanvasEventRingBuffer
+ * @param aWriterSem writing blocked semaphore for the CanvasEventRingBuffer
+ */
+ ipc::IPCResult RecvInitTranslator(TextureType aTextureType,
+ gfx::BackendType aBackendType,
+ Handle&& aReadHandle,
+ nsTArray<Handle>&& aBufferHandles,
+ uint64_t aBufferSize,
+ CrossProcessSemaphoreHandle&& aReaderSem,
+ CrossProcessSemaphoreHandle&& aWriterSem);
+
+ /**
+ * Restart the translation from a Stopped state.
+ */
+ ipc::IPCResult RecvRestartTranslation();
+
+ /**
+ * Adds a new buffer to be translated. The current buffer will be recycled if
+ * it is of the default size. The translation will then be restarted.
+ */
+ ipc::IPCResult RecvAddBuffer(Handle&& aBufferHandle, uint64_t aBufferSize);
+
+ /**
+ * Sets the shared memory to be used for readback.
+ */
+ ipc::IPCResult RecvSetDataSurfaceBuffer(Handle&& aBufferHandle,
+ uint64_t aBufferSize);
+
+ ipc::IPCResult RecvClearCachedResources();
+
+ void ActorDestroy(ActorDestroyReason why) final;
+
+ void CheckAndSignalWriter();
+
+ /**
+ * 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.
+ */
+ void 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 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 aTextureId texture ID for this DrawTarget
+ * @param aTextureOwnerId texture owner ID for this DrawTarget
+ * @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, int64_t aTextureId,
+ RemoteTextureOwnerId aTextureOwnerId, const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat);
+
+ already_AddRefed<gfx::DrawTarget> CreateDrawTarget(
+ gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat) final;
+
+ already_AddRefed<gfx::GradientStops> GetOrCreateGradientStops(
+ gfx::DrawTarget* aDrawTarget, gfx::GradientStop* aRawStops,
+ uint32_t aNumStops, gfx::ExtendMode aExtendMode) final;
+
+ void CheckpointReached();
+
+ void PauseTranslation();
+
+ /**
+ * Removes the texture and other objects associated with a texture ID.
+ *
+ * @param aTextureId the texture ID to remove
+ */
+ void RemoveTexture(int64_t aTextureId, RemoteTextureTxnType aTxnType = 0,
+ RemoteTextureTxnId aTxnId = 0);
+
+ bool LockTexture(int64_t aTextureId, OpenMode aMode,
+ bool aInvalidContents = false);
+ bool UnlockTexture(int64_t aTextureId);
+
+ bool PresentTexture(int64_t aTextureId, RemoteTextureId aId);
+
+ bool PushRemoteTexture(int64_t aTextureId, TextureData* aData,
+ RemoteTextureId aId, RemoteTextureOwnerId aOwnerId);
+
+ /**
+ * Overriden to remove any DataSourceSurfaces associated with the RefPtr.
+ *
+ * @param aRefPtr the key to the surface
+ * @param aSurface the surface to store
+ */
+ void AddSourceSurface(gfx::ReferencePtr aRefPtr,
+ gfx::SourceSurface* aSurface) final {
+ if (mMappedSurface == aRefPtr) {
+ mPreparedMap = nullptr;
+ mMappedSurface = nullptr;
+ }
+ RemoveDataSurface(aRefPtr);
+ InlineTranslator::AddSourceSurface(aRefPtr, aSurface);
+ }
+
+ /**
+ * Removes the SourceSurface and other objects associated with a SourceSurface
+ * from another process.
+ *
+ * @param aRefPtr the key to the objects to remove
+ */
+ void RemoveSourceSurface(gfx::ReferencePtr aRefPtr) final {
+ if (mMappedSurface == aRefPtr) {
+ mPreparedMap = nullptr;
+ mMappedSurface = nullptr;
+ }
+ RemoveDataSurface(aRefPtr);
+ InlineTranslator::RemoveSourceSurface(aRefPtr);
+ }
+
+ already_AddRefed<gfx::SourceSurface> LookupExternalSurface(
+ uint64_t aKey) final;
+
+ /**
+ * Gets the cached DataSourceSurface, if it exists, associated with a
+ * SourceSurface from another process.
+ *
+ * @param aRefPtr the key used to find the DataSourceSurface
+ * @returns the DataSourceSurface or nullptr if not found
+ */
+ gfx::DataSourceSurface* LookupDataSurface(gfx::ReferencePtr aRefPtr);
+
+ /**
+ * Used to cache the DataSourceSurface from a SourceSurface associated with a
+ * SourceSurface from another process. This is to improve performance if we
+ * require the data for that SourceSurface.
+ *
+ * @param aRefPtr the key used to store the DataSourceSurface
+ * @param aSurface the DataSourceSurface to store
+ */
+ void AddDataSurface(gfx::ReferencePtr aRefPtr,
+ RefPtr<gfx::DataSourceSurface>&& aSurface);
+
+ /**
+ * Gets the cached DataSourceSurface, if it exists, associated with a
+ * SourceSurface from another process.
+ *
+ * @param aRefPtr the key used to find the DataSourceSurface
+ * @returns the DataSourceSurface or nullptr if not found
+ */
+ void RemoveDataSurface(gfx::ReferencePtr aRefPtr);
+
+ /**
+ * Sets a ScopedMap, to be used in a later event.
+ *
+ * @param aSurface the associated surface in the other process
+ * @param aMap the ScopedMap to store
+ */
+ void SetPreparedMap(gfx::ReferencePtr aSurface,
+ UniquePtr<gfx::DataSourceSurface::ScopedMap> aMap);
+
+ /**
+ * Gets the ScopedMap stored using SetPreparedMap.
+ *
+ * @param aSurface must match the surface from the SetPreparedMap call
+ * @returns the ScopedMap if aSurface matches otherwise nullptr
+ */
+ UniquePtr<gfx::DataSourceSurface::ScopedMap> GetPreparedMap(
+ gfx::ReferencePtr aSurface);
+
+ void PrepareShmem(int64_t aTextureId);
+
+ void RecycleBuffer();
+
+ void NextBuffer();
+
+ void GetDataSurface(uint64_t aSurfaceRef);
+
+ private:
+ ~CanvasTranslator();
+
+ void AddBuffer(Handle&& aBufferHandle, size_t aBufferSize);
+
+ void SetDataSurfaceBuffer(Handle&& aBufferHandle, size_t aBufferSize);
+
+ bool ReadNextEvent(EventType& aEventType);
+
+ bool HasPendingEvent();
+
+ bool ReadPendingEvent(EventType& aEventType);
+
+ bool CheckDeactivated();
+
+ void Deactivate();
+
+ bool TryDrawTargetWebglFallback(int64_t aTextureId,
+ gfx::DrawTargetWebgl* aWebgl);
+ void ForceDrawTargetWebglFallback();
+
+ void BlockCanvas();
+
+ UniquePtr<TextureData> CreateTextureData(const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat,
+ bool aClear);
+
+ void EnsureRemoteTextureOwner(
+ RemoteTextureOwnerId aOwnerId = RemoteTextureOwnerId());
+
+ UniquePtr<TextureData> CreateOrRecycleTextureData(const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat);
+
+ already_AddRefed<gfx::DrawTarget> CreateFallbackDrawTarget(
+ gfx::ReferencePtr aRefPtr, int64_t aTextureId,
+ RemoteTextureOwnerId aTextureOwnerId, const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat);
+
+ void ClearTextureInfo();
+
+ bool HandleExtensionEvent(int32_t aType);
+
+ bool CreateReferenceTexture();
+ bool CheckForFreshCanvasDevice(int aLineNumber);
+ void NotifyDeviceChanged();
+
+ bool EnsureSharedContextWebgl();
+ gfx::DrawTargetWebgl* GetDrawTargetWebgl(int64_t aTextureId,
+ bool aCheckForFallback = true) const;
+ void NotifyRequiresRefresh(int64_t aTextureId, bool aDispatch = true);
+ void CacheSnapshotShmem(int64_t aTextureId, bool aDispatch = true);
+
+ void ClearCachedResources();
+
+ const RefPtr<TaskQueue> mTranslationTaskQueue;
+ const RefPtr<SharedSurfacesHolder> mSharedSurfacesHolder;
+#if defined(XP_WIN)
+ RefPtr<ID3D11Device> mDevice;
+#endif
+ RefPtr<gfx::SharedContextWebgl> mSharedContext;
+ RefPtr<RemoteTextureOwnerClient> mRemoteTextureOwner;
+
+ size_t mDefaultBufferSize = 0;
+ uint32_t mMaxSpinCount;
+ TimeDuration mNextEventTimeout;
+
+ using State = CanvasDrawEventRecorder::State;
+ using Header = CanvasDrawEventRecorder::Header;
+
+ RefPtr<ipc::SharedMemoryBasic> mHeaderShmem;
+ Header* mHeader = nullptr;
+
+ struct CanvasShmem {
+ RefPtr<ipc::SharedMemoryBasic> shmem;
+ auto Size() { return shmem->Size(); }
+ gfx::MemReader CreateMemReader() {
+ return {static_cast<char*>(shmem->memory()), Size()};
+ }
+ };
+ std::queue<CanvasShmem> mCanvasShmems;
+ CanvasShmem mCurrentShmem;
+ gfx::MemReader mCurrentMemReader{0, 0};
+ RefPtr<ipc::SharedMemoryBasic> mDataSurfaceShmem;
+ UniquePtr<CrossProcessSemaphore> mWriterSemaphore;
+ UniquePtr<CrossProcessSemaphore> mReaderSemaphore;
+ TextureType mTextureType = TextureType::Unknown;
+ UniquePtr<TextureData> mReferenceTextureData;
+ dom::ContentParentId mContentId;
+ uint32_t mManagerId;
+ // Sometimes during device reset our reference DrawTarget can be null, so we
+ // hold the BackendType separately.
+ gfx::BackendType mBackendType = gfx::BackendType::NONE;
+ base::ProcessId mOtherPid = base::kInvalidProcessId;
+ struct TextureInfo {
+ gfx::ReferencePtr mRefPtr;
+ UniquePtr<TextureData> mTextureData;
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ RemoteTextureOwnerId mRemoteTextureOwnerId;
+ bool mNotifiedRequiresRefresh = false;
+ // Ref-count of how active uses of the DT. Avoids deletion when locked.
+ int32_t mLocked = 1;
+ OpenMode mTextureLockMode = OpenMode::OPEN_NONE;
+
+ gfx::DrawTargetWebgl* GetDrawTargetWebgl(
+ bool aCheckForFallback = true) const;
+ };
+ std::unordered_map<int64_t, TextureInfo> mTextureInfo;
+ nsRefPtrHashtable<nsPtrHashKey<void>, gfx::DataSourceSurface> mDataSurfaces;
+ gfx::ReferencePtr mMappedSurface;
+ UniquePtr<gfx::DataSourceSurface::ScopedMap> mPreparedMap;
+ Atomic<bool> mDeactivated{false};
+ Atomic<bool> mBlocked{false};
+ Atomic<bool> mIPDLClosed{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..ba4bb41b5d
--- /dev/null
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -0,0 +1,193 @@
+/* -*- 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/Atomics.h"
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol, ProtocolID
+#include "mozilla/layers/KnowsCompositor.h" // for KnowsCompositor
+#include "nsRect.h" // for nsIntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+namespace layers {
+class CompositableClient;
+class CompositableHandle;
+class ImageContainer;
+class PTextureChild;
+class SurfaceDescriptorTiles;
+class TextureClient;
+
+/**
+ * FwdTransactionCounter issues forwarder transaction numbers that represent a
+ * sequential stream of transactions to be transported over IPDL. Since every
+ * top-level protocol represents its own independently-sequenced IPDL queue,
+ * transaction numbers most naturally align with top-level protocols, rather
+ * than have different sub-protocols with their own independent transaction
+ * numbers that can't be usefully sequenced. FwdTransactionCounter expects
+ * users of it to provide themselves as proof they are a top-level protocol
+ * to avoid issues.
+ */
+struct FwdTransactionCounter {
+ explicit FwdTransactionCounter(mozilla::ipc::IToplevelProtocol* aToplevel)
+ : mFwdTransactionType(aToplevel->GetProtocolId()) {}
+
+ /**
+ * The ID of the top-level protocol. This is useful to tag the source of
+ * the transaction numbers in case different sources must be disambiguated.
+ */
+ mozilla::ipc::ProtocolId mFwdTransactionType;
+
+ /**
+ * Transaction id of ShadowLayerForwarder.
+ * It is incremented by UpdateFwdTransactionId() in each BeginTransaction()
+ * call.
+ */
+ uint64_t mFwdTransactionId = 0;
+};
+
+/**
+ * FwdTransactionTracker is to be used by CompositableForwarder consumers that
+ * must remember the last transaction in which they were used.
+ */
+class FwdTransactionTracker {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FwdTransactionTracker)
+
+ static already_AddRefed<FwdTransactionTracker> GetOrCreate(
+ RefPtr<FwdTransactionTracker>& aTracker) {
+ if (!aTracker) {
+ aTracker = new FwdTransactionTracker;
+ }
+ return do_AddRef(aTracker);
+ }
+
+ bool IsUsed() const { return mFwdTransactionType && mFwdTransactionId; }
+
+ void Use(const FwdTransactionCounter& aCounter) {
+ mFwdTransactionType = aCounter.mFwdTransactionType;
+ mFwdTransactionId = aCounter.mFwdTransactionId;
+ }
+
+ Atomic<mozilla::ipc::ProtocolId> mFwdTransactionType{
+ (mozilla::ipc::ProtocolId)0};
+ Atomic<uint64_t> mFwdTransactionId{0};
+
+ private:
+ FwdTransactionTracker() = default;
+ ~FwdTransactionTracker() = default;
+};
+
+inline RemoteTextureTxnType ToRemoteTextureTxnType(
+ const RefPtr<FwdTransactionTracker>& aTracker) {
+ return aTracker ? (RemoteTextureTxnType)aTracker->mFwdTransactionType : 0;
+}
+
+inline RemoteTextureTxnId ToRemoteTextureTxnId(
+ const RefPtr<FwdTransactionTracker>& aTracker) {
+ return aTracker ? (RemoteTextureTxnId)aTracker->mFwdTransactionId : 0;
+}
+
+/**
+ * A transaction is a set of changes that happenned on the content side, that
+ * should be sent to the compositor side.
+ * CompositableForwarder is an interface to manage a transaction of
+ * compositable objetcs.
+ *
+ * ShadowLayerForwarder is an example of a CompositableForwarder (that can
+ * additionally forward modifications of the Layer tree).
+ * ImageBridgeChild is another CompositableForwarder.
+ *
+ * CompositableForwarder implements KnowsCompositor for simplicity as all
+ * implementations of CompositableForwarder currently also implement
+ * KnowsCompositor. This dependency could be split if we add new use cases.
+ */
+class CompositableForwarder : public KnowsCompositor {
+ public:
+ CompositableForwarder();
+ ~CompositableForwarder();
+
+ /**
+ * Setup the IPDL actor for aCompositable to be part of layers
+ * transactions.
+ */
+ virtual void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer = nullptr) = 0;
+
+ virtual void ReleaseCompositable(const CompositableHandle& aHandle) = 0;
+ virtual bool DestroyInTransaction(PTextureChild* aTexture) = 0;
+
+ /**
+ * Tell the CompositableHost on the compositor side to remove the texture
+ * from the CompositableHost.
+ * This function does not delete the TextureHost corresponding to the
+ * TextureClient passed in parameter.
+ * When the TextureClient has TEXTURE_DEALLOCATE_CLIENT flag,
+ * the transaction becomes synchronous.
+ */
+ virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) = 0;
+
+ struct TimedTextureClient {
+ TimedTextureClient()
+ : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {}
+
+ TextureClient* mTextureClient;
+ TimeStamp mTimeStamp;
+ nsIntRect mPictureRect;
+ int32_t mFrameID;
+ int32_t mProducerID;
+ };
+ /**
+ * Tell the CompositableHost on the compositor side what textures to use for
+ * the next composition.
+ */
+ virtual void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) = 0;
+
+ virtual void UseRemoteTexture(
+ CompositableClient* aCompositable, const RemoteTextureId aTextureId,
+ const RemoteTextureOwnerId aOwnerId, const gfx::IntSize aSize,
+ const TextureFlags aFlags,
+ const RefPtr<layers::FwdTransactionTracker>& aTracker) = 0;
+
+ void UpdateFwdTransactionId() {
+ ++GetFwdTransactionCounter().mFwdTransactionId;
+ }
+ uint64_t GetFwdTransactionId() {
+ return GetFwdTransactionCounter().mFwdTransactionId;
+ }
+ mozilla::ipc::ProtocolId GetFwdTransactionType() {
+ return GetFwdTransactionCounter().mFwdTransactionType;
+ }
+
+ virtual bool InForwarderThread() = 0;
+
+ void AssertInForwarderThread() { MOZ_ASSERT(InForwarderThread()); }
+
+ protected:
+ virtual FwdTransactionCounter& GetFwdTransactionCounter() = 0;
+
+ void TrackFwdTransaction(const RefPtr<FwdTransactionTracker>& aTracker) {
+ if (aTracker) {
+ aTracker->Use(GetFwdTransactionCounter());
+ }
+ }
+
+ 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..bb6c5a209b
--- /dev/null
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositableTransactionParent.h"
+#include "CompositableHost.h" // for CompositableParent, etc
+#include "CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/WebRenderImageHost.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/Unused.h"
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+bool CompositableParentManager::ReceiveCompositableUpdate(
+ const CompositableOperation& aEdit) {
+ // Ignore all operations on compositables created on stale compositors. We
+ // return true because the child is unable to handle errors.
+ RefPtr<CompositableHost> compositable =
+ FindCompositable(aEdit.compositable());
+ if (!compositable) {
+ return false;
+ }
+ return ReceiveCompositableUpdate(aEdit.detail(), WrapNotNull(compositable),
+ aEdit.compositable());
+}
+
+bool CompositableParentManager::ReceiveCompositableUpdate(
+ const CompositableOperationDetail& aDetail,
+ NotNull<CompositableHost*> aCompositable,
+ const CompositableHandle& aHandle) {
+ switch (aDetail.type()) {
+ case CompositableOperationDetail::TOpRemoveTexture: {
+ const OpRemoveTexture& op = aDetail.get_OpRemoveTexture();
+
+ RefPtr<TextureHost> tex =
+ TextureHost::AsTextureHost(op.texture().AsParent());
+
+ MOZ_ASSERT(tex.get());
+ aCompositable->RemoveTextureHost(tex);
+ break;
+ }
+ case CompositableOperationDetail::TOpUseTexture: {
+ const OpUseTexture& op = aDetail.get_OpUseTexture();
+
+ AutoTArray<CompositableHost::TimedTexture, 4> textures;
+ for (auto& timedTexture : op.textures()) {
+ CompositableHost::TimedTexture* t = textures.AppendElement();
+ t->mTexture =
+ TextureHost::AsTextureHost(timedTexture.texture().AsParent());
+ MOZ_ASSERT(t->mTexture);
+ t->mTimeStamp = timedTexture.timeStamp();
+ t->mPictureRect = timedTexture.picture();
+ t->mFrameID = timedTexture.frameID();
+ t->mProducerID = timedTexture.producerID();
+ if (timedTexture.readLocked()) {
+ t->mTexture->SetReadLocked();
+ }
+ }
+ if (textures.Length() > 0) {
+ aCompositable->UseTextureHost(textures);
+
+ for (auto& timedTexture : op.textures()) {
+ RefPtr<TextureHost> texture =
+ TextureHost::AsTextureHost(timedTexture.texture().AsParent());
+ if (texture) {
+ texture->SetLastFwdTransactionId(mFwdTransactionId);
+ // Make sure that each texture was handled by the compositable
+ // because the recycling logic depends on it.
+ MOZ_ASSERT(texture->NumCompositableRefs() > 0);
+ }
+ }
+ }
+ break;
+ }
+ case CompositableOperationDetail::TOpUseRemoteTexture: {
+ const OpUseRemoteTexture& op = aDetail.get_OpUseRemoteTexture();
+ auto* host = aCompositable->AsWebRenderImageHost();
+ MOZ_ASSERT(host);
+
+ host->PushPendingRemoteTexture(op.textureId(), op.ownerId(),
+ GetChildProcessId(), op.size(),
+ op.textureFlags());
+ host->UseRemoteTexture();
+ break;
+ }
+ default: {
+ MOZ_ASSERT(false, "bad type");
+ }
+ }
+
+ return true;
+}
+
+void CompositableParentManager::DestroyActor(const OpDestroy& aOp) {
+ switch (aOp.type()) {
+ case OpDestroy::TPTexture: {
+ auto actor = aOp.get_PTexture().AsParent();
+ TextureHost::ReceivedDestroy(actor);
+ break;
+ }
+ case OpDestroy::TCompositableHandle: {
+ ReleaseCompositable(aOp.get_CompositableHandle());
+ break;
+ }
+ default: {
+ MOZ_ASSERT(false, "unsupported type");
+ }
+ }
+}
+
+RefPtr<CompositableHost> CompositableParentManager::AddCompositable(
+ const CompositableHandle& aHandle, const TextureInfo& aInfo) {
+ if (mCompositables.find(aHandle.Value()) != mCompositables.end()) {
+ NS_ERROR("Client should not allocate duplicate handles");
+ return nullptr;
+ }
+ if (!aHandle) {
+ NS_ERROR("Client should not allocate 0 as a handle");
+ return nullptr;
+ }
+
+ RefPtr<CompositableHost> host = CompositableHost::Create(aInfo);
+ if (!host) {
+ return nullptr;
+ }
+
+ mCompositables[aHandle.Value()] = host;
+ return host;
+}
+
+RefPtr<CompositableHost> CompositableParentManager::FindCompositable(
+ const CompositableHandle& aHandle) {
+ auto iter = mCompositables.find(aHandle.Value());
+ if (iter == mCompositables.end()) {
+ return nullptr;
+ }
+
+ return iter->second;
+}
+
+void CompositableParentManager::ReleaseCompositable(
+ const CompositableHandle& aHandle) {
+ auto iter = mCompositables.find(aHandle.Value());
+ if (iter == mCompositables.end()) {
+ return;
+ }
+ iter->second->OnReleased();
+ mCompositables.erase(iter);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositableTransactionParent.h b/gfx/layers/ipc/CompositableTransactionParent.h
new file mode 100644
index 0000000000..172c6bb035
--- /dev/null
+++ b/gfx/layers/ipc/CompositableTransactionParent.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
+#define MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
+
+#include <vector> // for vector
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/NotNull.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/LayersMessages.h" // for EditReply, etc
+#include "mozilla/layers/TextureClient.h"
+#include "CompositableHost.h"
+
+namespace mozilla {
+namespace layers {
+
+// Since PCompositble has two potential manager protocols, we can't just call
+// the Manager() method usually generated when there's one manager protocol,
+// so both manager protocols implement this and we keep a reference to them
+// through this interface.
+class CompositableParentManager : public HostIPCAllocator {
+ public:
+ CompositableParentManager() = default;
+
+ void DestroyActor(const OpDestroy& aOp);
+
+ void UpdateFwdTransactionId(uint64_t aTransactionId) {
+ MOZ_ASSERT(mFwdTransactionId < aTransactionId);
+ mFwdTransactionId = aTransactionId;
+ }
+
+ uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
+
+ RefPtr<CompositableHost> AddCompositable(const CompositableHandle& aHandle,
+ const TextureInfo& aInfo);
+ RefPtr<CompositableHost> FindCompositable(const CompositableHandle& aHandle);
+
+ protected:
+ /**
+ * Handle the IPDL messages that affect PCompositable actors.
+ */
+ bool ReceiveCompositableUpdate(const CompositableOperation& aEdit);
+ bool ReceiveCompositableUpdate(const CompositableOperationDetail& aDetail,
+ NotNull<CompositableHost*> aCompositable,
+ const CompositableHandle& aHandle);
+
+ void ReleaseCompositable(const CompositableHandle& aHandle);
+
+ uint64_t mFwdTransactionId = 0;
+
+ /**
+ * Mapping form IDs to CompositableHosts.
+ */
+ std::map<uint64_t, RefPtr<CompositableHost>> mCompositables;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositorBench.cpp b/gfx/layers/ipc/CompositorBench.cpp
new file mode 100644
index 0000000000..0fb4d8012b
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBench.cpp
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CompositorBench.h"
+
+#ifdef MOZ_COMPOSITOR_BENCH
+# include "mozilla/gfx/2D.h"
+# include "mozilla/layers/Compositor.h"
+# include "mozilla/layers/Effects.h"
+# include "mozilla/ProfilerMarkers.h"
+# include "mozilla/StaticPrefs_layers.h"
+# include "mozilla/TimeStamp.h"
+# include <math.h>
+
+# define TEST_STEPS 1000
+# define DURATION_THRESHOLD 30
+# define THRESHOLD_ABORT_COUNT 5
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static float SimplePseudoRandom(int aStep, int aCount) {
+ srand(aStep * 1000 + aCount);
+ return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
+}
+
+class BenchTest {
+ public:
+ BenchTest(const char* aTestName) : mTestName(aTestName) {}
+
+ virtual ~BenchTest() = default;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {}
+ virtual void Teardown(Compositor* aCompositor) {}
+ virtual void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) = 0;
+
+ const char* ToString() { return mTestName; }
+
+ private:
+ const char* mTestName;
+};
+
+static void DrawFrameTrivialQuad(Compositor* aCompositor,
+ const gfx::Rect& aScreenRect, size_t aStep,
+ const EffectChain& effects) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
+ (int)(i / aScreenRect.height), 1, 1);
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix transform2d;
+
+ gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+}
+
+static void DrawFrameStressQuad(Compositor* aCompositor,
+ const gfx::Rect& aScreenRect, size_t aStep,
+ const EffectChain& effects) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ const gfx::Rect& rect =
+ gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
+ aScreenRect.height * SimplePseudoRandom(i, 1),
+ aScreenRect.width * SimplePseudoRandom(i, 2),
+ aScreenRect.height * SimplePseudoRandom(i, 3));
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix transform2d;
+ transform2d = transform2d.PreRotate(SimplePseudoRandom(i, 4) * 70.f);
+
+ gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+}
+
+class EffectSolidColorBench : public BenchTest {
+ public:
+ EffectSolidColorBench()
+ : BenchTest("EffectSolidColorBench (clear frame with EffectSolidColor)") {
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ float tmp;
+ float red = modff(aStep * 0.03f, &tmp);
+ EffectChain effects;
+ effects.mPrimaryEffect =
+ new EffectSolidColor(gfx::DeviceColor(red, 0.4f, 0.4f, 1.0f));
+
+ const gfx::Rect& rect = aScreenRect;
+ const gfx::Rect& clipRect = aScreenRect;
+
+ float opacity = 1.f;
+
+ gfx::Matrix4x4 transform;
+ aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+ }
+};
+
+class EffectSolidColorTrivialBench : public BenchTest {
+ public:
+ EffectSolidColorTrivialBench()
+ : BenchTest("EffectSolidColorTrivialBench (10s 1x1 EffectSolidColor)") {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ float tmp;
+ float red = modff(i * 0.03f, &tmp);
+ EffectChain effects;
+ return MakeAndAddRef<EffectSolidColor>(
+ gfx::DeviceColor(red, 0.4f, 0.4f, 1.0f));
+ }
+};
+
+class EffectSolidColorStressBench : public BenchTest {
+ public:
+ EffectSolidColorStressBench()
+ : BenchTest(
+ "EffectSolidColorStressBench (10s various EffectSolidColor)") {}
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ float tmp;
+ float red = modff(i * 0.03f, &tmp);
+ EffectChain effects;
+ return MakeAndAddRef<EffectSolidColor>(
+ gfx::DeviceColor(red, 0.4f, 0.4f, 1.0f));
+ }
+};
+
+class UploadBench : public BenchTest {
+ public:
+ UploadBench() : BenchTest("Upload Bench (10s 256x256 upload)") {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ int w = 256;
+ int h = 256;
+ mBuf = (uint32_t*)malloc(w * h * sizeof(uint32_t));
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h),
+ SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ for (size_t i = 0; i < aStep * 10; i++) {
+ mTexture->Update(mSurface);
+ }
+ }
+};
+
+class TrivialTexturedQuadBench : public BenchTest {
+ public:
+ TrivialTexturedQuadBench()
+ : BenchTest("Trvial Textured Quad (10s 256x256 quads)") {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ size_t w = 256;
+ size_t h = 256;
+ mBuf = (uint32_t*)malloc(w * h * sizeof(uint32_t));
+ for (size_t i = 0; i < w * h; i++) {
+ mBuf[i] = 0xFF00008F;
+ }
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h),
+ SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ mTexture->Update(mSurface);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ already_AddRefed<Effect> CreateEffect(size_t i) {
+ return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture,
+ SamplingFilter::POINT, true);
+ }
+};
+
+class StressTexturedQuadBench : public BenchTest {
+ public:
+ StressTexturedQuadBench()
+ : BenchTest("Stress Textured Quad (10s 256x256 quads)") {}
+
+ uint32_t* mBuf;
+ RefPtr<DataSourceSurface> mSurface;
+ RefPtr<DataTextureSource> mTexture;
+
+ virtual void Setup(Compositor* aCompositor, size_t aStep) {
+ int bytesPerPixel = 4;
+ size_t w = 256;
+ size_t h = 256;
+ mBuf = (uint32_t*)malloc(w * h * sizeof(uint32_t));
+ for (size_t i = 0; i < w * h; i++) {
+ mBuf[i] = 0xFF00008F;
+ }
+
+ mSurface = Factory::CreateWrappingDataSourceSurface(
+ reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h),
+ SurfaceFormat::B8G8R8A8);
+ mTexture = aCompositor->CreateDataTextureSource();
+ mTexture->Update(mSurface);
+ }
+
+ void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect,
+ size_t aStep) {
+ EffectChain effects;
+ effects.mPrimaryEffect = CreateEffect(aStep);
+
+ DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+ }
+
+ virtual void Teardown(Compositor* aCompositor) {
+ mSurface = nullptr;
+ mTexture = nullptr;
+ free(mBuf);
+ }
+
+ virtual already_AddRefed<Effect> CreateEffect(size_t i) {
+ return CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture,
+ SamplingFilter::POINT, true);
+ }
+};
+
+static void RunCompositorBench(Compositor* aCompositor,
+ const gfx::Rect& aScreenRect) {
+ std::vector<BenchTest*> tests;
+
+ tests.push_back(new EffectSolidColorBench());
+ tests.push_back(new UploadBench());
+ tests.push_back(new EffectSolidColorTrivialBench());
+ tests.push_back(new EffectSolidColorStressBench());
+ tests.push_back(new TrivialTexturedQuadBench());
+ tests.push_back(new StressTexturedQuadBench());
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ BenchTest* test = tests[i];
+ std::vector<TimeDuration> results;
+ int testsOverThreshold = 0;
+ PROFILER_MARKER_UNTYPED(
+ ProfilerString8View::WrapNullTerminatedString(test->ToString()),
+ GRAPHICS);
+ for (size_t j = 0; j < TEST_STEPS; j++) {
+ test->Setup(aCompositor, j);
+
+ TimeStamp start = TimeStamp::Now();
+ IntRect screenRect(aScreenRect.x, aScreenRect.y, aScreenRect.width,
+ aScreenRect.height);
+ aCompositor->BeginFrame(IntRect(screenRect.x, screenRect.y,
+ screenRect.width, screenRect.height),
+ nullptr, aScreenRect, nullptr, nullptr);
+
+ test->DrawFrame(aCompositor, aScreenRect, j);
+
+ aCompositor->EndFrame();
+ results.push_back(TimeStamp::Now() - start);
+
+ if (results[j].ToMilliseconds() > DURATION_THRESHOLD) {
+ testsOverThreshold++;
+ if (testsOverThreshold == THRESHOLD_ABORT_COUNT) {
+ test->Teardown(aCompositor);
+ break;
+ }
+ } else {
+ testsOverThreshold = 0;
+ }
+ test->Teardown(aCompositor);
+ }
+
+ printf_stderr("%s\n", test->ToString());
+ printf_stderr("Run step, Time (ms)\n");
+ for (size_t j = 0; j < results.size(); j++) {
+ printf_stderr("%i,%f\n", j, (float)results[j].ToMilliseconds());
+ }
+ printf_stderr("\n", test->ToString());
+ }
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ delete tests[i];
+ }
+}
+
+void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect) {
+ static bool sRanBenchmark = false;
+ bool wantBenchmark = StaticPrefs::layers_bench_enabled();
+ if (wantBenchmark && !sRanBenchmark) {
+ RunCompositorBench(aCompositor, aScreenRect);
+ }
+ sRanBenchmark = wantBenchmark;
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositorBench.h b/gfx/layers/ipc/CompositorBench.h
new file mode 100644
index 0000000000..4a3c5874e9
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBench.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBench_h
+#define mozilla_layers_CompositorBench_h
+
+#include "mozilla/gfx/Rect.h" // for Rect
+
+namespace mozilla {
+namespace layers {
+
+class Compositor;
+
+// Uncomment this line to rebuild with compositor bench.
+// #define MOZ_COMPOSITOR_BENCH
+
+#ifdef MOZ_COMPOSITOR_BENCH
+void CompositorBench(Compositor* aCompositor, const gfx::IntRect& aScreenRect);
+#else
+static inline void CompositorBench(Compositor* aCompositor,
+ const gfx::IntRect& aScreenRect) {}
+#endif
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp
new file mode 100644
index 0000000000..070e6d673e
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -0,0 +1,640 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include <stddef.h> // for size_t
+#include "base/task.h" // for NewRunnableMethod, etc
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/layers/CompositorManagerChild.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/APZCTreeManagerChild.h"
+#include "mozilla/layers/CanvasChild.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "mozilla/layers/PTextureChild.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/TextureClientPool.h" // for TextureClientPool
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/layers/SyncObject.h" // for SyncObjectClient
+#include "mozilla/gfx/CanvasManagerChild.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/Telemetry.h"
+#include "gfxConfig.h"
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Unused.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsThreadUtils.h"
+#if defined(XP_WIN)
+# include "WinUtils.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetChild.h"
+#endif
+#include "VsyncSource.h"
+
+using mozilla::Unused;
+using mozilla::gfx::GPUProcessManager;
+
+namespace mozilla {
+namespace layers {
+
+static int sShmemCreationCounter = 0;
+
+static void ResetShmemCounter() { sShmemCreationCounter = 0; }
+
+static void ShmemAllocated(CompositorBridgeChild* aProtocol) {
+ sShmemCreationCounter++;
+ if (sShmemCreationCounter > 256) {
+ aProtocol->SendSyncWithCompositor();
+ ResetShmemCounter();
+ MOZ_PERFORMANCE_WARNING(
+ "gfx", "The number of shmem allocations is too damn high!");
+ }
+}
+
+static StaticRefPtr<CompositorBridgeChild> sCompositorBridge;
+
+Atomic<int32_t> KnowsCompositor::sSerialCounter(0);
+
+CompositorBridgeChild::CompositorBridgeChild(CompositorManagerChild* aManager)
+ : mCompositorManager(aManager),
+ mIdNamespace(0),
+ mResourceId(0),
+ mCanSend(false),
+ mActorDestroyed(false),
+ mPaused(false),
+ mThread(NS_GetCurrentThread()),
+ mProcessToken(0),
+ mSectionAllocator(nullptr) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+CompositorBridgeChild::~CompositorBridgeChild() {
+ if (mCanSend) {
+ gfxCriticalError() << "CompositorBridgeChild was not deinitialized";
+ }
+}
+
+bool CompositorBridgeChild::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void CompositorBridgeChild::PrepareFinalDestroy() {
+ // Because of medium high priority DidComposite, we need to repost to
+ // medium high priority queue to ensure the actor is destroyed after possible
+ // pending DidComposite message.
+ nsCOMPtr<nsIRunnable> runnable =
+ NewRunnableMethod("CompositorBridgeChild::AfterDestroy", this,
+ &CompositorBridgeChild::AfterDestroy);
+ NS_DispatchToCurrentThreadQueue(runnable.forget(),
+ EventQueuePriority::MediumHigh);
+}
+
+void CompositorBridgeChild::AfterDestroy() {
+ // Note that we cannot rely upon mCanSend here because we already set that to
+ // false to prevent normal IPDL calls from being made after SendWillClose.
+ // The only time we should not issue Send__delete__ is if the actor is already
+ // destroyed, e.g. the compositor process crashed.
+ if (!mActorDestroyed) {
+ // We saw this send fail quite often with "Channel closing", probably a race
+ // with the other side closing or some event scheduling order.
+ if (GetIPCChannel()->CanSend()) {
+ Send__delete__(this);
+ }
+ mActorDestroyed = true;
+ }
+
+ if (sCompositorBridge == this) {
+ sCompositorBridge = nullptr;
+ }
+}
+
+void CompositorBridgeChild::Destroy() {
+ // This must not be called from the destructor!
+ mTexturesWaitingNotifyNotUsed.clear();
+
+ // Destroying the layer manager may cause all sorts of things to happen, so
+ // let's make sure there is still a reference to keep this alive whatever
+ // happens.
+ RefPtr<CompositorBridgeChild> selfRef = this;
+
+ for (size_t i = 0; i < mTexturePools.Length(); i++) {
+ mTexturePools[i]->Destroy();
+ }
+
+ if (mSectionAllocator) {
+ delete mSectionAllocator;
+ mSectionAllocator = nullptr;
+ }
+
+ if (mLayerManager) {
+ mLayerManager->Destroy();
+ mLayerManager = nullptr;
+ }
+
+ if (!mCanSend) {
+ // We may have already called destroy but still have lingering references
+ // or CompositorBridgeChild::ActorDestroy was called. Ensure that we do our
+ // post destroy clean up no matter what. It is safe to call multiple times.
+ NS_GetCurrentThread()->Dispatch(
+ NewRunnableMethod("CompositorBridgeChild::PrepareFinalDestroy", selfRef,
+ &CompositorBridgeChild::PrepareFinalDestroy));
+ return;
+ }
+
+ AutoTArray<PWebRenderBridgeChild*, 16> wrBridges;
+ ManagedPWebRenderBridgeChild(wrBridges);
+ for (int i = wrBridges.Length() - 1; i >= 0; --i) {
+ RefPtr<WebRenderBridgeChild> wrBridge =
+ static_cast<WebRenderBridgeChild*>(wrBridges[i]);
+ wrBridge->Destroy(/* aIsSync */ false);
+ }
+
+ AutoTArray<PAPZChild*, 16> apzChildren;
+ ManagedPAPZChild(apzChildren);
+ for (PAPZChild* child : apzChildren) {
+ Unused << child->SendDestroy();
+ }
+
+ const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild();
+ for (const auto& key : textures) {
+ RefPtr<TextureClient> texture = TextureClient::AsTextureClient(key);
+
+ if (texture) {
+ texture->Destroy();
+ }
+ }
+
+ // The WillClose message is synchronous, so we know that after it returns
+ // any messages sent by the above code will have been processed on the
+ // other side.
+ SendWillClose();
+ mCanSend = false;
+
+ // We no longer care about unexpected shutdowns, in the remote process case.
+ mProcessToken = 0;
+
+ // The call just made to SendWillClose can result in IPC from the
+ // CompositorBridgeParent to the CompositorBridgeChild (e.g. caused by the
+ // destruction of shared memory). We need to ensure this gets processed by the
+ // CompositorBridgeChild before it gets destroyed. It suffices to ensure that
+ // events already in the thread get processed before the
+ // CompositorBridgeChild is destroyed, so we add a task to the thread to
+ // handle compositor destruction.
+
+ // From now on we can't send any message message.
+ NS_GetCurrentThread()->Dispatch(
+ NewRunnableMethod("CompositorBridgeChild::PrepareFinalDestroy", selfRef,
+ &CompositorBridgeChild::PrepareFinalDestroy));
+}
+
+// static
+void CompositorBridgeChild::ShutDown() {
+ if (sCompositorBridge) {
+ sCompositorBridge->Destroy();
+ SpinEventLoopUntil("CompositorBridgeChild::ShutDown"_ns,
+ [&]() { return !sCompositorBridge; });
+ }
+}
+
+void CompositorBridgeChild::InitForContent(uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aNamespace);
+
+ if (RefPtr<CompositorBridgeChild> old = sCompositorBridge.forget()) {
+ // Note that at this point, ActorDestroy may not have been called yet,
+ // meaning mCanSend is still true. In this case we will try to send a
+ // synchronous WillClose message to the parent, and will certainly get
+ // a false result and a MsgDropped processing error. This is okay.
+ old->Destroy();
+ }
+
+ mCanSend = true;
+ mIdNamespace = aNamespace;
+
+ sCompositorBridge = this;
+}
+
+void CompositorBridgeChild::InitForWidget(uint64_t aProcessToken,
+ WebRenderLayerManager* aLayerManager,
+ uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aProcessToken);
+ MOZ_ASSERT(aLayerManager);
+ MOZ_ASSERT(aNamespace);
+
+ mCanSend = true;
+ mProcessToken = aProcessToken;
+ mLayerManager = aLayerManager;
+ mIdNamespace = aNamespace;
+}
+
+/*static*/
+CompositorBridgeChild* CompositorBridgeChild::Get() {
+ // This is only expected to be used in child processes. While the parent
+ // process does have CompositorBridgeChild instances, it has _multiple_ (one
+ // per window), and therefore there is no global singleton available.
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ return sCompositorBridge;
+}
+
+/* static */
+bool CompositorBridgeChild::CompositorIsInGPUProcess() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (XRE_IsParentProcess()) {
+ return !!GPUProcessManager::Get()->GetGPUChild();
+ }
+
+ MOZ_ASSERT(XRE_IsContentProcess());
+ CompositorBridgeChild* bridge = CompositorBridgeChild::Get();
+ if (!bridge) {
+ return false;
+ }
+
+ return bridge->OtherPid() != dom::ContentChild::GetSingleton()->OtherPid();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvDidComposite(
+ const LayersId& aId, const nsTArray<TransactionId>& aTransactionIds,
+ const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd) {
+ // Hold a reference to keep texture pools alive. See bug 1387799
+ const auto texturePools = mTexturePools.Clone();
+
+ for (const auto& id : aTransactionIds) {
+ if (mLayerManager) {
+ MOZ_ASSERT(!aId.IsValid());
+ MOZ_ASSERT(mLayerManager->GetBackendType() == LayersBackend::LAYERS_WR);
+ // Hold a reference to keep LayerManager alive. See Bug 1242668.
+ RefPtr<WebRenderLayerManager> m = mLayerManager;
+ m->DidComposite(id, aCompositeStart, aCompositeEnd);
+ } else if (aId.IsValid()) {
+ RefPtr<dom::BrowserChild> child = dom::BrowserChild::GetFrom(aId);
+ if (child) {
+ child->DidComposite(id, aCompositeStart, aCompositeEnd);
+ }
+ }
+ }
+
+ for (size_t i = 0; i < texturePools.Length(); i++) {
+ texturePools[i]->ReturnDeferredClients();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvNotifyFrameStats(
+ nsTArray<FrameStats>&& aFrameStats) {
+ gfxPlatform::GetPlatform()->NotifyFrameStats(std::move(aFrameStats));
+ return IPC_OK();
+}
+
+void CompositorBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ if (aWhy == AbnormalShutdown) {
+ // If the parent side runs into a problem then the actor will be destroyed.
+ // There is nothing we can do in the child side, here sets mCanSend as
+ // false.
+ gfxCriticalNote << "CompositorBridgeChild receives IPC close with "
+ "reason=AbnormalShutdown";
+ }
+
+ mCanSend = false;
+ mActorDestroyed = true;
+
+ if (mProcessToken && XRE_IsParentProcess()) {
+ GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+ }
+}
+
+bool CompositorBridgeChild::SendWillClose() {
+ MOZ_RELEASE_ASSERT(mCanSend);
+ return PCompositorBridgeChild::SendWillClose();
+}
+
+bool CompositorBridgeChild::SendPause() {
+ if (!mCanSend) {
+ return false;
+ }
+ mPaused = true;
+ return PCompositorBridgeChild::SendPause();
+}
+
+bool CompositorBridgeChild::SendResume() {
+ if (!mCanSend) {
+ return false;
+ }
+ mPaused = false;
+ return PCompositorBridgeChild::SendResume();
+}
+
+bool CompositorBridgeChild::SendResumeAsync() {
+ if (!mCanSend) {
+ return false;
+ }
+ mPaused = false;
+ return PCompositorBridgeChild::SendResumeAsync();
+}
+
+bool CompositorBridgeChild::SendAdoptChild(const LayersId& id) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendAdoptChild(id);
+}
+
+bool CompositorBridgeChild::SendFlushRendering(
+ const wr::RenderReasons& aReasons) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendFlushRendering(aReasons);
+}
+
+bool CompositorBridgeChild::SendStartFrameTimeRecording(
+ const int32_t& bufferSize, uint32_t* startIndex) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendStartFrameTimeRecording(bufferSize,
+ startIndex);
+}
+
+bool CompositorBridgeChild::SendStopFrameTimeRecording(
+ const uint32_t& startIndex, nsTArray<float>* intervals) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::SendStopFrameTimeRecording(startIndex,
+ intervals);
+}
+
+PTextureChild* CompositorBridgeChild::AllocPTextureChild(
+ const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&,
+ const TextureFlags&, const LayersId&, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ return TextureClient::CreateIPDLActor();
+}
+
+bool CompositorBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvParentAsyncMessages(
+ nsTArray<AsyncParentMessageData>&& aMessages) {
+ for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
+ const AsyncParentMessageData& message = aMessages[i];
+
+ switch (message.type()) {
+ case AsyncParentMessageData::TOpNotifyNotUsed: {
+ const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
+ NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
+ break;
+ }
+ default:
+ NS_ERROR("unknown AsyncParentMessageData type");
+ return IPC_FAIL_NO_REASON(this);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvObserveLayersUpdate(
+ const LayersId& aLayersId, const 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(aActive);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvCompositorOptionsChanged(
+ const LayersId& aLayersId, const CompositorOptions& aNewOptions) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (RefPtr<dom::BrowserParent> tab =
+ dom::BrowserParent::GetBrowserParentFromLayersId(aLayersId)) {
+ Unused << tab->SendCompositorOptionsChanged(aNewOptions);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeChild::RecvNotifyJankedAnimations(
+ const LayersId& aLayersId, nsTArray<uint64_t>&& aJankedAnimations) {
+ if (mLayerManager) {
+ MOZ_ASSERT(!aLayersId.IsValid());
+ mLayerManager->UpdatePartialPrerenderedAnimations(aJankedAnimations);
+ } else if (aLayersId.IsValid()) {
+ RefPtr<dom::BrowserChild> child = dom::BrowserChild::GetFrom(aLayersId);
+ if (child) {
+ child->NotifyJankedAnimations(aJankedAnimations);
+ }
+ }
+
+ return IPC_OK();
+}
+
+void CompositorBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(
+ TextureClient* aClient) {
+ if (!aClient) {
+ return;
+ }
+
+ bool waitNotifyNotUsed =
+ aClient->GetFlags() & TextureFlags::RECYCLE ||
+ aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END;
+ if (!waitNotifyNotUsed) {
+ return;
+ }
+
+ aClient->SetLastFwdTransactionId(
+ GetFwdTransactionCounter().mFwdTransactionId);
+ mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient);
+}
+
+void CompositorBridgeChild::NotifyNotUsed(uint64_t aTextureId,
+ uint64_t aFwdTransactionId) {
+ auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId);
+ if (it != mTexturesWaitingNotifyNotUsed.end()) {
+ if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) {
+ // Released on host side, but client already requested newer use texture.
+ return;
+ }
+ mTexturesWaitingNotifyNotUsed.erase(it);
+ }
+}
+
+void CompositorBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) {
+ mTexturesWaitingNotifyNotUsed.erase(aTextureId);
+}
+
+FixedSizeSmallShmemSectionAllocator*
+CompositorBridgeChild::GetTileLockAllocator() {
+ if (!IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!mSectionAllocator) {
+ mSectionAllocator = new FixedSizeSmallShmemSectionAllocator(this);
+ }
+ return mSectionAllocator;
+}
+
+PTextureChild* CompositorBridgeChild::CreateTexture(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ const dom::ContentParentId& aContentId, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId) {
+ PTextureChild* textureChild =
+ AllocPTextureChild(aSharedData, aReadLock, aLayersBackend, aFlags,
+ LayersId{0} /* FIXME */, aSerial, aExternalImageId);
+
+ return SendPTextureConstructor(
+ textureChild, aSharedData, std::move(aReadLock), aLayersBackend, aFlags,
+ LayersId{0} /* FIXME? */, aSerial, aExternalImageId);
+}
+
+already_AddRefed<CanvasChild> CompositorBridgeChild::GetCanvasChild() {
+ MOZ_ASSERT(gfxPlatform::UseRemoteCanvas());
+ if (auto* cm = gfx::CanvasManagerChild::Get()) {
+ return cm->GetCanvasChild().forget();
+ }
+ return nullptr;
+}
+
+void CompositorBridgeChild::EndCanvasTransaction() {
+ if (auto* cm = gfx::CanvasManagerChild::Get()) {
+ cm->EndCanvasTransaction();
+ }
+}
+
+void CompositorBridgeChild::ClearCachedResources() {
+ if (auto* cm = gfx::CanvasManagerChild::Get()) {
+ cm->ClearCachedResources();
+ }
+}
+
+bool CompositorBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
+ ShmemAllocated(this);
+ return PCompositorBridgeChild::AllocUnsafeShmem(aSize, aShmem);
+}
+
+bool CompositorBridgeChild::AllocShmem(size_t aSize, ipc::Shmem* aShmem) {
+ ShmemAllocated(this);
+ return PCompositorBridgeChild::AllocShmem(aSize, aShmem);
+}
+
+bool CompositorBridgeChild::DeallocShmem(ipc::Shmem& aShmem) {
+ if (!mCanSend) {
+ return false;
+ }
+ return PCompositorBridgeChild::DeallocShmem(aShmem);
+}
+
+widget::PCompositorWidgetChild*
+CompositorBridgeChild::AllocPCompositorWidgetChild(
+ const CompositorWidgetInitData& aInitData) {
+ // We send the constructor manually.
+ MOZ_CRASH("Should not be called");
+ return nullptr;
+}
+
+bool CompositorBridgeChild::DeallocPCompositorWidgetChild(
+ PCompositorWidgetChild* aActor) {
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+ delete aActor;
+ return true;
+#else
+ return false;
+#endif
+}
+
+PAPZCTreeManagerChild* CompositorBridgeChild::AllocPAPZCTreeManagerChild(
+ const LayersId& aLayersId) {
+ APZCTreeManagerChild* child = new APZCTreeManagerChild();
+ child->AddIPDLReference();
+
+ return child;
+}
+
+PAPZChild* CompositorBridgeChild::AllocPAPZChild(const LayersId& aLayersId) {
+ // We send the constructor manually.
+ MOZ_CRASH("Should not be called");
+ return nullptr;
+}
+
+bool CompositorBridgeChild::DeallocPAPZChild(PAPZChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+bool CompositorBridgeChild::DeallocPAPZCTreeManagerChild(
+ PAPZCTreeManagerChild* aActor) {
+ APZCTreeManagerChild* child = static_cast<APZCTreeManagerChild*>(aActor);
+ child->ReleaseIPDLReference();
+ return true;
+}
+
+// -
+
+PWebRenderBridgeChild* CompositorBridgeChild::AllocPWebRenderBridgeChild(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize&,
+ const WindowKind&) {
+ WebRenderBridgeChild* child = new WebRenderBridgeChild(aPipelineId);
+ child->AddIPDLReference();
+ return child;
+}
+
+bool CompositorBridgeChild::DeallocPWebRenderBridgeChild(
+ PWebRenderBridgeChild* aActor) {
+ WebRenderBridgeChild* child = static_cast<WebRenderBridgeChild*>(aActor);
+ child->ReleaseIPDLReference();
+ return true;
+}
+
+uint64_t CompositorBridgeChild::GetNextResourceId() {
+ ++mResourceId;
+ MOZ_RELEASE_ASSERT(mResourceId != UINT32_MAX);
+
+ uint64_t id = mIdNamespace;
+ id = (id << 32) | mResourceId;
+
+ return id;
+}
+
+wr::MaybeExternalImageId CompositorBridgeChild::GetNextExternalImageId() {
+ return Some(wr::ToExternalImageId(GetNextResourceId()));
+}
+
+wr::PipelineId CompositorBridgeChild::GetNextPipelineId() {
+ return wr::AsPipelineId(GetNextResourceId());
+}
+
+FwdTransactionCounter& CompositorBridgeChild::GetFwdTransactionCounter() {
+ return mCompositorManager->GetFwdTransactionCounter();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h
new file mode 100644
index 0000000000..7e0a4799fe
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBridgeChild_h
+#define mozilla_layers_CompositorBridgeChild_h
+
+#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Monitor.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PCompositorBridgeChild.h"
+#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "nsClassHashtable.h" // for nsClassHashtable
+#include "nsCOMPtr.h" // for nsCOMPtr
+#include "nsHashKeys.h" // for nsUint64HashKey
+#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
+#include "nsIWeakReferenceUtils.h"
+
+#include <unordered_map>
+
+namespace mozilla {
+
+namespace dom {
+class BrowserChild;
+} // namespace dom
+
+namespace widget {
+class CompositorWidget;
+} // namespace widget
+
+namespace layers {
+
+using mozilla::dom::BrowserChild;
+
+class IAPZCTreeManager;
+class APZCTreeManagerChild;
+class CanvasChild;
+class CompositorBridgeParent;
+class CompositorManagerChild;
+class CompositorOptions;
+class WebRenderLayerManager;
+class TextureClient;
+class TextureClientPool;
+struct FrameMetrics;
+struct FwdTransactionCounter;
+
+class CompositorBridgeChild final : public PCompositorBridgeChild,
+ public TextureForwarder {
+ typedef nsTArray<AsyncParentMessageData> AsyncParentMessageArray;
+
+ friend class PCompositorBridgeChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorBridgeChild, override);
+
+ explicit CompositorBridgeChild(CompositorManagerChild* aManager);
+
+ /**
+ * Initialize the singleton compositor bridge for a content process.
+ */
+ void InitForContent(uint32_t aNamespace);
+
+ void InitForWidget(uint64_t aProcessToken,
+ WebRenderLayerManager* aLayerManager, uint32_t aNamespace);
+
+ void Destroy();
+
+ static CompositorBridgeChild* Get();
+
+ // Returns whether the compositor is in the GPU process (false if in the UI
+ // process). This may only be called on the main thread.
+ static bool CompositorIsInGPUProcess();
+
+ mozilla::ipc::IPCResult RecvDidComposite(
+ const LayersId& aId, const nsTArray<TransactionId>& aTransactionIds,
+ const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd);
+
+ mozilla::ipc::IPCResult RecvNotifyFrameStats(
+ nsTArray<FrameStats>&& aFrameStats);
+
+ mozilla::ipc::IPCResult RecvNotifyJankedAnimations(
+ const LayersId& aLayersId, nsTArray<uint64_t>&& aJankedAnimations);
+
+ PTextureChild* AllocPTextureChild(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+
+ bool DeallocPTextureChild(PTextureChild* actor);
+
+ mozilla::ipc::IPCResult RecvParentAsyncMessages(
+ nsTArray<AsyncParentMessageData>&& aMessages);
+ PTextureChild* CreateTexture(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ const dom::ContentParentId& aContentId, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId) override;
+
+ already_AddRefed<CanvasChild> GetCanvasChild() final;
+
+ void EndCanvasTransaction();
+
+ /**
+ * Release resources until they are next required.
+ */
+ void ClearCachedResources();
+
+ // Beware that these methods don't override their super-class equivalent
+ // (which are not virtual), they just overload them. All of these Send*
+ // methods just add a sanity check (that it is not too late send a message)
+ // and forward the call to the super-class's equivalent method. This means
+ // that it is correct to call directly the super-class methods, but you won't
+ // get the extra safety provided here.
+ bool SendWillClose();
+ bool SendPause();
+ bool SendResume();
+ bool SendResumeAsync();
+ bool SendAdoptChild(const LayersId& id);
+ bool SendFlushRendering(const wr::RenderReasons& aReasons);
+ bool SendStartFrameTimeRecording(const int32_t& bufferSize,
+ uint32_t* startIndex);
+ bool SendStopFrameTimeRecording(const uint32_t& startIndex,
+ nsTArray<float>* intervals);
+ bool IsSameProcess() const override;
+
+ bool IPCOpen() const override { return mCanSend; }
+
+ bool IsPaused() const { return mPaused; }
+
+ static void ShutDown();
+
+ FwdTransactionCounter& GetFwdTransactionCounter();
+
+ /**
+ * Hold TextureClient ref until end of usage on host side if
+ * TextureFlags::RECYCLE is set. Host side's usage is checked via
+ * CompositableRef.
+ */
+ void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient);
+
+ /**
+ * Notify id of Texture When host side end its use. Transaction id is used to
+ * make sure if there is no newer usage.
+ */
+ void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
+
+ void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override;
+
+ FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() override;
+
+ nsISerialEventTarget* GetThread() const override { return mThread; }
+
+ base::ProcessId GetParentPid() const override { return OtherPid(); }
+
+ bool AllocUnsafeShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+ bool AllocShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ PCompositorWidgetChild* AllocPCompositorWidgetChild(
+ const CompositorWidgetInitData& aInitData);
+ bool DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor);
+
+ PAPZCTreeManagerChild* AllocPAPZCTreeManagerChild(const LayersId& aLayersId);
+ bool DeallocPAPZCTreeManagerChild(PAPZCTreeManagerChild* aActor);
+
+ PAPZChild* AllocPAPZChild(const LayersId& aLayersId);
+ bool DeallocPAPZChild(PAPZChild* aActor);
+
+ PWebRenderBridgeChild* AllocPWebRenderBridgeChild(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize&,
+ const WindowKind&);
+ bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor);
+
+ wr::MaybeExternalImageId GetNextExternalImageId() override;
+
+ wr::PipelineId GetNextPipelineId();
+
+ private:
+ // Private destructor, to discourage deletion outside of Release():
+ virtual ~CompositorBridgeChild();
+
+ // Must only be called from the paint thread. If the main thread is delaying
+ // IPC messages, this forwards all such delayed IPC messages to the I/O thread
+ // and resumes IPC.
+ void ResumeIPCAfterAsyncPaint();
+
+ void PrepareFinalDestroy();
+ void AfterDestroy();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ mozilla::ipc::IPCResult RecvObserveLayersUpdate(const LayersId& aLayersId,
+ const bool& aActive);
+
+ mozilla::ipc::IPCResult RecvCompositorOptionsChanged(
+ const LayersId& aLayersId, const CompositorOptions& aNewOptions);
+
+ uint64_t GetNextResourceId();
+
+ RefPtr<CompositorManagerChild> mCompositorManager;
+
+ RefPtr<WebRenderLayerManager> mLayerManager;
+
+ uint32_t mIdNamespace;
+ uint32_t mResourceId;
+
+ // When not multi-process, hold a reference to the CompositorBridgeParent to
+ // keep it alive. This reference should be null in multi-process.
+ RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeChild);
+
+ // True until the beginning of the two-step shutdown sequence of this actor.
+ bool mCanSend;
+
+ // False until the actor is destroyed.
+ bool mActorDestroyed;
+
+ bool mPaused;
+
+ /**
+ * Hold TextureClients refs until end of their usages on host side.
+ * It defer calling of TextureClient recycle callback.
+ */
+ std::unordered_map<uint64_t, RefPtr<TextureClient>>
+ mTexturesWaitingNotifyNotUsed;
+
+ nsCOMPtr<nsISerialEventTarget> mThread;
+
+ AutoTArray<RefPtr<TextureClientPool>, 2> mTexturePools;
+
+ uint64_t mProcessToken;
+
+ FixedSizeSmallShmemSectionAllocator* mSectionAllocator;
+
+ // TextureClients that must be kept alive during async painting. This
+ // is only accessed on the main thread.
+ nsTArray<RefPtr<TextureClient>> mTextureClientsForAsyncPaint;
+
+ RefPtr<CanvasChild> mCanvasChild;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorBrigedChild_h
diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp
new file mode 100644
index 0000000000..3982791357
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -0,0 +1,2008 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorBridgeParent.h"
+
+#include <stdio.h> // for fprintf, stdout
+#include <stdint.h> // for uint64_t
+#include <map> // for _Rb_tree_iterator, etc
+#include <utility> // for pair
+
+#include "apz/src/APZCTreeManager.h" // for APZCTreeManager
+#include "base/process.h" // for ProcessId
+#include "gfxContext.h" // for gfxContext
+#include "gfxPlatform.h" // for gfxPlatform
+#include "TreeTraversal.h" // for ForEachNode
+#ifdef MOZ_WIDGET_GTK
+# include "gfxPlatformGtk.h" // for gfxPlatform
+#endif
+#include "mozilla/AutoRestore.h" // for AutoRestore
+#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for IntSize
+#include "mozilla/gfx/gfxVars.h" // for gfxVars
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZSampler.h" // for APZSampler
+#include "mozilla/layers/APZThreadUtils.h" // for APZThreadUtils
+#include "mozilla/layers/APZUpdater.h" // for APZUpdater
+#include "mozilla/layers/CompositionRecorder.h" // for CompositionRecorder
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorAnimationStorage.h" // for CompositorAnimationStorage
+#include "mozilla/layers/CompositorManagerParent.h" // for CompositorManagerParent
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/CompositorVsyncScheduler.h"
+#include "mozilla/layers/ContentCompositorBridgeParent.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/OMTASampler.h"
+#include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/PodOperations.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/glean/GleanMetrics.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsTArray.h" // for nsTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#ifdef XP_WIN
+# include "mozilla/layers/CompositorD3D11.h"
+# include "mozilla/widget/WinCompositorWidget.h"
+# include "mozilla/WindowsVersion.h"
+#endif
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/VsyncDispatcher.h"
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+# include "VsyncSource.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetParent.h"
+#endif
+#ifdef XP_WIN
+# include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+namespace mozilla {
+
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+
+using base::ProcessId;
+
+using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON;
+
+/* static*/
+StaticMonitor CompositorBridgeParent::sIndirectLayerTreesLock;
+
+/* static */
+CompositorBridgeParent::LayerTreeMap CompositorBridgeParent::sIndirectLayerTrees
+ MOZ_GUARDED_BY(CompositorBridgeParent::sIndirectLayerTreesLock);
+
+/// Equivalent to asserting CompositorThreadHolder::IsInCompositorThread with
+/// the addition that it doesn't assert if the compositor thread holder is
+/// already gone during late shutdown.
+static void AssertIsInCompositorThread() {
+ MOZ_RELEASE_ASSERT(!CompositorThread() ||
+ CompositorThreadHolder::IsInCompositorThread());
+}
+
+CompositorBridgeParentBase::CompositorBridgeParentBase(
+ CompositorManagerParent* aManager)
+ : mCanSend(true), mCompositorManager(aManager) {}
+
+CompositorBridgeParentBase::~CompositorBridgeParentBase() = default;
+
+ProcessId CompositorBridgeParentBase::GetChildProcessId() { return OtherPid(); }
+
+dom::ContentParentId CompositorBridgeParentBase::GetContentId() {
+ return mCompositorManager->GetContentId();
+}
+
+void CompositorBridgeParentBase::NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) {
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
+ if (!texture) {
+ return;
+ }
+
+ if (!(texture->GetFlags() & TextureFlags::RECYCLE) &&
+ !(texture->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END)) {
+ return;
+ }
+
+ uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
+ mPendingAsyncMessage.push_back(OpNotifyNotUsed(textureId, aTransactionId));
+}
+
+void CompositorBridgeParentBase::SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) {
+ Unused << SendParentAsyncMessages(aMessage);
+}
+
+bool CompositorBridgeParentBase::AllocShmem(size_t aSize, ipc::Shmem* aShmem) {
+ return PCompositorBridgeParent::AllocShmem(aSize, aShmem);
+}
+
+bool CompositorBridgeParentBase::AllocUnsafeShmem(size_t aSize,
+ ipc::Shmem* aShmem) {
+ return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aShmem);
+}
+
+bool CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem) {
+ return PCompositorBridgeParent::DeallocShmem(aShmem);
+}
+
+CompositorBridgeParent::LayerTreeState::LayerTreeState()
+ : mApzcTreeManagerParent(nullptr),
+ mParent(nullptr),
+ mContentCompositorBridgeParent(nullptr) {}
+
+CompositorBridgeParent::LayerTreeState::~LayerTreeState() {
+ if (mController) {
+ mController->Destroy();
+ }
+}
+
+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() {
+ MOZ_ASSERT(NS_IsMainThread());
+ 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...
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ sIndirectLayerTrees.clear();
+}
+
+CompositorBridgeParent::CompositorBridgeParent(
+ CompositorManagerParent* aManager, CSSToLayoutDeviceScale aScale,
+ const TimeDuration& aVsyncRate, const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize,
+ uint64_t aInnerWindowId)
+ : CompositorBridgeParentBase(aManager),
+ mWidget(nullptr),
+ mScale(aScale),
+ mVsyncRate(aVsyncRate),
+ mPaused(false),
+ mHaveCompositionRecorder(false),
+ mIsForcedFirstPaint(false),
+ mUseExternalSurfaceSize(aUseExternalSurfaceSize),
+ mEGLSurfaceSize(aSurfaceSize),
+ mOptions(aOptions),
+ mCompositorBridgeID(0),
+ mRootLayerTreeID{0},
+ mInnerWindowId(aInnerWindowId),
+ mCompositorScheduler(nullptr),
+ mAnimationStorage(nullptr) {}
+
+void CompositorBridgeParent::InitSameProcess(widget::CompositorWidget* aWidget,
+ const LayersId& aLayerTreeId) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mWidget = aWidget;
+ mRootLayerTreeID = aLayerTreeId;
+#if defined(XP_WIN)
+ // when run in headless mode, no WinCompositorWidget is created
+ if (widget::WinCompositorWidget* windows = mWidget->AsWindows()) {
+ windows->SetRootLayerTreeID(mRootLayerTreeID);
+ }
+#endif
+
+ Initialize();
+}
+
+bool CompositorBridgeParent::IsPaused() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mPaused;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvInitialize(
+ const LayersId& aRootLayerTreeId) {
+ MOZ_ASSERT(XRE_IsGPUProcess());
+
+ mRootLayerTreeID = aRootLayerTreeId;
+#ifdef XP_WIN
+ // headless mode is probably always same-process; but just in case...
+ if (widget::WinCompositorWidget* windows = mWidget->AsWindows()) {
+ windows->SetRootLayerTreeID(mRootLayerTreeID);
+ }
+#endif
+
+ Initialize();
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::Initialize() {
+ MOZ_ASSERT(CompositorThread(),
+ "The compositor thread must be Initialized before instanciating a "
+ "CompositorBridgeParent.");
+
+ if (mOptions.UseAPZ()) {
+ MOZ_ASSERT(!mApzcTreeManager);
+ MOZ_ASSERT(!mApzSampler);
+ MOZ_ASSERT(!mApzUpdater);
+ mApzcTreeManager = APZCTreeManager::Create(mRootLayerTreeID);
+ mApzSampler = new APZSampler(mApzcTreeManager, true);
+ mApzUpdater = new APZUpdater(mApzcTreeManager, true);
+ }
+
+ CompositorAnimationStorage* animationStorage = GetAnimationStorage();
+ mOMTASampler = new OMTASampler(animationStorage, mRootLayerTreeID);
+
+ mPaused = mOptions.InitiallyPaused();
+
+ mCompositorBridgeID = 0;
+ // FIXME: This holds on the the fact that right now the only thing that
+ // can destroy this instance is initialized on the compositor thread after
+ // this task has been processed.
+ MOZ_ASSERT(CompositorThread());
+ CompositorThread()->Dispatch(NewRunnableFunction(
+ "AddCompositorRunnable", &AddCompositor, this, &mCompositorBridgeID));
+
+ { // scope lock
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
+ }
+}
+
+LayersId CompositorBridgeParent::RootLayerTreeId() {
+ MOZ_ASSERT(mRootLayerTreeID.IsValid());
+ return mRootLayerTreeID;
+}
+
+CompositorBridgeParent::~CompositorBridgeParent() {
+ MOZ_DIAGNOSTIC_ASSERT(
+ !mCanSend,
+ "ActorDestroy or RecvWillClose should have been called first.");
+ MOZ_DIAGNOSTIC_ASSERT(mRefCnt == 0,
+ "ActorDealloc should have been called first.");
+ nsTArray<PTextureParent*> textures;
+ ManagedPTextureParent(textures);
+ // We expect all textures to be destroyed by now.
+ MOZ_DIAGNOSTIC_ASSERT(textures.Length() == 0);
+ for (unsigned int i = 0; i < textures.Length(); ++i) {
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
+ tex->DeallocateDeviceData();
+ }
+ // Check if WebRender/Compositor was shutdown.
+ if (mWrBridge) {
+ gfxCriticalNote << "CompositorBridgeParent destroyed without shutdown";
+ }
+}
+
+void CompositorBridgeParent::ForceIsFirstPaint() {
+ if (mWrBridge) {
+ mIsForcedFirstPaint = true;
+ }
+}
+
+void CompositorBridgeParent::StopAndClearResources() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mPaused = true;
+
+ // We need to clear the APZ tree before we destroy the WebRender API below,
+ // because in the case of async scene building that will shut down the updater
+ // thread and we need to run the task before that happens.
+ MOZ_ASSERT((mApzSampler != nullptr) == (mApzcTreeManager != nullptr));
+ MOZ_ASSERT((mApzUpdater != nullptr) == (mApzcTreeManager != nullptr));
+ if (mApzUpdater) {
+ mApzSampler->Destroy();
+ mApzSampler = nullptr;
+ mApzUpdater->ClearTree(mRootLayerTreeID);
+ mApzUpdater = nullptr;
+ mApzcTreeManager = nullptr;
+ }
+
+ if (mWrBridge) {
+ // Ensure we are not holding the sIndirectLayerTreesLock when destroying
+ // the WebRenderBridgeParent instances because it may block on WR.
+ std::vector<RefPtr<WebRenderBridgeParent>> indirectBridgeParents;
+ { // scope lock
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([&](LayerTreeState* lts, LayersId) -> void {
+ if (lts->mWrBridge) {
+ indirectBridgeParents.emplace_back(lts->mWrBridge.forget());
+ }
+ lts->mParent = nullptr;
+ });
+ }
+ for (const RefPtr<WebRenderBridgeParent>& bridge : indirectBridgeParents) {
+ bridge->Destroy();
+ }
+ indirectBridgeParents.clear();
+
+ RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+ // Ensure we are not holding the sIndirectLayerTreesLock here because we
+ // are going to block on WR threads in order to shut it down properly.
+ mWrBridge->Destroy();
+ mWrBridge = nullptr;
+
+ if (api) {
+ // Make extra sure we are done cleaning WebRender up before continuing.
+ // After that we wont have a way to talk to a lot of the webrender parts.
+ api->FlushSceneBuilder();
+ api = nullptr;
+ }
+
+ if (mAsyncImageManager) {
+ mAsyncImageManager->Destroy();
+ // WebRenderAPI should be already destructed
+ mAsyncImageManager = nullptr;
+ }
+ }
+
+ // This must be destroyed now since it accesses the widget.
+ if (mCompositorScheduler) {
+ mCompositorScheduler->Destroy();
+ mCompositorScheduler = nullptr;
+ }
+
+ if (mOMTASampler) {
+ mOMTASampler->Destroy();
+ mOMTASampler = nullptr;
+ }
+
+ // After this point, it is no longer legal to access the widget.
+ mWidget = nullptr;
+
+ // Clear mAnimationStorage here to ensure that the compositor thread
+ // still exists when we destroy it.
+ mAnimationStorage = nullptr;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvWillClose() {
+ StopAndClearResources();
+ // Once we get the WillClose message, the client side is going to go away
+ // soon and we can't be guaranteed that sending messages will work.
+ mCanSend = false;
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::DeferredDestroy() {
+ MOZ_ASSERT(!NS_IsMainThread());
+ mSelfRef = nullptr;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvPause() {
+ PauseComposition();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvRequestFxrOutput() {
+#ifdef XP_WIN
+ // Continue forwarding the request to the Widget + SwapChain
+ mWidget->AsWindows()->RequestFxrOutput();
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvResume() {
+ ResumeComposition();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvResumeAsync() {
+ ResumeComposition();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+CompositorBridgeParent::RecvWaitOnTransactionProcessed() {
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRendering(
+ const wr::RenderReasons& aReasons) {
+ if (mWrBridge) {
+ mWrBridge->FlushRendering(aReasons);
+ return IPC_OK();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyMemoryPressure() {
+ NotifyMemoryPressure();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvFlushRenderingAsync(
+ const wr::RenderReasons& aReasons) {
+ if (mWrBridge) {
+ mWrBridge->FlushRendering(aReasons, false);
+ return IPC_OK();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvForcePresent(
+ const wr::RenderReasons& aReasons) {
+ if (mWrBridge) {
+ mWrBridge->ScheduleForcedGenerateFrame(aReasons);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvStartFrameTimeRecording(
+ const int32_t& aBufferSize, uint32_t* aOutStartIndex) {
+ if (mWrBridge) {
+ *aOutStartIndex = mWrBridge->StartFrameTimeRecording(aBufferSize);
+ } else {
+ *aOutStartIndex = 0;
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvStopFrameTimeRecording(
+ const uint32_t& aStartIndex, nsTArray<float>* intervals) {
+ if (mWrBridge) {
+ mWrBridge->StopFrameTimeRecording(aStartIndex, *intervals);
+ }
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::ActorDestroy(ActorDestroyReason why) {
+ mCanSend = false;
+
+ StopAndClearResources();
+
+ RemoveCompositor(mCompositorBridgeID);
+
+ { // scope lock
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ sIndirectLayerTrees.erase(mRootLayerTreeID);
+ }
+
+ // There are chances that the ref count reaches zero on the main thread
+ // shortly after this function returns while some ipdl code still needs to run
+ // on this thread. We must keep the compositor parent alive untill the code
+ // handling message reception is finished on this thread.
+ mSelfRef = this;
+ NS_GetCurrentThread()->Dispatch(
+ NewRunnableMethod("layers::CompositorBridgeParent::DeferredDestroy", this,
+ &CompositorBridgeParent::DeferredDestroy));
+}
+
+void CompositorBridgeParent::ScheduleRenderOnCompositorThread(
+ wr::RenderReasons aReasons) {
+ MOZ_ASSERT(CompositorThread());
+ CompositorThread()->Dispatch(NewRunnableMethod<wr::RenderReasons>(
+ "layers::CompositorBridgeParent::ScheduleComposition", this,
+ &CompositorBridgeParent::ScheduleComposition, aReasons));
+}
+
+void CompositorBridgeParent::PauseComposition() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "PauseComposition() can only be called on the compositor thread");
+
+ if (!mPaused) {
+ mPaused = true;
+
+ TimeStamp now = TimeStamp::Now();
+ if (mWrBridge) {
+ mWrBridge->Pause();
+ NotifyPipelineRendered(mWrBridge->PipelineId(),
+ mWrBridge->GetCurrentEpoch(), VsyncId(), now, now,
+ now);
+ }
+ }
+}
+
+bool CompositorBridgeParent::ResumeComposition() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
+ "ResumeComposition() can only be called on the compositor thread");
+
+ bool resumed = mWidget->OnResumeComposition();
+ resumed = resumed && mWrBridge->Resume();
+
+ if (!resumed) {
+#ifdef MOZ_WIDGET_ANDROID
+ // We can't get a surface. This could be because the activity changed
+ // between the time resume was scheduled and now.
+ __android_log_print(
+ ANDROID_LOG_INFO, "CompositorBridgeParent",
+ "Unable to renew compositor surface; remaining in paused state");
+#endif
+ return false;
+ }
+
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mPaused = false;
+
+ mCompositorScheduler->ForceComposeToTarget(wr::RenderReasons::WIDGET, nullptr,
+ nullptr);
+ return true;
+}
+
+void CompositorBridgeParent::SetEGLSurfaceRect(int x, int y, int width,
+ int height) {
+ NS_ASSERTION(mUseExternalSurfaceSize,
+ "Compositor created without UseExternalSurfaceSize provided");
+ mEGLSurfaceSize.SizeTo(width, height);
+}
+
+bool CompositorBridgeParent::ResumeCompositionAndResize(int x, int y, int width,
+ int height) {
+ SetEGLSurfaceRect(x, y, width, height);
+ return ResumeComposition();
+}
+
+void CompositorBridgeParent::ScheduleComposition(wr::RenderReasons aReasons) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mPaused) {
+ return;
+ }
+
+ if (mWrBridge) {
+ mWrBridge->ScheduleGenerateFrame(aReasons);
+ }
+}
+
+PAPZCTreeManagerParent* CompositorBridgeParent::AllocPAPZCTreeManagerParent(
+ const LayersId& aLayersId) {
+ // This should only ever get called in the GPU process.
+ MOZ_ASSERT(XRE_IsGPUProcess());
+ // We should only ever get this if APZ is enabled in this compositor.
+ MOZ_ASSERT(mOptions.UseAPZ());
+ // The mApzcTreeManager and mApzUpdater should have been created via
+ // RecvInitialize()
+ MOZ_ASSERT(mApzcTreeManager);
+ MOZ_ASSERT(mApzUpdater);
+ // The main process should pass in 0 because we assume mRootLayerTreeID
+ MOZ_ASSERT(!aLayersId.IsValid());
+
+ StaticMonitorAutoLock 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 StaticMonitorAutoLock& 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();
+
+ StaticMonitorAutoLock 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) {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ return sIndirectLayerTrees[aLayersId].mParent;
+}
+
+/*static*/
+RefPtr<CompositorBridgeParent>
+CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(
+ const wr::WindowId& aWindowId) {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ for (auto it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end();
+ it++) {
+ LayerTreeState* state = &it->second;
+ if (!state->mWrBridge) {
+ continue;
+ }
+ // state->mWrBridge might be a root WebRenderBridgeParent or one of a
+ // content process, but in either case the state->mParent will be the same.
+ // So we don't need to distinguish between the two.
+ if (RefPtr<wr::WebRenderAPI> api = state->mWrBridge->GetWebRenderAPI()) {
+ if (api->GetId() == aWindowId) {
+ return state->mParent;
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool CompositorBridgeParent::SetTestSampleTime(const LayersId& aId,
+ const TimeStamp& aTime) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ if (aTime.IsNull()) {
+ return false;
+ }
+
+ mTestTime = Some(aTime);
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetTestSampleTime(mTestTime);
+ }
+
+ if (mWrBridge) {
+ mWrBridge->FlushRendering(wr::RenderReasons::TESTING);
+ return true;
+ }
+
+ return true;
+}
+
+void CompositorBridgeParent::LeaveTestMode(const LayersId& aId) {
+ mTestTime = Nothing();
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetTestSampleTime(mTestTime);
+ }
+}
+
+CompositorAnimationStorage* CompositorBridgeParent::GetAnimationStorage() {
+ if (!mAnimationStorage) {
+ mAnimationStorage = new CompositorAnimationStorage(this);
+ }
+ return mAnimationStorage;
+}
+
+void CompositorBridgeParent::NotifyJankedAnimations(
+ const JankedAnimations& aJankedAnimations) {
+ MOZ_ASSERT(!aJankedAnimations.empty());
+
+ if (StaticPrefs::layout_animation_prerender_partial_jank()) {
+ return;
+ }
+
+ for (const auto& entry : aJankedAnimations) {
+ const LayersId& layersId = entry.first;
+ const nsTArray<uint64_t>& animations = entry.second;
+ if (layersId == mRootLayerTreeID) {
+ if (mWrBridge) {
+ Unused << SendNotifyJankedAnimations(LayersId{0}, animations);
+ }
+ // It unlikely happens multiple processes have janked animations at same
+ // time, so it should be fine with enumerating sIndirectLayerTrees every
+ // time.
+ } else if (const LayerTreeState* state = GetIndirectShadowTree(layersId)) {
+ if (ContentCompositorBridgeParent* cpcp =
+ state->mContentCompositorBridgeParent) {
+ Unused << cpcp->SendNotifyJankedAnimations(layersId, animations);
+ }
+ }
+ }
+}
+
+void CompositorBridgeParent::SetTestAsyncScrollOffset(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) {
+ if (mApzUpdater) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ mApzUpdater->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
+ }
+}
+
+void CompositorBridgeParent::SetTestAsyncZoom(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) {
+ if (mApzUpdater) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ mApzUpdater->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
+ }
+}
+
+void CompositorBridgeParent::FlushApzRepaints(const LayersId& aLayersId) {
+ MOZ_ASSERT(mApzUpdater);
+ MOZ_ASSERT(aLayersId.IsValid());
+ mApzUpdater->RunOnControllerThread(
+ aLayersId, NS_NewRunnableFunction(
+ "layers::CompositorBridgeParent::FlushApzRepaints",
+ [=]() { APZCTreeManager::FlushApzRepaints(aLayersId); }));
+}
+
+void CompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) {
+ if (mApzUpdater) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ mApzUpdater->GetAPZTestData(aLayersId, aOutData);
+ }
+}
+
+void CompositorBridgeParent::GetFrameUniformity(const LayersId& aLayersId,
+ FrameUniformityData* aOutData) {
+}
+
+void CompositorBridgeParent::SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) {
+ if (!mApzcTreeManager || !mApzUpdater) {
+ return;
+ }
+ // Need to specifically bind this since it's overloaded.
+ void (APZCTreeManager::*setTargetApzcFunc)(
+ uint64_t, const nsTArray<ScrollableLayerGuid>&) =
+ &APZCTreeManager::SetTargetAPZC;
+ RefPtr<Runnable> task =
+ NewRunnableMethod<uint64_t,
+ StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
+ "layers::CompositorBridgeParent::SetConfirmedTargetAPZC",
+ mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId,
+ std::move(aTargets));
+ mApzUpdater->RunOnUpdaterThread(aLayersId, task.forget());
+}
+
+void CompositorBridgeParent::SetFixedLayerMargins(ScreenIntCoord aTop,
+ ScreenIntCoord aBottom) {
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetFixedLayerMargins(aTop, aBottom);
+ }
+
+ ScheduleComposition(wr::RenderReasons::RESIZE);
+}
+
+void CompositorBridgeParent::AddCompositor(CompositorBridgeParent* compositor,
+ uint64_t* outID) {
+ AssertIsInCompositorThread();
+
+ static uint64_t sNextID = 1;
+
+ ++sNextID;
+ (*sCompositorMap)[sNextID] = compositor;
+ *outID = sNextID;
+}
+
+CompositorBridgeParent* CompositorBridgeParent::RemoveCompositor(uint64_t id) {
+ AssertIsInCompositorThread();
+
+ CompositorMap::iterator it = sCompositorMap->find(id);
+ if (it == sCompositorMap->end()) {
+ return nullptr;
+ }
+ CompositorBridgeParent* retval = it->second;
+ sCompositorMap->erase(it);
+ return retval;
+}
+
+void CompositorBridgeParent::NotifyVsync(const VsyncEvent& aVsync,
+ const LayersId& aLayersId) {
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ auto it = sIndirectLayerTrees.find(aLayersId);
+ if (it == sIndirectLayerTrees.end()) return;
+
+ CompositorBridgeParent* cbp = it->second.mParent;
+ if (!cbp || !cbp->mWidget) return;
+
+ RefPtr<VsyncObserver> obs = cbp->mWidget->GetVsyncObserver();
+ if (!obs) return;
+
+ obs->NotifyVsync(aVsync);
+}
+
+/* static */
+void CompositorBridgeParent::ScheduleForcedComposition(
+ const LayersId& aLayersId, wr::RenderReasons aReasons) {
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ auto it = sIndirectLayerTrees.find(aLayersId);
+ if (it == sIndirectLayerTrees.end()) {
+ return;
+ }
+
+ CompositorBridgeParent* cbp = it->second.mParent;
+ if (!cbp || !cbp->mWidget) {
+ return;
+ }
+
+ if (cbp->mWrBridge) {
+ cbp->mWrBridge->ScheduleForcedGenerateFrame(aReasons);
+ }
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyChildCreated(
+ const LayersId& child, CompositorOptions* aOptions) {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ NotifyChildCreated(child);
+ *aOptions = mOptions;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvNotifyChildRecreated(
+ const LayersId& aChild, CompositorOptions* aOptions) {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+
+ if (sIndirectLayerTrees.find(aChild) != sIndirectLayerTrees.end()) {
+ NS_WARNING("Invalid to register the same layer tree twice");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ NotifyChildCreated(aChild);
+ *aOptions = mOptions;
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::NotifyChildCreated(LayersId aChild) {
+ sIndirectLayerTreesLock.AssertCurrentThreadOwns();
+ sIndirectLayerTrees[aChild].mParent = this;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvMapAndNotifyChildCreated(
+ const LayersId& aChild, const base::ProcessId& aOwnerPid,
+ CompositorOptions* aOptions) {
+ // We only use this message when the remote compositor is in the GPU process.
+ // It is harmless to call it, though.
+ MOZ_ASSERT(XRE_IsGPUProcess());
+
+ LayerTreeOwnerTracker::Get()->Map(aChild, aOwnerPid);
+
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ NotifyChildCreated(aChild);
+ *aOptions = mOptions;
+ return IPC_OK();
+}
+
+enum class CompositorOptionsChangeKind {
+ eSupported,
+ eBestEffort,
+ eUnsupported
+};
+
+static CompositorOptionsChangeKind ClassifyCompositorOptionsChange(
+ const CompositorOptions& aOld, const CompositorOptions& aNew) {
+ if (aOld == aNew) {
+ return CompositorOptionsChangeKind::eSupported;
+ }
+ if (aOld.EqualsIgnoringApzEnablement(aNew)) {
+ return CompositorOptionsChangeKind::eBestEffort;
+ }
+ return CompositorOptionsChangeKind::eUnsupported;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvAdoptChild(
+ const LayersId& child) {
+ RefPtr<APZUpdater> oldApzUpdater;
+ APZCTreeManagerParent* parent;
+ bool apzEnablementChanged = false;
+ RefPtr<WebRenderBridgeParent> childWrBridge;
+
+ // Before adopting the child, save the old compositor's root content
+ // controller. We may need this to clear old layer transforms associated
+ // with the child.
+ // This is outside the lock because GetGeckoContentControllerForRoot()
+ // does its own locking.
+ RefPtr<GeckoContentController> oldRootController =
+ GetGeckoContentControllerForRoot(child);
+
+ { // scope lock
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ // If child is already belong to this CompositorBridgeParent,
+ // no need to handle adopting child.
+ if (sIndirectLayerTrees[child].mParent == this) {
+ return IPC_OK();
+ }
+
+ if (sIndirectLayerTrees[child].mParent) {
+ switch (ClassifyCompositorOptionsChange(
+ sIndirectLayerTrees[child].mParent->mOptions, mOptions)) {
+ case CompositorOptionsChangeKind::eUnsupported: {
+ MOZ_ASSERT(false,
+ "Moving tab between windows whose compositor options"
+ "differ in unsupported ways. Things may break in "
+ "unexpected ways");
+ break;
+ }
+ case CompositorOptionsChangeKind::eBestEffort: {
+ NS_WARNING(
+ "Moving tab between windows with different APZ enablement. "
+ "This is supported on a best-effort basis, but some things may "
+ "break.");
+ apzEnablementChanged = true;
+ break;
+ }
+ case CompositorOptionsChangeKind::eSupported: {
+ // The common case, no action required.
+ break;
+ }
+ }
+ oldApzUpdater = sIndirectLayerTrees[child].mParent->mApzUpdater;
+ }
+ if (mWrBridge) {
+ childWrBridge = sIndirectLayerTrees[child].mWrBridge;
+ }
+ parent = sIndirectLayerTrees[child].mApzcTreeManagerParent;
+ }
+
+ if (childWrBridge) {
+ MOZ_ASSERT(mWrBridge);
+ RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+ api = api->Clone();
+ wr::Epoch newEpoch = childWrBridge->UpdateWebRender(
+ mWrBridge->CompositorScheduler(), std::move(api),
+ mWrBridge->AsyncImageManager(),
+ mWrBridge->GetTextureFactoryIdentifier());
+ // Pretend we composited, since parent CompositorBridgeParent was replaced.
+ TimeStamp now = TimeStamp::Now();
+ NotifyPipelineRendered(childWrBridge->PipelineId(), newEpoch, VsyncId(),
+ now, now, now);
+ }
+
+ {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ // Update sIndirectLayerTrees[child].mParent after
+ // WebRenderBridgeParent::UpdateWebRender().
+ NotifyChildCreated(child);
+ }
+
+ if (oldApzUpdater) {
+ // If we are moving a child from an APZ-enabled window to an APZ-disabled
+ // window (which can happen if e.g. a WebExtension moves a tab into a
+ // popup window), try to handle it gracefully by clearing the old layer
+ // transforms associated with the child. (Since the new compositor is
+ // APZ-disabled, there will be nothing to update the transforms going
+ // forward.)
+ if (!mApzUpdater && oldRootController) {
+ // Tell the old APZCTreeManager not to send any more layer transforms
+ // for this layers ids.
+ oldApzUpdater->MarkAsDetached(child);
+
+ // Clear the current transforms.
+ nsTArray<MatrixMessage> clear;
+ clear.AppendElement(MatrixMessage(Nothing(), ScreenRect(), child));
+ oldRootController->NotifyLayerTransforms(std::move(clear));
+ }
+ }
+ if (mApzUpdater) {
+ if (parent) {
+ MOZ_ASSERT(mApzcTreeManager);
+ parent->ChildAdopted(mApzcTreeManager, mApzUpdater);
+ }
+ mApzUpdater->NotifyLayerTreeAdopted(child, oldApzUpdater);
+ }
+ if (apzEnablementChanged) {
+ Unused << SendCompositorOptionsChanged(child, mOptions);
+ }
+ return IPC_OK();
+}
+
+PWebRenderBridgeParent* CompositorBridgeParent::AllocPWebRenderBridgeParent(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize,
+ const WindowKind& aWindowKind) {
+ MOZ_ASSERT(wr::AsLayersId(aPipelineId) == mRootLayerTreeID);
+ MOZ_ASSERT(!mWrBridge);
+ MOZ_ASSERT(!mCompositorScheduler);
+ MOZ_ASSERT(mWidget);
+
+#ifdef XP_WIN
+ if (mWidget && mWidget->AsWindows()) {
+ const auto options = mWidget->GetCompositorOptions();
+ if (!options.UseSoftwareWebRender() &&
+ (DeviceManagerDx::Get()->CanUseDComp() ||
+ gfxVars::UseWebRenderFlipSequentialWin())) {
+ mWidget->AsWindows()->EnsureCompositorWindow();
+ } else if (options.UseSoftwareWebRender() &&
+ mWidget->AsWindows()->GetCompositorHwnd()) {
+ mWidget->AsWindows()->DestroyCompositorWindow();
+ }
+ }
+#endif
+
+ RefPtr<widget::CompositorWidget> widget = mWidget;
+ wr::WrWindowId windowId = wr::NewWindowId();
+ if (mApzUpdater) {
+ // If APZ is enabled, we need to register the APZ updater with the window id
+ // before the updater thread is created in WebRenderAPI::Create, so
+ // that the callback from the updater thread can find the right APZUpdater.
+ mApzUpdater->SetWebRenderWindowId(windowId);
+ }
+ if (mApzSampler) {
+ // Same as for mApzUpdater, but for the sampler thread.
+ mApzSampler->SetWebRenderWindowId(windowId);
+ }
+ if (mOMTASampler) {
+ // Same, but for the OMTA sampler.
+ mOMTASampler->SetWebRenderWindowId(windowId);
+ }
+
+ nsCString error("FEATURE_FAILURE_WEBRENDER_INITIALIZE_UNSPECIFIED");
+ RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(
+ this, std::move(widget), windowId, aSize, aWindowKind, error);
+ if (!api) {
+ mWrBridge =
+ WebRenderBridgeParent::CreateDestroyed(aPipelineId, std::move(error));
+ mWrBridge.get()->AddRef(); // IPDL reference
+ return mWrBridge;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ // On Android, WebRenderAPI::Resume() call is triggered from Java side. But
+ // Java side does not know about fallback to RenderCompositorOGLSWGL. In this
+ // fallback case, RenderCompositor::Resume() needs to be called from gfx code.
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mPaused && mWidget->GetCompositorOptions().UseSoftwareWebRender() &&
+ mWidget->GetCompositorOptions().AllowSoftwareWebRenderOGL()) {
+ api->Resume();
+ }
+#endif
+
+ wr::TransactionBuilder txn(api);
+ txn.SetRootPipeline(aPipelineId);
+ api->SendTransaction(txn);
+
+ bool useCompositorWnd = false;
+#ifdef XP_WIN
+ // Headless mode uses HeadlessWidget.
+ if (mWidget->AsWindows()) {
+ useCompositorWnd = !!mWidget->AsWindows()->GetCompositorHwnd();
+ }
+#endif
+ mAsyncImageManager =
+ new AsyncImagePipelineManager(api->Clone(), useCompositorWnd);
+ RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager;
+ mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr,
+ std::move(api), std::move(asyncMgr),
+ mVsyncRate);
+ mWrBridge.get()->AddRef(); // IPDL reference
+
+ mAsyncImageManager->SetTextureFactoryIdentifier(
+ mWrBridge->GetTextureFactoryIdentifier());
+
+ mCompositorScheduler = mWrBridge->CompositorScheduler();
+ MOZ_ASSERT(mCompositorScheduler);
+ { // scope lock
+ StaticMonitorAutoLock 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);
+ {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ auto it = sIndirectLayerTrees.find(wr::AsLayersId(parent->PipelineId()));
+ if (it != sIndirectLayerTrees.end()) {
+ it->second.mWrBridge = nullptr;
+ }
+ }
+ parent->Release(); // IPDL reference
+ return true;
+}
+
+void CompositorBridgeParent::NotifyMemoryPressure() {
+ if (mWrBridge) {
+ RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+ if (api) {
+ api->NotifyMemoryPressure();
+ }
+ }
+}
+
+void CompositorBridgeParent::AccumulateMemoryReport(wr::MemoryReport* aReport) {
+ if (mWrBridge) {
+ RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+ if (api) {
+ api->AccumulateMemoryReport(aReport);
+ }
+ }
+}
+
+/*static*/
+void CompositorBridgeParent::InitializeStatics() {
+ gfxVars::SetForceSubpixelAAWherePossibleListener(&UpdateQualitySettings);
+ gfxVars::SetWebRenderDebugFlagsListener(&UpdateDebugFlags);
+ gfxVars::SetWebRenderBoolParametersListener(&UpdateWebRenderBoolParameters);
+ gfxVars::SetWebRenderBatchingLookbackListener(&UpdateWebRenderParameters);
+ gfxVars::SetWebRenderBlobTileSizeListener(&UpdateWebRenderParameters);
+ gfxVars::SetWebRenderBatchedUploadThresholdListener(
+ &UpdateWebRenderParameters);
+
+ gfxVars::SetWebRenderProfilerUIListener(&UpdateWebRenderProfilerUI);
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateQualitySettings() {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ if (CompositorThread()) {
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("CompositorBridgeParent::UpdateQualitySettings",
+ &CompositorBridgeParent::UpdateQualitySettings));
+ }
+
+ // If there is no compositor thread, e.g. due to shutdown, then we can
+ // safefully just ignore this request.
+ return;
+ }
+
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ if (!wrBridge->IsRootWebRenderBridgeParent()) {
+ return;
+ }
+ wrBridge->UpdateQualitySettings();
+ });
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateDebugFlags() {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ if (CompositorThread()) {
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("CompositorBridgeParent::UpdateDebugFlags",
+ &CompositorBridgeParent::UpdateDebugFlags));
+ }
+
+ // If there is no compositor thread, e.g. due to shutdown, then we can
+ // safefully just ignore this request.
+ return;
+ }
+
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ if (!wrBridge->IsRootWebRenderBridgeParent()) {
+ return;
+ }
+ wrBridge->UpdateDebugFlags();
+ });
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateWebRenderBoolParameters() {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ if (CompositorThread()) {
+ CompositorThread()->Dispatch(NewRunnableFunction(
+ "CompositorBridgeParent::UpdateWebRenderBoolParameters",
+ &CompositorBridgeParent::UpdateWebRenderBoolParameters));
+ }
+
+ return;
+ }
+
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ if (!wrBridge->IsRootWebRenderBridgeParent()) {
+ return;
+ }
+ wrBridge->UpdateBoolParameters();
+ });
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateWebRenderParameters() {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ if (CompositorThread()) {
+ CompositorThread()->Dispatch(NewRunnableFunction(
+ "CompositorBridgeParent::UpdateWebRenderParameters",
+ &CompositorBridgeParent::UpdateWebRenderParameters));
+ }
+
+ return;
+ }
+
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ if (!wrBridge->IsRootWebRenderBridgeParent()) {
+ return;
+ }
+ wrBridge->UpdateParameters();
+ });
+}
+
+/*static*/
+void CompositorBridgeParent::UpdateWebRenderProfilerUI() {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ ForEachWebRenderBridgeParent([&](WebRenderBridgeParent* wrBridge) -> void {
+ if (!wrBridge->IsRootWebRenderBridgeParent()) {
+ return;
+ }
+ wrBridge->UpdateProfilerUI();
+ });
+}
+
+RefPtr<WebRenderBridgeParent> CompositorBridgeParent::GetWebRenderBridgeParent()
+ const {
+ return mWrBridge;
+}
+
+Maybe<TimeStamp> CompositorBridgeParent::GetTestingTimeStamp() const {
+ return mTestTime;
+}
+
+void EraseLayerState(LayersId aId) {
+ RefPtr<APZUpdater> apz;
+ RefPtr<WebRenderBridgeParent> wrBridge;
+
+ { // scope lock
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ auto iter = CompositorBridgeParent::sIndirectLayerTrees.find(aId);
+ if (iter != CompositorBridgeParent::sIndirectLayerTrees.end()) {
+ CompositorBridgeParent* parent = iter->second.mParent;
+ if (parent) {
+ apz = parent->GetAPZUpdater();
+ }
+ wrBridge = iter->second.mWrBridge;
+ CompositorBridgeParent::sIndirectLayerTrees.erase(iter);
+ }
+ }
+
+ if (apz) {
+ apz->NotifyLayerTreeRemoved(aId);
+ }
+
+ if (wrBridge) {
+ wrBridge->Destroy();
+ }
+}
+
+/*static*/
+void CompositorBridgeParent::DeallocateLayerTreeId(LayersId aId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ // Here main thread notifies compositor to remove an element from
+ // sIndirectLayerTrees. This removed element might be queried soon.
+ // Checking the elements of sIndirectLayerTrees exist or not before using.
+ if (!CompositorThread()) {
+ gfxCriticalError() << "Attempting to post to an invalid Compositor Thread";
+ return;
+ }
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("EraseLayerStateRunnable", &EraseLayerState, aId));
+}
+
+static void UpdateControllerForLayersId(LayersId aLayersId,
+ GeckoContentController* aController) {
+ // Adopt ref given to us by SetControllerForLayerTree()
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ CompositorBridgeParent::sIndirectLayerTrees[aLayersId].mController =
+ already_AddRefed<GeckoContentController>(aController);
+}
+
+ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(
+ LayersId aLayersId, GeckoContentController* aController)
+ : mLayersId(aLayersId) {
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ CompositorBridgeParent::sIndirectLayerTrees[aLayersId].mController =
+ aController;
+}
+
+ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration() {
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ CompositorBridgeParent::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) {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aLayersId);
+ if (sIndirectLayerTrees.end() == cit) {
+ return nullptr;
+ }
+ LayerTreeState* lts = &cit->second;
+
+ RefPtr<IAPZCTreeManager> apzctm =
+ lts->mParent ? lts->mParent->mApzcTreeManager.get() : nullptr;
+ return apzctm.forget();
+}
+
+static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ // Tracks when a vsync occurs according to the HardwareComposer.
+ struct VsyncMarker {
+ static constexpr mozilla::Span<const char> MarkerTypeName() {
+ return mozilla::MakeStringSpan("VsyncTimestamp");
+ }
+ static void StreamJSONMarkerData(
+ baseprofiler::SpliceableJSONWriter& aWriter) {}
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
+ // Nothing outside the defaults.
+ return schema;
+ }
+ };
+ profiler_add_marker("VsyncTimestamp", geckoprofiler::category::GRAPHICS,
+ MarkerTiming::InstantAt(aVsyncTimestamp),
+ VsyncMarker{});
+ }
+}
+
+/*static */
+void CompositorBridgeParent::PostInsertVsyncProfilerMarker(
+ TimeStamp aVsyncTimestamp) {
+ // Called in the vsync thread
+ if (profiler_is_active() && CompositorThreadHolder::IsActive()) {
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("InsertVsyncProfilerMarkerRunnable",
+ InsertVsyncProfilerMarker, aVsyncTimestamp));
+ }
+}
+
+widget::PCompositorWidgetParent*
+CompositorBridgeParent::AllocPCompositorWidgetParent(
+ const CompositorWidgetInitData& aInitData) {
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+ if (mWidget) {
+ // Should not create two widgets on the same compositor.
+ return nullptr;
+ }
+
+ widget::CompositorWidgetParent* widget =
+ new widget::CompositorWidgetParent(aInitData, mOptions);
+ widget->AddRef();
+
+ // Sending the constructor acts as initialization as well.
+ mWidget = widget;
+ return widget;
+#else
+ return nullptr;
+#endif
+}
+
+bool CompositorBridgeParent::DeallocPCompositorWidgetParent(
+ PCompositorWidgetParent* aActor) {
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+ static_cast<widget::CompositorWidgetParent*>(aActor)->Release();
+ return true;
+#else
+ return false;
+#endif
+}
+
+CompositorController*
+CompositorBridgeParent::LayerTreeState::GetCompositorController() const {
+ return mParent;
+}
+
+void CompositorBridgeParent::NotifyDidSceneBuild(
+ RefPtr<const wr::WebRenderPipelineInfo> aInfo) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mPaused) {
+ return;
+ }
+
+ if (mWrBridge) {
+ mWrBridge->NotifyDidSceneBuild(aInfo);
+ }
+}
+
+void CompositorBridgeParent::NotifyDidRender(const VsyncId& aCompositeStartId,
+ TimeStamp& aCompositeStart,
+ TimeStamp& aRenderStart,
+ TimeStamp& aCompositeEnd,
+ wr::RendererStats* aStats) {
+ if (!mWrBridge) {
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(mWrBridge->IsRootWebRenderBridgeParent());
+
+ RefPtr<UiCompositorControllerParent> uiController =
+ UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeID);
+
+ if (uiController && mIsForcedFirstPaint) {
+ uiController->NotifyFirstPaint();
+ mIsForcedFirstPaint = false;
+ }
+
+ nsTArray<CompositionPayload> payload =
+ mWrBridge->TakePendingScrollPayload(aCompositeStartId);
+ if (!payload.IsEmpty()) {
+ RecordCompositionPayloadsPresented(aCompositeEnd, payload);
+ }
+
+ nsTArray<ImageCompositeNotificationInfo> notifications;
+ mWrBridge->ExtractImageCompositeNotifications(&notifications);
+ if (!notifications.IsEmpty()) {
+ Unused << ImageBridgeParent::NotifyImageComposites(notifications);
+ }
+}
+
+bool CompositorBridgeParent::sStable = false;
+uint32_t CompositorBridgeParent::sFramesComposited = 0;
+
+/* static */ void CompositorBridgeParent::ResetStable() {
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ if (CompositorThread()) {
+ CompositorThread()->Dispatch(
+ NewRunnableFunction("CompositorBridgeParent::ResetStable",
+ &CompositorBridgeParent::ResetStable));
+ }
+
+ // If there is no compositor thread, e.g. due to shutdown, then we can
+ // safefully just ignore this request.
+ return;
+ }
+
+ sStable = false;
+ sFramesComposited = 0;
+}
+
+void CompositorBridgeParent::MaybeDeclareStable() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ if (sStable) {
+ return;
+ }
+
+ // Once we render as many frames as the threshold, we declare this instance of
+ // the GPU process 'stable'. This causes the parent process to always respawn
+ // the GPU process if it crashes.
+ if (++sFramesComposited >=
+ StaticPrefs::layers_gpu_process_stable_frame_threshold()) {
+ sStable = true;
+
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "CompositorBridgeParent::MaybeDeclareStable", []() -> void {
+ if (XRE_IsParentProcess()) {
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+ if (gpm) {
+ gpm->OnProcessDeclaredStable();
+ }
+ } else {
+ gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton();
+ if (gpu && gpu->CanSend()) {
+ Unused << gpu->SendDeclareStable();
+ }
+ }
+ }));
+ }
+}
+
+void CompositorBridgeParent::NotifyPipelineRendered(
+ const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
+ const VsyncId& aCompositeStartId, TimeStamp& aCompositeStart,
+ TimeStamp& aRenderStart, TimeStamp& aCompositeEnd,
+ wr::RendererStats* aStats) {
+ if (!mWrBridge || !mAsyncImageManager) {
+ return;
+ }
+
+ bool isRoot = mWrBridge->PipelineId() == aPipelineId;
+ RefPtr<WebRenderBridgeParent> wrBridge =
+ isRoot ? mWrBridge
+ : RefPtr<WebRenderBridgeParent>(
+ mAsyncImageManager->GetWrBridge(aPipelineId));
+ if (!wrBridge) {
+ return;
+ }
+
+ CompositorBridgeParentBase* compBridge =
+ isRoot ? this : wrBridge->GetCompositorBridge();
+ if (!compBridge) {
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(isRoot == wrBridge->IsRootWebRenderBridgeParent());
+
+ wrBridge->RemoveEpochDataPriorTo(aEpoch);
+
+ nsTArray<FrameStats> stats;
+ nsTArray<TransactionId> transactions;
+
+ RefPtr<UiCompositorControllerParent> uiController =
+ UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeID);
+
+ wrBridge->FlushTransactionIdsForEpoch(
+ aEpoch, aCompositeStartId, aCompositeStart, aRenderStart, aCompositeEnd,
+ uiController, aStats, stats, transactions);
+ if (transactions.IsEmpty()) {
+ MOZ_ASSERT(stats.IsEmpty());
+ return;
+ }
+
+ MaybeDeclareStable();
+
+ LayersId layersId = isRoot ? LayersId{0} : wrBridge->GetLayersId();
+ Unused << compBridge->SendDidComposite(layersId, transactions,
+ aCompositeStart, aCompositeEnd);
+
+ if (!stats.IsEmpty()) {
+ Unused << SendNotifyFrameStats(stats);
+ }
+}
+
+RefPtr<AsyncImagePipelineManager>
+CompositorBridgeParent::GetAsyncImagePipelineManager() const {
+ return mAsyncImageManager;
+}
+
+/* static */ CompositorBridgeParent::LayerTreeState*
+CompositorBridgeParent::GetIndirectShadowTree(LayersId aId) {
+ // Only the compositor thread should use this method variant
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ StaticMonitorAutoLock 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) {
+ // 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.
+ StaticMonitorAutoLock 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 StaticMonitorAutoLock& aProofOfLock) {
+ CompositorBridgeParent::sIndirectLayerTreesLock.AssertCurrentThreadOwns();
+ CompositorBridgeParent::LayerTreeState* contentState = nullptr;
+ auto itr = CompositorBridgeParent::sIndirectLayerTrees.find(aContentLayersId);
+ if (CompositorBridgeParent::sIndirectLayerTrees.end() != itr) {
+ contentState = &itr->second;
+ }
+
+ // |contentState| is the state for the content process, but we want the
+ // APZCTMParent for the parent process owning that content process. So we have
+ // to jump to the LayerTreeState for the root layer tree id for that layer
+ // tree, and use the mApzcTreeManagerParent from that. This should also work
+ // with nested content processes, because RootLayerTreeId() will bypass any
+ // intermediate processes' ids and go straight to the root.
+ if (contentState && contentState->mParent) {
+ LayersId rootLayersId = contentState->mParent->RootLayerTreeId();
+ itr = CompositorBridgeParent::sIndirectLayerTrees.find(rootLayersId);
+ CompositorBridgeParent::LayerTreeState* rootState =
+ (CompositorBridgeParent::sIndirectLayerTrees.end() != itr)
+ ? &itr->second
+ : nullptr;
+ return rootState;
+ }
+
+ // Don't return contentState, that would be a lie!
+ return nullptr;
+}
+
+/* static */
+APZCTreeManagerParent* CompositorBridgeParent::GetApzcTreeManagerParentForRoot(
+ LayersId aContentLayersId) {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState* state =
+ GetStateForRoot(aContentLayersId, lock);
+ return state ? state->mApzcTreeManagerParent : nullptr;
+}
+
+/* static */
+GeckoContentController*
+CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ LayersId aContentLayersId) {
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState* state =
+ GetStateForRoot(aContentLayersId, lock);
+ return state ? state->mController.get() : nullptr;
+}
+
+PTextureParent* CompositorBridgeParent::AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ return TextureHost::CreateIPDLActor(
+ this, aSharedData, std::move(aReadLock), aLayersBackend, aFlags,
+ mCompositorManager->GetContentId(), aSerial, aExternalImageId);
+}
+
+bool CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) {
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+bool CompositorBridgeParent::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void CompositorBridgeParent::NotifyWebRenderDisableNativeCompositor() {
+ MOZ_ASSERT(CompositorThread()->IsOnCurrentThread());
+ if (mWrBridge) {
+ mWrBridge->DisableNativeCompositor();
+ }
+}
+
+int32_t RecordContentFrameTime(
+ const VsyncId& aTxnId, const TimeStamp& aVsyncStart,
+ const TimeStamp& aTxnStart, const VsyncId& aCompositeId,
+ const TimeStamp& aCompositeEnd, const TimeDuration& aFullPaintTime,
+ const TimeDuration& aVsyncRate, bool aContainsSVGGroup,
+ bool aRecordUploadStats, wr::RendererStats* aStats /* = nullptr */) {
+ double latencyMs = (aCompositeEnd - aTxnStart).ToMilliseconds();
+ double latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
+ int32_t fracLatencyNorm = lround(latencyNorm * 100.0);
+
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ struct ContentFrameMarker {
+ static constexpr Span<const char> MarkerTypeName() {
+ return MakeStringSpan("CONTENT_FRAME_TIME");
+ }
+ static void StreamJSONMarkerData(
+ baseprofiler::SpliceableJSONWriter& aWriter) {}
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
+ // Nothing outside the defaults.
+ return schema;
+ }
+ };
+
+ profiler_add_marker("CONTENT_FRAME_TIME", geckoprofiler::category::GRAPHICS,
+ MarkerTiming::Interval(aTxnStart, aCompositeEnd),
+ ContentFrameMarker{});
+ }
+
+ mozilla::glean::gfx_content_frame_time::from_paint.AccumulateSamples(
+ {static_cast<unsigned long long>(fracLatencyNorm)});
+
+ if (!(aTxnId == VsyncId()) && aVsyncStart) {
+ latencyMs = (aCompositeEnd - aVsyncStart).ToMilliseconds();
+ latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
+ fracLatencyNorm = lround(latencyNorm * 100.0);
+ int32_t result = fracLatencyNorm;
+ mozilla::glean::gfx_content_frame_time::from_vsync.AccumulateSamples(
+ {static_cast<unsigned long long>(fracLatencyNorm)});
+
+ if (aContainsSVGGroup) {
+ mozilla::glean::gfx_content_frame_time::with_svg.AccumulateSamples(
+ {static_cast<unsigned long long>(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);
+ mozilla::glean::gfx_content_frame_time::reason
+ .EnumGet(glean::gfx_content_frame_time::ReasonLabel::eOnTime)
+ .Add();
+ } else {
+ if (aCompositeId == VsyncId()) {
+ // aCompositeId is 0, possibly something got trigged from
+ // outside vsync?
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::NoVsyncNoId);
+ mozilla::glean::gfx_content_frame_time::reason
+ .EnumGet(glean::gfx_content_frame_time::ReasonLabel::eNoVsyncNoId)
+ .Add();
+ } else if (aTxnId >= aCompositeId) {
+ // Vsync ids are nonsensical, maybe we're trying to catch up?
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::NoVsync);
+ mozilla::glean::gfx_content_frame_time::reason
+ .EnumGet(glean::gfx_content_frame_time::ReasonLabel::eNoVsync)
+ .Add();
+ } 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);
+ mozilla::glean::gfx_content_frame_time::reason
+ .EnumGet(glean::gfx_content_frame_time::ReasonLabel::
+ eMissedCompositeLong)
+ .Add();
+ } else if (aFullPaintTime >= TimeDuration::FromMilliseconds(10)) {
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::MissedCompositeMid);
+ mozilla::glean::gfx_content_frame_time::reason
+ .EnumGet(glean::gfx_content_frame_time::ReasonLabel::
+ eMissedCompositeMid)
+ .Add();
+ } else if (aFullPaintTime >= TimeDuration::FromMilliseconds(5)) {
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::MissedCompositeLow);
+ mozilla::glean::gfx_content_frame_time::reason
+ .EnumGet(glean::gfx_content_frame_time::ReasonLabel::
+ eMissedCompositeLow)
+ .Add();
+ } else {
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::MissedComposite);
+ mozilla::glean::gfx_content_frame_time::reason
+ .EnumGet(
+ glean::gfx_content_frame_time::ReasonLabel::eMissedComposite)
+ .Add();
+ }
+ } else {
+ // Composite started on time, but must have taken too long.
+ Telemetry::AccumulateCategorical(
+ LABELS_CONTENT_FRAME_TIME_REASON::SlowComposite);
+ mozilla::glean::gfx_content_frame_time::reason
+ .EnumGet(glean::gfx_content_frame_time::ReasonLabel::eSlowComposite)
+ .Add();
+ }
+ }
+
+ if (aRecordUploadStats) {
+ if (aStats) {
+ latencyMs -= (double(aStats->resource_upload_time) / 1000000.0);
+ latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
+ fracLatencyNorm = lround(latencyNorm * 100.0);
+ }
+ mozilla::glean::gfx_content_frame_time::without_resource_upload
+ .AccumulateSamples(
+ {static_cast<unsigned long long>(fracLatencyNorm)});
+
+ if (aStats) {
+ latencyMs -= (double(aStats->gpu_cache_upload_time) / 1000000.0);
+ latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
+ fracLatencyNorm = lround(latencyNorm * 100.0);
+ }
+ mozilla::glean::gfx_content_frame_time::without_resource_upload
+ .AccumulateSamples(
+ {static_cast<unsigned long long>(fracLatencyNorm)});
+ }
+ return result;
+ }
+
+ return 0;
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvBeginRecording(
+ const TimeStamp& aRecordingStart, BeginRecordingResolver&& aResolve) {
+ if (mHaveCompositionRecorder) {
+ aResolve(false);
+ return IPC_OK();
+ }
+
+ if (mWrBridge) {
+ mWrBridge->BeginRecording(aRecordingStart);
+ }
+
+ mHaveCompositionRecorder = true;
+ aResolve(true);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorBridgeParent::RecvEndRecording(
+ EndRecordingResolver&& aResolve) {
+ if (!mHaveCompositionRecorder) {
+ aResolve(Nothing());
+ return IPC_OK();
+ }
+
+ if (mWrBridge) {
+ mWrBridge->EndRecording()->Then(
+ NS_GetCurrentThread(), __func__,
+ [resolve{aResolve}](FrameRecording&& recording) {
+ resolve(Some(std::move(recording)));
+ },
+ [resolve{aResolve}]() { resolve(Nothing()); });
+ } else {
+ aResolve(Nothing());
+ }
+
+ mHaveCompositionRecorder = false;
+
+ return IPC_OK();
+}
+
+void CompositorBridgeParent::FlushPendingWrTransactionEventsWithWait() {
+ if (!mWrBridge) {
+ return;
+ }
+
+ std::vector<RefPtr<WebRenderBridgeParent>> bridgeParents;
+ { // scope lock
+ StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
+ ForEachIndirectLayerTree([&](LayerTreeState* lts, LayersId) -> void {
+ if (lts->mWrBridge) {
+ bridgeParents.emplace_back(lts->mWrBridge);
+ }
+ });
+ }
+
+ for (auto& bridge : bridgeParents) {
+ bridge->FlushPendingWrTransactionEventsWithWait();
+ }
+}
+
+void RecordCompositionPayloadsPresented(
+ const TimeStamp& aCompositionEndTime,
+ const nsTArray<CompositionPayload>& aPayloads) {
+ if (aPayloads.Length()) {
+ TimeStamp presented = aCompositionEndTime;
+ for (const CompositionPayload& payload : aPayloads) {
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ MOZ_RELEASE_ASSERT(payload.mType <= kHighestCompositionPayloadType);
+ nsAutoCString name(
+ kCompositionPayloadTypeNames[uint8_t(payload.mType)]);
+ name.AppendLiteral(" Payload Presented");
+ // This doesn't really need to be a text marker. Once we have a version
+ // of profiler_add_marker that accepts both a start time and an end
+ // time, we could use that here.
+ nsPrintfCString text(
+ "Latency: %dms",
+ int32_t((presented - payload.mTimeStamp).ToMilliseconds()));
+ PROFILER_MARKER_TEXT(
+ name, GRAPHICS,
+ MarkerTiming::Interval(payload.mTimeStamp, presented), text);
+ }
+
+ if (payload.mType == CompositionPayloadType::eKeyPress) {
+ glean::performance_interaction::keypress_present_latency
+ .AccumulateRawDuration(presented - payload.mTimeStamp);
+ } else if (payload.mType == CompositionPayloadType::eAPZScroll) {
+ mozilla::glean::gfx::scroll_present_latency.AccumulateRawDuration(
+ presented - payload.mTimeStamp);
+ } else if (payload.mType ==
+ CompositionPayloadType::eMouseUpFollowedByClick) {
+ glean::performance_interaction::mouseup_click_present_latency
+ .AccumulateRawDuration(presented - payload.mTimeStamp);
+ }
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h
new file mode 100644
index 0000000000..0e6bd48f9a
--- /dev/null
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -0,0 +1,648 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorBridgeParent_h
+#define mozilla_layers_CompositorBridgeParent_h
+
+#include <stdint.h> // for uint64_t
+#include <unordered_map>
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Maybe.h"
+#include "mozilla/Monitor.h" // for Monitor
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/StaticMonitor.h" // for StaticMonitor
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/layers/CompositorController.h"
+#include "mozilla/layers/CompositorVsyncSchedulerOwner.h"
+#include "mozilla/layers/FocusTarget.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for IShmemAllocator
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/PCompositorBridgeParent.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+
+namespace gfx {
+class GPUProcessManager;
+class GPUParent;
+} // namespace gfx
+
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace widget {
+class CompositorWidget;
+}
+
+namespace wr {
+class WebRenderPipelineInfo;
+struct Epoch;
+struct MemoryReport;
+struct PipelineId;
+struct RendererStats;
+} // namespace wr
+
+namespace layers {
+
+class APZCTreeManager;
+class APZCTreeManagerParent;
+class APZSampler;
+class APZTestData;
+class APZUpdater;
+class AsyncImagePipelineManager;
+class CompositorAnimationStorage;
+class CompositorBridgeParent;
+class CompositorManagerParent;
+class CompositorVsyncScheduler;
+class FrameUniformityData;
+class GeckoContentController;
+class IAPZCTreeManager;
+class OMTASampler;
+class ContentCompositorBridgeParent;
+class CompositorThreadHolder;
+class InProcessCompositorSession;
+class UiCompositorControllerParent;
+class WebRenderBridgeParent;
+class WebRenderScrollDataWrapper;
+struct CollectedFrames;
+
+struct ScopedLayerTreeRegistration {
+ // For WebRender
+ ScopedLayerTreeRegistration(LayersId aLayersId,
+ GeckoContentController* aController);
+ ~ScopedLayerTreeRegistration();
+
+ private:
+ LayersId mLayersId;
+};
+
+class CompositorBridgeParentBase : public PCompositorBridgeParent,
+ public HostIPCAllocator,
+ public mozilla::ipc::IShmemAllocator {
+ friend class PCompositorBridgeParent;
+
+ public:
+ explicit CompositorBridgeParentBase(CompositorManagerParent* aManager);
+
+ virtual bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) {
+ return true;
+ }
+ virtual void LeaveTestMode(const LayersId& aId) {}
+ enum class TransformsToSkip : uint8_t { NoneOfThem = 0, APZ = 1 };
+ virtual void SetTestAsyncScrollOffset(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) = 0;
+ virtual void SetTestAsyncZoom(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) = 0;
+ virtual void FlushApzRepaints(const LayersId& aLayersId) = 0;
+ virtual void GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) {}
+ virtual void GetFrameUniformity(const LayersId& aLayersId,
+ FrameUniformityData* data) = 0;
+ virtual void SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) = 0;
+
+ IShmemAllocator* AsShmemAllocator() override { return this; }
+
+ CompositorBridgeParentBase* AsCompositorBridgeParentBase() override {
+ return this;
+ }
+
+ mozilla::ipc::IPCResult RecvSyncWithCompositor() { return IPC_OK(); }
+
+ mozilla::ipc::IPCResult Recv__delete__() override { return IPC_OK(); }
+
+ virtual void ObserveLayersUpdate(LayersId aLayersId, bool aActive) = 0;
+
+ // HostIPCAllocator
+ base::ProcessId GetChildProcessId() override;
+ dom::ContentParentId GetContentId() override;
+ void NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) override;
+ void SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) override;
+
+ // IShmemAllocator
+ bool AllocShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+ bool AllocUnsafeShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override {
+ return HostIPCAllocator::AddRef();
+ }
+ NS_IMETHOD_(MozExternalRefCountType) Release() override {
+ return HostIPCAllocator::Release();
+ }
+ virtual bool IsRemote() const { return false; }
+
+ virtual void NotifyMemoryPressure() {}
+ virtual void AccumulateMemoryReport(wr::MemoryReport*) {}
+
+ protected:
+ virtual ~CompositorBridgeParentBase();
+
+ virtual PAPZParent* AllocPAPZParent(const LayersId& layersId) = 0;
+ virtual bool DeallocPAPZParent(PAPZParent* aActor) = 0;
+
+ virtual PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(
+ const LayersId& layersId) = 0;
+ virtual bool DeallocPAPZCTreeManagerParent(
+ PAPZCTreeManagerParent* aActor) = 0;
+
+ virtual PTextureParent* AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aBackend, const TextureFlags& aTextureFlags,
+ const LayersId& id, const uint64_t& aSerial,
+ const MaybeExternalImageId& aExternalImageId) = 0;
+ virtual bool DeallocPTextureParent(PTextureParent* aActor) = 0;
+
+ virtual PWebRenderBridgeParent* AllocPWebRenderBridgeParent(
+ const PipelineId& pipelineId, const LayoutDeviceIntSize& aSize,
+ const WindowKind& aWindowKind) = 0;
+ virtual bool DeallocPWebRenderBridgeParent(
+ PWebRenderBridgeParent* aActor) = 0;
+
+ virtual PCompositorWidgetParent* AllocPCompositorWidgetParent(
+ const CompositorWidgetInitData& aInitData) = 0;
+ virtual bool DeallocPCompositorWidgetParent(
+ PCompositorWidgetParent* aActor) = 0;
+
+ virtual mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& id) = 0;
+ virtual mozilla::ipc::IPCResult RecvFlushRenderingAsync(
+ const wr::RenderReasons& aReasons) = 0;
+ virtual mozilla::ipc::IPCResult RecvForcePresent(
+ const wr::RenderReasons& aReasons) = 0;
+ virtual mozilla::ipc::IPCResult RecvBeginRecording(
+ const TimeStamp& aRecordingStart, BeginRecordingResolver&& aResolve) = 0;
+ virtual mozilla::ipc::IPCResult RecvEndRecording(
+ EndRecordingResolver&& aResolve) = 0;
+ virtual mozilla::ipc::IPCResult RecvInitialize(
+ const LayersId& rootLayerTreeId) = 0;
+ virtual mozilla::ipc::IPCResult RecvWillClose() = 0;
+ virtual mozilla::ipc::IPCResult RecvPause() = 0;
+ virtual mozilla::ipc::IPCResult RecvRequestFxrOutput() = 0;
+ virtual mozilla::ipc::IPCResult RecvResume() = 0;
+ virtual mozilla::ipc::IPCResult RecvResumeAsync() = 0;
+ virtual mozilla::ipc::IPCResult RecvNotifyChildCreated(
+ const LayersId& id, CompositorOptions* compositorOptions) = 0;
+ virtual mozilla::ipc::IPCResult RecvMapAndNotifyChildCreated(
+ const LayersId& id, const ProcessId& owner,
+ CompositorOptions* compositorOptions) = 0;
+ virtual mozilla::ipc::IPCResult RecvNotifyChildRecreated(
+ const LayersId& id, CompositorOptions* compositorOptions) = 0;
+ virtual mozilla::ipc::IPCResult RecvFlushRendering(
+ const wr::RenderReasons& aReasons) = 0;
+ virtual mozilla::ipc::IPCResult RecvNotifyMemoryPressure() = 0;
+ virtual mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() = 0;
+ virtual mozilla::ipc::IPCResult RecvStartFrameTimeRecording(
+ const int32_t& bufferSize, uint32_t* startIndex) = 0;
+ virtual mozilla::ipc::IPCResult RecvStopFrameTimeRecording(
+ const uint32_t& startIndex, nsTArray<float>* intervals) = 0;
+ virtual mozilla::ipc::IPCResult RecvCheckContentOnlyTDR(
+ const uint32_t& sequenceNum, bool* isContentOnlyTDR) = 0;
+
+ bool mCanSend;
+
+ protected:
+ RefPtr<CompositorManagerParent> mCompositorManager;
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(
+ CompositorBridgeParentBase::TransformsToSkip)
+
+class CompositorBridgeParent final : public CompositorBridgeParentBase,
+ public CompositorController {
+ friend class CompositorThreadHolder;
+ friend class InProcessCompositorSession;
+ friend class gfx::GPUProcessManager;
+ friend class gfx::GPUParent;
+ friend class PCompositorBridgeParent;
+
+ public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override {
+ return CompositorBridgeParentBase::AddRef();
+ }
+ NS_IMETHOD_(MozExternalRefCountType) Release() override {
+ return CompositorBridgeParentBase::Release();
+ }
+
+ explicit CompositorBridgeParent(CompositorManagerParent* aManager,
+ CSSToLayoutDeviceScale aScale,
+ const TimeDuration& aVsyncRate,
+ const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize,
+ uint64_t aInnerWindowId);
+
+ void InitSameProcess(widget::CompositorWidget* aWidget,
+ const LayersId& aLayerTreeId);
+
+ mozilla::ipc::IPCResult RecvInitialize(
+ const LayersId& aRootLayerTreeId) override;
+ mozilla::ipc::IPCResult RecvWillClose() override;
+ mozilla::ipc::IPCResult RecvPause() override;
+ mozilla::ipc::IPCResult RecvRequestFxrOutput() override;
+ mozilla::ipc::IPCResult RecvResume() override;
+ mozilla::ipc::IPCResult RecvResumeAsync() override;
+ mozilla::ipc::IPCResult RecvNotifyChildCreated(
+ const LayersId& child, CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvMapAndNotifyChildCreated(
+ const LayersId& child, const base::ProcessId& pid,
+ CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvNotifyChildRecreated(
+ const LayersId& child, CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& child) override;
+ mozilla::ipc::IPCResult RecvFlushRendering(
+ const wr::RenderReasons& aReasons) override;
+ mozilla::ipc::IPCResult RecvFlushRenderingAsync(
+ const wr::RenderReasons& aReasons) override;
+ mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override;
+ mozilla::ipc::IPCResult RecvForcePresent(
+ const wr::RenderReasons& aReasons) override;
+
+ mozilla::ipc::IPCResult RecvStartFrameTimeRecording(
+ const int32_t& aBufferSize, uint32_t* aOutStartIndex) override;
+ mozilla::ipc::IPCResult RecvStopFrameTimeRecording(
+ const uint32_t& aStartIndex, nsTArray<float>* intervals) override;
+
+ mozilla::ipc::IPCResult RecvCheckContentOnlyTDR(
+ const uint32_t& sequenceNum, bool* isContentOnlyTDR) override {
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult RecvNotifyMemoryPressure() override;
+ mozilla::ipc::IPCResult RecvBeginRecording(
+ const TimeStamp& aRecordingStart,
+ BeginRecordingResolver&& aResolve) override;
+ mozilla::ipc::IPCResult RecvEndRecording(
+ EndRecordingResolver&& aResolve) override;
+
+ void NotifyMemoryPressure() override;
+ void AccumulateMemoryReport(wr::MemoryReport*) override;
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override;
+ void LeaveTestMode(const LayersId& aId) override;
+ CompositorAnimationStorage* GetAnimationStorage();
+ using JankedAnimations =
+ std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn>;
+ void NotifyJankedAnimations(const JankedAnimations& aJankedAnimations);
+ void SetTestAsyncScrollOffset(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) override;
+ void SetTestAsyncZoom(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) override;
+ void FlushApzRepaints(const LayersId& aLayersId) override;
+ void GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) override;
+ void GetFrameUniformity(const LayersId& aLayersId,
+ FrameUniformityData* data) override;
+ void SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) override;
+ void SetFixedLayerMargins(ScreenIntCoord aTop, ScreenIntCoord aBottom);
+
+ PTextureParent* AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) override;
+ bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ 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, bool aActive) override {}
+
+ /**
+ * This forces the is-first-paint flag to true. This is intended to
+ * be called by the widget code when it loses its viewport information
+ * (or for whatever reason wants to refresh the viewport information).
+ * The information refresh happens because the compositor will call
+ * SetFirstPaintViewport on the next frame of composition.
+ */
+ void ForceIsFirstPaint();
+
+ void NotifyChildCreated(LayersId aChild);
+
+ void AsyncRender();
+
+ // Can be called from any thread
+ void ScheduleRenderOnCompositorThread(wr::RenderReasons aReasons) override;
+
+ void ScheduleComposition(wr::RenderReasons aReasons);
+
+ static void ScheduleForcedComposition(const LayersId& aLayersId,
+ wr::RenderReasons aReasons);
+
+ /**
+ * Returns the unique layer tree identifier that corresponds to the root
+ * tree of this compositor.
+ */
+ LayersId RootLayerTreeId();
+
+ /**
+ * Initialize statics.
+ */
+ static void InitializeStatics();
+
+ /**
+ * Notify the compositor for the given layer tree that vsync has occurred.
+ */
+ static void NotifyVsync(const VsyncEvent& aVsync, const LayersId& aLayersId);
+
+ /**
+ * Set aController as the pan/zoom callback for the subtree referred
+ * to by aLayersId.
+ *
+ * Must run on content main thread.
+ */
+ static void SetControllerForLayerTree(LayersId aLayersId,
+ GeckoContentController* aController);
+
+ struct LayerTreeState {
+ LayerTreeState();
+ ~LayerTreeState();
+ RefPtr<GeckoContentController> mController;
+ APZCTreeManagerParent* mApzcTreeManagerParent;
+ RefPtr<CompositorBridgeParent> mParent;
+ RefPtr<WebRenderBridgeParent> mWrBridge;
+ // Pointer to the ContentCompositorBridgeParent. Used by APZCs to share
+ // their FrameMetrics with the corresponding child process that holds
+ // the PCompositorBridgeChild
+ ContentCompositorBridgeParent* mContentCompositorBridgeParent;
+
+ CompositorController* GetCompositorController() const;
+ RefPtr<UiCompositorControllerParent> mUiControllerParent;
+ };
+
+ /**
+ * Lookup the indirect shadow tree for |aId| and return it if it
+ * exists. Otherwise null is returned. This must only be called on
+ * the compositor thread.
+ */
+ static LayerTreeState* GetIndirectShadowTree(LayersId aId);
+
+ /**
+ * Lookup the indirect shadow tree for |aId|, call the function object and
+ * return true if found. If not found, return false.
+ */
+ static bool CallWithIndirectShadowTree(
+ LayersId aId, const std::function<void(LayerTreeState&)>& aFunc);
+
+ /**
+ * Given the layers id for a content process, get the APZCTreeManagerParent
+ * for the corresponding *root* layers id. That is, the APZCTreeManagerParent,
+ * if one is found, will always be connected to the parent process rather
+ * than a content process. Note that unless the compositor process is
+ * separated this is expected to return null, because if the compositor is
+ * living in the gecko parent process then there is no APZCTreeManagerParent
+ * for the parent process.
+ */
+ static APZCTreeManagerParent* GetApzcTreeManagerParentForRoot(
+ LayersId aContentLayersId);
+ /**
+ * Same as the GetApzcTreeManagerParentForRoot function, but returns
+ * the GeckoContentController for the parent process.
+ */
+ static GeckoContentController* GetGeckoContentControllerForRoot(
+ LayersId aContentLayersId);
+
+ /**
+ * Used by the profiler to denote when a vsync occured
+ */
+ static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp);
+
+ widget::CompositorWidget* GetWidget() { return mWidget; }
+
+ PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(
+ const LayersId& aLayersId) override;
+ bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
+
+ // Helper method so that we don't have to expose mApzcTreeManager to
+ // ContentCompositorBridgeParent.
+ void AllocateAPZCTreeManagerParent(
+ const StaticMonitorAutoLock& aProofOfLayerTreeStateLock,
+ const LayersId& aLayersId, LayerTreeState& aLayerTreeStateToUpdate);
+
+ PAPZParent* AllocPAPZParent(const LayersId& aLayersId) override;
+ bool DeallocPAPZParent(PAPZParent* aActor) override;
+
+ RefPtr<APZSampler> GetAPZSampler() const;
+ RefPtr<APZUpdater> GetAPZUpdater() const;
+ RefPtr<OMTASampler> GetOMTASampler() const;
+
+ uint64_t GetInnerWindowId() const { return mInnerWindowId; }
+
+ CompositorOptions GetOptions() const { return mOptions; }
+
+ TimeDuration GetVsyncInterval() const {
+ // the variable is called "rate" but really it's an interval
+ return mVsyncRate;
+ }
+
+ PWebRenderBridgeParent* AllocPWebRenderBridgeParent(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize,
+ const WindowKind& aWindowKind) override;
+ bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
+ RefPtr<WebRenderBridgeParent> GetWebRenderBridgeParent() const;
+ Maybe<TimeStamp> GetTestingTimeStamp() const;
+
+ static CompositorBridgeParent* GetCompositorBridgeParentFromLayersId(
+ const LayersId& aLayersId);
+ static RefPtr<CompositorBridgeParent> GetCompositorBridgeParentFromWindowId(
+ const wr::WindowId& aWindowId);
+
+ /**
+ * This returns a reference to the IAPZCTreeManager "controller subinterface"
+ * to which pan/zoom-related events can be sent. The controller subinterface
+ * doesn't expose any sampler-thread APZCTreeManager methods.
+ */
+ static already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager(
+ LayersId aLayersId);
+
+ WebRenderBridgeParent* GetWrBridge() { return mWrBridge; }
+
+ void FlushPendingWrTransactionEventsWithWait();
+
+ private:
+ void Initialize();
+
+ /**
+ * Called during destruction in order to release resources as early as
+ * possible.
+ */
+ void StopAndClearResources();
+
+ /**
+ * Release compositor-thread resources referred to by |aID|.
+ *
+ * Must run on the content main thread.
+ */
+ static void DeallocateLayerTreeId(LayersId aId);
+
+ /**
+ * Notify the compositor the quality settings have been updated.
+ */
+ static void UpdateQualitySettings();
+
+ /**
+ * Notify the compositor the debug flags have been updated.
+ */
+ static void UpdateDebugFlags();
+
+ /**
+ * Notify the compositor some webrender parameters have been updated.
+ */
+ static void UpdateWebRenderParameters();
+
+ /**
+ * Notify the compositor some webrender parameters have been updated.
+ */
+ static void UpdateWebRenderBoolParameters();
+
+ /**
+ * Notify the compositor webrender profiler UI string has been updated.
+ */
+ static void UpdateWebRenderProfilerUI();
+
+ static void ResetStable();
+
+ void MaybeDeclareStable();
+
+ protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~CompositorBridgeParent();
+
+ void DeferredDestroy();
+
+ void SetEGLSurfaceRect(int x, int y, int width, int height);
+
+ public:
+ void PauseComposition();
+ bool ResumeComposition();
+ bool ResumeCompositionAndResize(int x, int y, int width, int height);
+ bool IsPaused();
+
+ typedef std::map<LayersId, CompositorBridgeParent::LayerTreeState>
+ LayerTreeMap;
+
+ static StaticMonitor sIndirectLayerTreesLock;
+ static LayerTreeMap sIndirectLayerTrees
+ MOZ_GUARDED_BY(sIndirectLayerTreesLock);
+
+ protected:
+ /**
+ * Add a compositor to the global compositor map.
+ */
+ static void AddCompositor(CompositorBridgeParent* compositor, uint64_t* id);
+ /**
+ * Remove a compositor from the global compositor map.
+ */
+ static CompositorBridgeParent* RemoveCompositor(uint64_t id);
+
+ /**
+ * Creates the global compositor map.
+ */
+ static void Setup();
+
+ /**
+ * Remaning cleanups after the compositore thread is gone.
+ */
+ static void FinishShutdown();
+
+ // The indirect layer tree lock must be held before calling this function.
+ // Callback should take (LayerTreeState* aState, const LayersId& aLayersId)
+ template <typename Lambda>
+ inline void ForEachIndirectLayerTree(const Lambda& aCallback);
+
+ // The indirect layer tree lock must be held before calling this function.
+ // Callback should take (LayerTreeState* aState, const LayersId& aLayersId)
+ template <typename Lambda>
+ static inline void ForEachWebRenderBridgeParent(const Lambda& aCallback);
+
+ static bool sStable;
+ static uint32_t sFramesComposited;
+
+ RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
+ RefPtr<WebRenderBridgeParent> mWrBridge;
+ widget::CompositorWidget* mWidget;
+ Maybe<TimeStamp> mTestTime;
+ CSSToLayoutDeviceScale mScale;
+ TimeDuration mVsyncRate;
+
+ bool mPaused;
+ bool mHaveCompositionRecorder;
+ bool mIsForcedFirstPaint;
+
+ bool mUseExternalSurfaceSize;
+ gfx::IntSize mEGLSurfaceSize;
+
+ CompositorOptions mOptions;
+
+ uint64_t mCompositorBridgeID;
+ LayersId mRootLayerTreeID;
+
+ RefPtr<APZCTreeManager> mApzcTreeManager;
+ RefPtr<APZSampler> mApzSampler;
+ RefPtr<APZUpdater> mApzUpdater;
+ RefPtr<OMTASampler> mOMTASampler;
+
+ // Store the inner window id of the browser window, to use it in
+ // profiler markers.
+ uint64_t mInnerWindowId;
+
+ RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
+ // This makes sure the compositorParent is not destroyed before receiving
+ // confirmation that the channel is closed.
+ // mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy.
+ RefPtr<CompositorBridgeParent> mSelfRef;
+ RefPtr<CompositorAnimationStorage> mAnimationStorage;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CompositorBridgeParent);
+};
+
+int32_t RecordContentFrameTime(
+ const VsyncId& aTxnId, const TimeStamp& aVsyncStart,
+ const TimeStamp& aTxnStart, const VsyncId& aCompositeId,
+ const TimeStamp& aCompositeEnd, const TimeDuration& aFullPaintTime,
+ const TimeDuration& aVsyncRate, bool aContainsSVGGroup,
+ bool aRecordUploadStats, wr::RendererStats* aStats = nullptr);
+
+void RecordCompositionPayloadsPresented(
+ const TimeStamp& aCompositionEndTime,
+ const nsTArray<CompositionPayload>& aPayloads);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorBridgeParent_h
diff --git a/gfx/layers/ipc/CompositorManagerChild.cpp b/gfx/layers/ipc/CompositorManagerChild.cpp
new file mode 100644
index 0000000000..0e553745d3
--- /dev/null
+++ b/gfx/layers/ipc/CompositorManagerChild.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 "mozilla/layers/CompositorManagerChild.h"
+
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/dom/ContentChild.h" // for ContentChild
+#include "mozilla/dom/BrowserChild.h" // for BrowserChild
+#include "mozilla/ipc/Endpoint.h"
+#include "VsyncSource.h"
+
+namespace mozilla {
+namespace layers {
+
+using gfx::GPUProcessManager;
+
+StaticRefPtr<CompositorManagerChild> CompositorManagerChild::sInstance;
+Atomic<base::ProcessId> CompositorManagerChild::sOtherPid(0);
+
+/* static */
+bool CompositorManagerChild::IsInitialized(uint64_t aProcessToken) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return sInstance && sInstance->CanSend() &&
+ sInstance->mProcessToken == aProcessToken;
+}
+
+/* static */
+void CompositorManagerChild::InitSameProcess(uint32_t aNamespace,
+ uint64_t aProcessToken) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_WARN_IF(IsInitialized(aProcessToken))) {
+ MOZ_ASSERT_UNREACHABLE("Already initialized same process");
+ return;
+ }
+
+ RefPtr<CompositorManagerParent> parent =
+ CompositorManagerParent::CreateSameProcess(aNamespace);
+ RefPtr<CompositorManagerChild> child =
+ new CompositorManagerChild(parent, aProcessToken, aNamespace);
+ if (NS_WARN_IF(!child->CanSend())) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Failed to open same process protocol");
+ return;
+ }
+
+ parent->BindComplete(/* aIsRoot */ true);
+ sInstance = std::move(child);
+ sOtherPid = sInstance->OtherPid();
+}
+
+/* static */
+bool CompositorManagerChild::Init(Endpoint<PCompositorManagerChild>&& aEndpoint,
+ uint32_t aNamespace,
+ uint64_t aProcessToken /* = 0 */) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (sInstance) {
+ MOZ_ASSERT(sInstance->mNamespace != aNamespace);
+ }
+
+ sInstance = new CompositorManagerChild(std::move(aEndpoint), aProcessToken,
+ aNamespace);
+ sOtherPid = sInstance->OtherPid();
+ return sInstance->CanSend();
+}
+
+/* static */
+void CompositorManagerChild::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ CompositorBridgeChild::ShutDown();
+
+ if (!sInstance) {
+ return;
+ }
+
+ sInstance->Close();
+ sInstance = nullptr;
+ sOtherPid = 0;
+}
+
+/* static */
+void CompositorManagerChild::OnGPUProcessLost(uint64_t aProcessToken) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Since GPUChild and CompositorManagerChild will race on ActorDestroy, we
+ // cannot know if the CompositorManagerChild is about to be released but has
+ // yet to be. As such, we want to pre-emptively set mCanSend to false.
+ if (sInstance && sInstance->mProcessToken == aProcessToken) {
+ sInstance->mCanSend = false;
+ sOtherPid = 0;
+ }
+}
+
+/* static */
+bool CompositorManagerChild::CreateContentCompositorBridge(
+ uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
+ return false;
+ }
+
+ CompositorBridgeOptions options = ContentCompositorOptions();
+
+ RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance);
+ if (NS_WARN_IF(
+ !sInstance->SendPCompositorBridgeConstructor(bridge, options))) {
+ return false;
+ }
+
+ bridge->InitForContent(aNamespace);
+ return true;
+}
+
+/* static */
+already_AddRefed<CompositorBridgeChild>
+CompositorManagerChild::CreateWidgetCompositorBridge(
+ uint64_t aProcessToken, WebRenderLayerManager* aLayerManager,
+ uint32_t aNamespace, CSSToLayoutDeviceScale aScale,
+ const CompositorOptions& aOptions, bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
+ return nullptr;
+ }
+
+ TimeDuration vsyncRate =
+ gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher()->GetVsyncRate();
+
+ CompositorBridgeOptions options = WidgetCompositorOptions(
+ aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, aSurfaceSize,
+ aInnerWindowId);
+
+ RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance);
+ if (NS_WARN_IF(
+ !sInstance->SendPCompositorBridgeConstructor(bridge, options))) {
+ return nullptr;
+ }
+
+ bridge->InitForWidget(aProcessToken, aLayerManager, aNamespace);
+ return bridge.forget();
+}
+
+/* static */
+already_AddRefed<CompositorBridgeChild>
+CompositorManagerChild::CreateSameProcessWidgetCompositorBridge(
+ WebRenderLayerManager* aLayerManager, uint32_t aNamespace) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ if (NS_WARN_IF(!sInstance || !sInstance->CanSend())) {
+ return nullptr;
+ }
+
+ CompositorBridgeOptions options = SameProcessWidgetCompositorOptions();
+
+ RefPtr<CompositorBridgeChild> bridge = new CompositorBridgeChild(sInstance);
+ if (NS_WARN_IF(
+ !sInstance->SendPCompositorBridgeConstructor(bridge, options))) {
+ return nullptr;
+ }
+
+ bridge->InitForWidget(1, aLayerManager, aNamespace);
+ return bridge.forget();
+}
+
+CompositorManagerChild::CompositorManagerChild(CompositorManagerParent* aParent,
+ uint64_t aProcessToken,
+ uint32_t aNamespace)
+ : mProcessToken(aProcessToken),
+ mNamespace(aNamespace),
+ mResourceId(0),
+ mCanSend(false),
+ mSameProcess(true),
+ mFwdTransactionCounter(this) {
+ MOZ_ASSERT(aParent);
+
+ SetOtherProcessId(base::GetCurrentProcId());
+ if (NS_WARN_IF(!Open(aParent, CompositorThread(), ipc::ChildSide))) {
+ return;
+ }
+
+ mCanSend = true;
+ SetReplyTimeout();
+}
+
+CompositorManagerChild::CompositorManagerChild(
+ Endpoint<PCompositorManagerChild>&& aEndpoint, uint64_t aProcessToken,
+ uint32_t aNamespace)
+ : mProcessToken(aProcessToken),
+ mNamespace(aNamespace),
+ mResourceId(0),
+ mCanSend(false),
+ mSameProcess(false),
+ mFwdTransactionCounter(this) {
+ if (NS_WARN_IF(!aEndpoint.Bind(this))) {
+ return;
+ }
+
+ mCanSend = true;
+ SetReplyTimeout();
+}
+
+void CompositorManagerChild::ActorDestroy(ActorDestroyReason aReason) {
+ mCanSend = false;
+ if (sInstance == this) {
+ sInstance = nullptr;
+ }
+}
+
+void CompositorManagerChild::HandleFatalError(const char* aMsg) {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+void CompositorManagerChild::ProcessingError(Result aCode,
+ const char* aReason) {
+ if (aCode != MsgDropped) {
+ gfxDevCrash(gfx::LogReason::ProcessingError)
+ << "Processing error in CompositorBridgeChild: " << int(aCode);
+ }
+}
+
+void CompositorManagerChild::SetReplyTimeout() {
+#ifndef DEBUG
+ // Add a timeout for release builds to kill GPU process when it hangs.
+ if (XRE_IsParentProcess() && GPUProcessManager::Get()->GetGPUChild()) {
+ int32_t timeout =
+ StaticPrefs::layers_gpu_process_ipc_reply_timeout_ms_AtStartup();
+ SetReplyTimeoutMs(timeout);
+ }
+#endif
+}
+
+bool CompositorManagerChild::ShouldContinueFromReplyTimeout() {
+ if (XRE_IsParentProcess()) {
+ gfxCriticalNote << "Killing GPU process due to IPC reply timeout";
+ MOZ_DIAGNOSTIC_ASSERT(GPUProcessManager::Get()->GetGPUChild());
+ GPUProcessManager::Get()->KillProcess();
+ }
+ return false;
+}
+
+mozilla::ipc::IPCResult CompositorManagerChild::RecvNotifyWebRenderError(
+ const WebRenderError&& aError) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ GPUProcessManager::Get()->NotifyWebRenderError(aError);
+ return IPC_OK();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorManagerChild.h b/gfx/layers/ipc/CompositorManagerChild.h
new file mode 100644
index 0000000000..072b0d028e
--- /dev/null
+++ b/gfx/layers/ipc/CompositorManagerChild.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_GFX_COMPOSITORMANAGERCHILD_H
+#define MOZILLA_GFX_COMPOSITORMANAGERCHILD_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/layers/CompositableForwarder.h" // for FwdTransactionCounter
+#include "mozilla/layers/PCompositorManagerChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositorBridgeChild;
+class CompositorManagerParent;
+class WebRenderLayerManager;
+
+class CompositorManagerChild : public PCompositorManagerChild {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerChild, override)
+
+ public:
+ static bool IsInitialized(uint64_t aProcessToken);
+ static void InitSameProcess(uint32_t aNamespace, uint64_t aProcessToken);
+ static bool Init(Endpoint<PCompositorManagerChild>&& aEndpoint,
+ uint32_t aNamespace, uint64_t aProcessToken = 0);
+ static void Shutdown();
+ static void OnGPUProcessLost(uint64_t aProcessToken);
+
+ static bool CreateContentCompositorBridge(uint32_t aNamespace);
+
+ static already_AddRefed<CompositorBridgeChild> CreateWidgetCompositorBridge(
+ uint64_t aProcessToken, WebRenderLayerManager* aLayerManager,
+ uint32_t aNamespace, CSSToLayoutDeviceScale aScale,
+ const CompositorOptions& aOptions, bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId);
+
+ static already_AddRefed<CompositorBridgeChild>
+ CreateSameProcessWidgetCompositorBridge(WebRenderLayerManager* aLayerManager,
+ uint32_t aNamespace);
+
+ static CompositorManagerChild* GetInstance() {
+ MOZ_ASSERT(NS_IsMainThread());
+ return sInstance;
+ }
+
+ // Threadsafe way to get the compositor process ID.
+ static base::ProcessId GetOtherPid() { return sOtherPid; }
+
+ bool CanSend() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mCanSend;
+ }
+
+ bool SameProcess() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mSameProcess;
+ }
+
+ uint32_t GetNextResourceId() {
+ MOZ_ASSERT(NS_IsMainThread());
+ return ++mResourceId;
+ }
+
+ uint32_t GetNamespace() const { return mNamespace; }
+
+ bool OwnsExternalImageId(const wr::ExternalImageId& aId) const {
+ return mNamespace == static_cast<uint32_t>(wr::AsUint64(aId) >> 32);
+ }
+
+ wr::ExternalImageId GetNextExternalImageId() {
+ uint64_t id = GetNextResourceId();
+ MOZ_RELEASE_ASSERT(id != 0);
+ id |= (static_cast<uint64_t>(mNamespace) << 32);
+ return wr::ToExternalImageId(id);
+ }
+
+ void ActorDestroy(ActorDestroyReason aReason) override;
+
+ void HandleFatalError(const char* aMsg) override;
+
+ void ProcessingError(Result aCode, const char* aReason) override;
+
+ bool ShouldContinueFromReplyTimeout() override;
+
+ mozilla::ipc::IPCResult RecvNotifyWebRenderError(
+ const WebRenderError&& aError);
+
+ FwdTransactionCounter& GetFwdTransactionCounter() {
+ return mFwdTransactionCounter;
+ }
+
+ private:
+ static StaticRefPtr<CompositorManagerChild> sInstance;
+ static Atomic<base::ProcessId> sOtherPid;
+
+ CompositorManagerChild(CompositorManagerParent* aParent,
+ uint64_t aProcessToken, uint32_t aNamespace);
+
+ CompositorManagerChild(Endpoint<PCompositorManagerChild>&& aEndpoint,
+ uint64_t aProcessToken, uint32_t aNamespace);
+
+ virtual ~CompositorManagerChild() = default;
+
+ void SetReplyTimeout();
+
+ uint64_t mProcessToken;
+ uint32_t mNamespace;
+ uint32_t mResourceId;
+ bool mCanSend;
+ bool mSameProcess;
+ FwdTransactionCounter mFwdTransactionCounter;
+};
+
+} // 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..29d9699033
--- /dev/null
+++ b/gfx/layers/ipc/CompositorManagerParent.cpp
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/CanvasManagerParent.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/ContentCompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/RemoteTextureMap.h"
+#include "mozilla/layers/SharedSurfacesParent.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "gfxPlatform.h"
+#include "VsyncSource.h"
+
+namespace mozilla {
+namespace layers {
+
+StaticMonitor CompositorManagerParent::sMonitor;
+StaticRefPtr<CompositorManagerParent> CompositorManagerParent::sInstance;
+CompositorManagerParent::ManagerMap CompositorManagerParent::sManagers;
+
+/* static */
+already_AddRefed<CompositorManagerParent>
+CompositorManagerParent::CreateSameProcess(uint32_t aNamespace) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ StaticMonitorAutoLock lock(sMonitor);
+
+ // We are creating a manager for the UI process, inside the combined GPU/UI
+ // process. It is created more-or-less the same but we retain a reference to
+ // the parent to access state.
+ if (NS_WARN_IF(sInstance)) {
+ MOZ_ASSERT_UNREACHABLE("Already initialized");
+ return nullptr;
+ }
+
+ // The child is responsible for setting up the IPC channel in the same
+ // process case because if we open from the child perspective, we can do it
+ // on the main thread and complete before we return the manager handles.
+ RefPtr<CompositorManagerParent> parent =
+ new CompositorManagerParent(dom::ContentParentId(), aNamespace);
+ parent->SetOtherProcessId(base::GetCurrentProcId());
+ return parent.forget();
+}
+
+/* static */
+bool CompositorManagerParent::Create(
+ Endpoint<PCompositorManagerParent>&& aEndpoint,
+ dom::ContentParentId aChildId, uint32_t aNamespace, bool aIsRoot) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We are creating a manager for the another process, inside the GPU process
+ // (or UI process if it subsumbed the GPU process).
+ MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId());
+
+ if (!CompositorThreadHolder::IsActive()) {
+ return false;
+ }
+
+ RefPtr<CompositorManagerParent> bridge =
+ new CompositorManagerParent(aChildId, aNamespace);
+
+ RefPtr<Runnable> runnable =
+ NewRunnableMethod<Endpoint<PCompositorManagerParent>&&, bool>(
+ "CompositorManagerParent::Bind", bridge,
+ &CompositorManagerParent::Bind, std::move(aEndpoint), aIsRoot);
+ CompositorThread()->Dispatch(runnable.forget());
+ return true;
+}
+
+/* static */
+already_AddRefed<CompositorBridgeParent>
+CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(
+ CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize,
+ uint64_t aInnerWindowId) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // When we are in a combined UI / GPU process, InProcessCompositorSession
+ // requires both the parent and child PCompositorBridge actors for its own
+ // construction, which is done on the main thread. Normally
+ // CompositorBridgeParent is created on the compositor thread via the IPDL
+ // plumbing (CompositorManagerParent::AllocPCompositorBridgeParent). Thus to
+ // actually get a reference to the parent, we would need to block on the
+ // compositor thread until it handles our constructor message. Because only
+ // one one IPDL constructor is permitted per parent and child protocol, we
+ // cannot make the normal case async and this case sync. Instead what we do
+ // is leave the constructor async (a boon to the content process setup) and
+ // create the parent ahead of time. It will pull the preinitialized parent
+ // from the queue when it receives the message and give that to IPDL.
+
+ // Note that the static mutex not only is used to protect sInstance, but also
+ // mPendingCompositorBridges.
+ StaticMonitorAutoLock lock(sMonitor);
+ if (NS_WARN_IF(!sInstance)) {
+ return nullptr;
+ }
+
+ TimeDuration vsyncRate =
+ gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher()->GetVsyncRate();
+
+ RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent(
+ sInstance, aScale, vsyncRate, aOptions, aUseExternalSurfaceSize,
+ aSurfaceSize, aInnerWindowId);
+
+ sInstance->mPendingCompositorBridges.AppendElement(bridge);
+ return bridge.forget();
+}
+
+CompositorManagerParent::CompositorManagerParent(
+ dom::ContentParentId aContentId, uint32_t aNamespace)
+ : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()),
+ mSharedSurfacesHolder(MakeRefPtr<SharedSurfacesHolder>(aNamespace)),
+ mContentId(aContentId),
+ mNamespace(aNamespace) {}
+
+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());
+
+ StaticMonitorAutoLock lock(sMonitor);
+ if (aIsRoot) {
+ MOZ_ASSERT(!sInstance);
+ sInstance = this;
+ }
+
+ MOZ_RELEASE_ASSERT(sManagers.try_emplace(mNamespace, this).second);
+}
+
+void CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason) {
+ GetCurrentSerialEventTarget()->Dispatch(
+ NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy",
+ this, &CompositorManagerParent::DeferredDestroy));
+
+ if (mRemoteTextureTxnScheduler) {
+ mRemoteTextureTxnScheduler = nullptr;
+ }
+
+ StaticMonitorAutoLock lock(sMonitor);
+ if (sInstance == this) {
+ sInstance = nullptr;
+ }
+
+ MOZ_RELEASE_ASSERT(sManagers.erase(mNamespace) > 0);
+ sMonitor.NotifyAll();
+}
+
+void CompositorManagerParent::DeferredDestroy() {
+ mCompositorThreadHolder = nullptr;
+}
+
+/* static */
+void CompositorManagerParent::ShutdownInternal() {
+ nsTArray<RefPtr<CompositorManagerParent>> actors;
+
+ // We move here because we may attempt to acquire the same lock during the
+ // destroy to remove the reference in sManagers.
+ {
+ StaticMonitorAutoLock lock(sMonitor);
+ actors.SetCapacity(sManagers.size());
+ for (auto& i : sManagers) {
+ actors.AppendElement(i.second);
+ }
+ }
+
+ for (auto& actor : actors) {
+ actor->Close();
+ }
+}
+
+/* static */
+void CompositorManagerParent::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "layers::CompositorManagerParent::Shutdown",
+ []() -> void { CompositorManagerParent::ShutdownInternal(); }));
+}
+
+/* static */ void CompositorManagerParent::WaitForSharedSurface(
+ const wr::ExternalImageId& aId) {
+ uint32_t extNamespace = static_cast<uint32_t>(wr::AsUint64(aId) >> 32);
+ uint32_t resourceId = static_cast<uint32_t>(wr::AsUint64(aId));
+
+ StaticMonitorAutoLock lock(sMonitor);
+
+ while (true) {
+ const auto i = sManagers.find(extNamespace);
+ if (NS_WARN_IF(i == sManagers.end())) {
+ break;
+ }
+
+ // We know that when the resource ID is allocated, we either fail to have
+ // shared the surface with the compositor process, and so we don't use the
+ // external image ID, or we have queued an IPDL message over the
+ // corresponding CompositorManagerParent object to map that surface into
+ // memory. They are dispatched in order, so we can safely wait until either
+ // the actor is closed, or the last seen resource ID reaches the target.
+ if (i->second->mLastSharedSurfaceResourceId >= resourceId) {
+ break;
+ }
+
+ lock.Wait();
+ }
+}
+
+already_AddRefed<PCompositorBridgeParent>
+CompositorManagerParent::AllocPCompositorBridgeParent(
+ const CompositorBridgeOptions& aOpt) {
+ switch (aOpt.type()) {
+ case CompositorBridgeOptions::TContentCompositorOptions: {
+ RefPtr<ContentCompositorBridgeParent> bridge =
+ new ContentCompositorBridgeParent(this);
+ return bridge.forget();
+ }
+ case CompositorBridgeOptions::TWidgetCompositorOptions: {
+ // Only the UI process is allowed to create widget compositors in the
+ // compositor process.
+ gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton();
+ if (NS_WARN_IF(!gpu || OtherPid() != gpu->OtherPid())) {
+ MOZ_ASSERT_UNREACHABLE("Child cannot create widget compositor!");
+ break;
+ }
+
+ const WidgetCompositorOptions& opt = aOpt.get_WidgetCompositorOptions();
+ RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent(
+ this, opt.scale(), opt.vsyncRate(), opt.options(),
+ opt.useExternalSurfaceSize(), opt.surfaceSize(), opt.innerWindowId());
+ return bridge.forget();
+ }
+ case CompositorBridgeOptions::TSameProcessWidgetCompositorOptions: {
+ // If the GPU and UI process are combined, we actually already created the
+ // CompositorBridgeParent, so we need to reuse that to inject it into the
+ // IPDL framework.
+ if (NS_WARN_IF(OtherPid() != base::GetCurrentProcId())) {
+ MOZ_ASSERT_UNREACHABLE("Child cannot create same process compositor!");
+ break;
+ }
+
+ // Note that the static mutex not only is used to protect sInstance, but
+ // also mPendingCompositorBridges.
+ StaticMonitorAutoLock lock(sMonitor);
+ if (mPendingCompositorBridges.IsEmpty()) {
+ break;
+ }
+
+ RefPtr<CompositorBridgeParent> bridge = mPendingCompositorBridges[0];
+ mPendingCompositorBridges.RemoveElementAt(0);
+ return bridge.forget();
+ }
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
+/* static */ void CompositorManagerParent::AddSharedSurface(
+ const wr::ExternalImageId& aId, gfx::SourceSurfaceSharedData* aSurface) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ StaticMonitorAutoLock lock(sMonitor);
+ if (NS_WARN_IF(!sInstance)) {
+ return;
+ }
+
+ if (NS_WARN_IF(!sInstance->OwnsExternalImageId(aId))) {
+ MOZ_ASSERT_UNREACHABLE("Wrong namespace?");
+ return;
+ }
+
+ SharedSurfacesParent::AddSameProcess(aId, aSurface);
+
+ uint32_t resourceId = static_cast<uint32_t>(wr::AsUint64(aId));
+ MOZ_RELEASE_ASSERT(sInstance->mLastSharedSurfaceResourceId < resourceId);
+ sInstance->mLastSharedSurfaceResourceId = resourceId;
+ sMonitor.NotifyAll();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvAddSharedSurface(
+ const wr::ExternalImageId& aId, SurfaceDescriptorShared&& aDesc) {
+ if (NS_WARN_IF(!OwnsExternalImageId(aId))) {
+ MOZ_ASSERT_UNREACHABLE("Wrong namespace?");
+ return IPC_OK();
+ }
+
+ SharedSurfacesParent::Add(aId, std::move(aDesc), OtherPid());
+
+ StaticMonitorAutoLock lock(sMonitor);
+ uint32_t resourceId = static_cast<uint32_t>(wr::AsUint64(aId));
+ MOZ_RELEASE_ASSERT(mLastSharedSurfaceResourceId < resourceId);
+ mLastSharedSurfaceResourceId = resourceId;
+ sMonitor.NotifyAll();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvRemoveSharedSurface(
+ const wr::ExternalImageId& aId) {
+ if (NS_WARN_IF(!OwnsExternalImageId(aId))) {
+ MOZ_ASSERT_UNREACHABLE("Wrong namespace?");
+ return IPC_OK();
+ }
+
+ SharedSurfacesParent::Remove(aId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvReportSharedSurfacesMemory(
+ ReportSharedSurfacesMemoryResolver&& aResolver) {
+ SharedSurfacesMemoryReport report;
+ SharedSurfacesParent::AccumulateMemoryReport(mNamespace, report);
+ aResolver(std::move(report));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvNotifyMemoryPressure() {
+ nsTArray<PCompositorBridgeParent*> compositorBridges;
+ ManagedPCompositorBridgeParent(compositorBridges);
+ for (auto bridge : compositorBridges) {
+ static_cast<CompositorBridgeParentBase*>(bridge)->NotifyMemoryPressure();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvReportMemory(
+ ReportMemoryResolver&& aResolver) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MemoryReport aggregate;
+ PodZero(&aggregate);
+
+ // Accumulate RenderBackend usage.
+ nsTArray<PCompositorBridgeParent*> compositorBridges;
+ ManagedPCompositorBridgeParent(compositorBridges);
+ for (auto bridge : compositorBridges) {
+ static_cast<CompositorBridgeParentBase*>(bridge)->AccumulateMemoryReport(
+ &aggregate);
+ }
+
+ // Accumulate Renderer usage asynchronously, and resolve.
+ //
+ // Note that the IPDL machinery requires aResolver to be called on this
+ // thread, so we can't just pass it over to the renderer thread. We use
+ // an intermediate MozPromise instead.
+ wr::RenderThread::AccumulateMemoryReport(aggregate)->Then(
+ CompositorThread(), __func__,
+ [resolver = std::move(aResolver)](MemoryReport aReport) {
+ resolver(aReport);
+ },
+ [](bool) {
+ MOZ_ASSERT_UNREACHABLE("MemoryReport promises are never rejected");
+ });
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult CompositorManagerParent::RecvInitCanvasManager(
+ Endpoint<PCanvasManagerParent>&& aEndpoint) {
+ gfx::CanvasManagerParent::Init(std::move(aEndpoint), mSharedSurfacesHolder,
+ mContentId);
+ mRemoteTextureTxnScheduler = RemoteTextureTxnScheduler::Create(this);
+ return IPC_OK();
+}
+
+/* static */
+void CompositorManagerParent::NotifyWebRenderError(wr::WebRenderError aError) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ StaticMonitorAutoLock lock(sMonitor);
+ 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..17eeb61c8c
--- /dev/null
+++ b/gfx/layers/ipc/CompositorManagerParent.h
@@ -0,0 +1,114 @@
+/* -*- 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 <map>
+#include <stdint.h> // for uint32_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/StaticMonitor.h" // for StaticMonitor
+#include "mozilla/RefPtr.h" // for already_AddRefed
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/layers/PCompositorManagerParent.h"
+#include "nsTArray.h" // for AutoTArray
+
+namespace mozilla {
+
+namespace gfx {
+class SourceSurfaceSharedData;
+}
+
+namespace layers {
+
+class CompositorBridgeParent;
+class CompositorThreadHolder;
+class RemoteTextureTxnScheduler;
+class SharedSurfacesHolder;
+
+class CompositorManagerParent final : public PCompositorManagerParent {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerParent, final)
+
+ public:
+ static already_AddRefed<CompositorManagerParent> CreateSameProcess(
+ uint32_t aNamespace);
+ static bool Create(Endpoint<PCompositorManagerParent>&& aEndpoint,
+ dom::ContentParentId aContentId, uint32_t aNamespace,
+ bool aIsRoot);
+ static void Shutdown();
+
+ static already_AddRefed<CompositorBridgeParent>
+ CreateSameProcessWidgetCompositorBridge(CSSToLayoutDeviceScale aScale,
+ const CompositorOptions& aOptions,
+ bool aUseExternalSurfaceSize,
+ const gfx::IntSize& aSurfaceSize,
+ uint64_t aInnerWindowId);
+
+ static void WaitForSharedSurface(const wr::ExternalImageId& aId);
+
+ static void AddSharedSurface(const wr::ExternalImageId& aId,
+ gfx::SourceSurfaceSharedData* aSurface);
+
+ mozilla::ipc::IPCResult RecvAddSharedSurface(const wr::ExternalImageId& aId,
+ SurfaceDescriptorShared&& aDesc);
+ mozilla::ipc::IPCResult RecvRemoveSharedSurface(
+ const wr::ExternalImageId& aId);
+ mozilla::ipc::IPCResult RecvReportSharedSurfacesMemory(
+ ReportSharedSurfacesMemoryResolver&&);
+
+ mozilla::ipc::IPCResult RecvNotifyMemoryPressure();
+
+ mozilla::ipc::IPCResult RecvReportMemory(ReportMemoryResolver&&);
+
+ mozilla::ipc::IPCResult RecvInitCanvasManager(
+ Endpoint<PCanvasManagerParent>&&);
+
+ void BindComplete(bool aIsRoot);
+ void ActorDestroy(ActorDestroyReason aReason) override;
+
+ already_AddRefed<PCompositorBridgeParent> AllocPCompositorBridgeParent(
+ const CompositorBridgeOptions& aOpt);
+
+ static void NotifyWebRenderError(wr::WebRenderError aError);
+
+ const dom::ContentParentId& GetContentId() const { return mContentId; }
+
+ bool OwnsExternalImageId(const wr::ExternalImageId& aId) const {
+ return mNamespace == static_cast<uint32_t>(wr::AsUint64(aId) >> 32);
+ }
+
+ private:
+ static StaticMonitor sMonitor;
+ static StaticRefPtr<CompositorManagerParent> sInstance
+ MOZ_GUARDED_BY(sMonitor);
+
+ // Indexed by namespace.
+ using ManagerMap = std::map<uint32_t, CompositorManagerParent*>;
+ static ManagerMap sManagers MOZ_GUARDED_BY(sMonitor);
+
+ static void ShutdownInternal();
+
+ CompositorManagerParent(dom::ContentParentId aContentId, uint32_t aNamespace);
+ virtual ~CompositorManagerParent();
+
+ void Bind(Endpoint<PCompositorManagerParent>&& aEndpoint, bool aIsRoot);
+
+ void DeferredDestroy();
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+ RefPtr<SharedSurfacesHolder> mSharedSurfacesHolder;
+ AutoTArray<RefPtr<CompositorBridgeParent>, 1> mPendingCompositorBridges;
+ const dom::ContentParentId mContentId;
+ const uint32_t mNamespace;
+ uint32_t mLastSharedSurfaceResourceId MOZ_GUARDED_BY(sMonitor) = 0;
+ RefPtr<RemoteTextureTxnScheduler> mRemoteTextureTxnScheduler;
+};
+
+} // 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..65fbdc66f1
--- /dev/null
+++ b/gfx/layers/ipc/CompositorThread.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "CompositorThread.h"
+
+#include "CompositorBridgeParent.h"
+#include "gfxGradientCache.h"
+#include "MainThreadUtils.h"
+#include "VRManagerParent.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/VideoBridgeParent.h"
+#include "mozilla/media/MediaSystemResourceService.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace layers {
+
+static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
+static Atomic<bool> sFinishedCompositorShutDown(false);
+static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor;
+static ProfilerThreadId sProfilerThreadId;
+
+nsIThread* CompositorThread() {
+ return sCompositorThreadHolder
+ ? sCompositorThreadHolder->GetCompositorThread()
+ : nullptr;
+}
+
+CompositorThreadHolder* CompositorThreadHolder::GetSingleton() {
+ return sCompositorThreadHolder;
+}
+
+CompositorThreadHolder::CompositorThreadHolder()
+ : mCompositorThread(CreateCompositorThread()) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+CompositorThreadHolder::~CompositorThreadHolder() {
+ sFinishedCompositorShutDown = true;
+}
+
+/* static */ already_AddRefed<nsIThread>
+CompositorThreadHolder::CreateCompositorThread() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(!sCompositorThreadHolder,
+ "The compositor thread has already been started!");
+
+ // When the CanvasRenderer thread is disabled, WebGL may be handled on this
+ // thread, requiring a bigger stack size. See: CanvasManagerParent::Init
+ //
+ // This is 4M, which is higher than the default 256K.
+ // Increased with bug 1753349 to accommodate the `chromium/5359` branch of
+ // ANGLE, which has large peak stack usage for some pathological shader
+ // compilations.
+ //
+ // Previously increased to 512K to accommodate Mesa in bug 1753340.
+ //
+ // Previously increased to 320K to avoid a stack overflow in the
+ // Intel Vulkan driver initialization in bug 1716120.
+ //
+ // Note: we only override it if it's limited already.
+ uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE;
+ if (stackSize) {
+ stackSize =
+ std::max(stackSize, gfx::gfxVars::SupportsThreadsafeGL() &&
+ !gfx::gfxVars::UseCanvasRenderThread()
+ ? 4096U << 10
+ : 512U << 10);
+ }
+
+ nsCOMPtr<nsIThread> compositorThread;
+ nsresult rv = NS_NewNamedThread(
+ "Compositor", getter_AddRefs(compositorThread),
+ NS_NewRunnableFunction(
+ "CompositorThreadHolder::CompositorThreadHolderSetup",
+ []() {
+ sProfilerThreadId = profiler_current_thread_id();
+ sBackgroundHangMonitor = new mozilla::BackgroundHangMonitor(
+ "Compositor",
+ /* Timeout values are powers-of-two to enable us get better
+ data. 128ms is chosen for transient hangs because 8Hz should
+ be the minimally acceptable goal for Compositor
+ responsiveness (normal goal is 60Hz). */
+ 128,
+ /* 2048ms is chosen for permanent hangs because it's longer than
+ * most Compositor hangs seen in the wild, but is short enough
+ * to not miss getting native hang stacks. */
+ 2048);
+ nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
+ static_cast<nsThread*>(thread.get())->SetUseHangMonitor(true);
+ }),
+ {.stackSize = stackSize});
+
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ CompositorBridgeParent::Setup();
+ ImageBridgeParent::Setup();
+
+ return compositorThread.forget();
+}
+
+void CompositorThreadHolder::Start() {
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+ MOZ_ASSERT(!sCompositorThreadHolder,
+ "The compositor thread has already been started!");
+
+ // We unset the holder instead of asserting because failing to start the
+ // compositor thread may not be a fatal error. As long as this succeeds in
+ // either the GPU process or the UI process, the user will have a usable
+ // browser. If we get neither, it will crash as soon as we try to post to the
+ // compositor thread for the first time.
+ sProfilerThreadId = ProfilerThreadId();
+ sCompositorThreadHolder = new CompositorThreadHolder();
+ if (!sCompositorThreadHolder->GetCompositorThread()) {
+ gfxCriticalNote << "Compositor thread not started ("
+ << XRE_IsParentProcess() << ")";
+ sCompositorThreadHolder = nullptr;
+ }
+}
+
+void CompositorThreadHolder::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+ if (!sCompositorThreadHolder) {
+ // We've already shutdown or never started.
+ return;
+ }
+
+ ImageBridgeParent::Shutdown();
+ gfx::VRManagerParent::Shutdown();
+ MediaSystemResourceService::Shutdown();
+ CompositorManagerParent::Shutdown();
+ gfx::gfxGradientCache::Shutdown();
+
+ // Ensure there are no pending tasks that would cause an access to the
+ // thread's HangMonitor. APZ and Canvas can keep a reference to the compositor
+ // thread and may continue to dispatch tasks on it as the system shuts down.
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "CompositorThreadHolder::Shutdown",
+ [compositorThreadHolder =
+ RefPtr<CompositorThreadHolder>(sCompositorThreadHolder),
+ backgroundHangMonitor = UniquePtr<mozilla::BackgroundHangMonitor>(
+ sBackgroundHangMonitor)]() {
+ VideoBridgeParent::UnregisterExternalImages();
+ nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
+ static_cast<nsThread*>(thread.get())->SetUseHangMonitor(false);
+ }));
+
+ sCompositorThreadHolder = nullptr;
+ sBackgroundHangMonitor = nullptr;
+
+ SpinEventLoopUntil("CompositorThreadHolder::Shutdown"_ns, [&]() {
+ bool finished = sFinishedCompositorShutDown;
+ return finished;
+ });
+
+ // At this point, the CompositorThreadHolder instance will have been
+ // destroyed, but the compositor thread itself may still be running due to
+ // APZ/Canvas code holding a reference to the underlying
+ // nsIThread/nsISerialEventTarget. Any tasks scheduled to run on the
+ // compositor thread earlier in this function will have been run to
+ // completion.
+ CompositorBridgeParent::FinishShutdown();
+}
+
+/* static */
+bool CompositorThreadHolder::IsInCompositorThread() {
+ if (!CompositorThread()) {
+ return false;
+ }
+ bool in = false;
+ MOZ_ALWAYS_SUCCEEDS(CompositorThread()->IsOnCurrentThread(&in));
+ return in;
+}
+
+/* static */
+ProfilerThreadId CompositorThreadHolder::GetThreadId() {
+ return sCompositorThreadHolder ? sProfilerThreadId : ProfilerThreadId();
+}
+
+} // namespace layers
+} // namespace mozilla
+
+bool NS_IsInCompositorThread() {
+ return mozilla::layers::CompositorThreadHolder::IsInCompositorThread();
+}
diff --git a/gfx/layers/ipc/CompositorThread.h b/gfx/layers/ipc/CompositorThread.h
new file mode 100644
index 0000000000..3352889982
--- /dev/null
+++ b/gfx/layers/ipc/CompositorThread.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_layers_CompositorThread_h
+#define mozilla_layers_CompositorThread_h
+
+#include "nsISupportsImpl.h"
+#include "nsIThread.h"
+
+namespace mozilla::baseprofiler {
+class BaseProfilerThreadId;
+}
+using ProfilerThreadId = mozilla::baseprofiler::BaseProfilerThreadId;
+class nsIThread;
+
+namespace mozilla {
+namespace layers {
+
+class CompositorThreadHolder final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
+ CompositorThreadHolder)
+
+ public:
+ CompositorThreadHolder();
+
+ nsIThread* GetCompositorThread() const { return mCompositorThread; }
+
+ /**
+ * Returns true if the calling thread is the compositor thread. This works
+ * even if the CompositorThread has begun to shutdown.
+ */
+ bool IsInThread() {
+ bool rv = false;
+ MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mCompositorThread->IsOnCurrentThread(&rv)));
+ return rv;
+ }
+
+ nsresult Dispatch(already_AddRefed<nsIRunnable> event,
+ uint32_t flags = nsIEventTarget::DISPATCH_NORMAL) {
+ return mCompositorThread->Dispatch(std::move(event), flags);
+ }
+
+ static CompositorThreadHolder* GetSingleton();
+
+ static bool IsActive() { return !!GetSingleton(); }
+
+ /**
+ * Creates the compositor thread and the global compositor map.
+ */
+ static void Start();
+
+ /*
+ * Waits for all [CrossProcess]CompositorBridgeParents to shutdown and
+ * releases compositor-thread owned resources.
+ */
+ static void Shutdown();
+
+ // Returns true if the calling thread is the compositor thread.
+ static bool IsInCompositorThread();
+
+ // Thread id to use as a MarkerThreadId option for profiler markers.
+ static ProfilerThreadId GetThreadId();
+
+ private:
+ ~CompositorThreadHolder();
+
+ const nsCOMPtr<nsIThread> mCompositorThread;
+
+ static already_AddRefed<nsIThread> CreateCompositorThread();
+
+ friend class CompositorBridgeParent;
+};
+
+nsIThread* CompositorThread();
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorThread_h
diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.cpp b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
new file mode 100644
index 0000000000..6ef8903015
--- /dev/null
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/CompositorVsyncScheduler.h"
+
+#include <stdio.h> // for fprintf, stdout
+#include <stdint.h> // for uint64_t
+#include "base/task.h" // for CancelableTask, etc
+#include "base/thread.h" // for Thread
+#include "gfxPlatform.h" // for gfxPlatform
+#ifdef MOZ_WIDGET_GTK
+# include "gfxPlatformGtk.h" // for gfxPlatform
+#endif
+#include "mozilla/AutoRestore.h" // for AutoRestore
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for IntSize
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorVsyncSchedulerOwner.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/Telemetry.h"
+#include "mozilla/VsyncDispatcher.h"
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+# include "VsyncSource.h"
+#endif
+#include "mozilla/widget/CompositorWidget.h"
+#include "VRManager.h"
+
+namespace mozilla {
+
+namespace layers {
+
+using namespace mozilla::gfx;
+
+CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner)
+ : mMutex("CompositorVsyncScheduler.Observer.Mutex"), mOwner(aOwner) {}
+
+CompositorVsyncScheduler::Observer::~Observer() { MOZ_ASSERT(!mOwner); }
+
+void CompositorVsyncScheduler::Observer::NotifyVsync(const VsyncEvent& aVsync) {
+ MutexAutoLock lock(mMutex);
+ if (!mOwner) {
+ return;
+ }
+ mOwner->NotifyVsync(aVsync);
+}
+
+void CompositorVsyncScheduler::Observer::Destroy() {
+ MutexAutoLock lock(mMutex);
+ mOwner = nullptr;
+}
+
+CompositorVsyncScheduler::CompositorVsyncScheduler(
+ CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner,
+ widget::CompositorWidget* aWidget)
+ : mVsyncSchedulerOwner(aVsyncSchedulerOwner),
+ mLastComposeTime(SampleTime::FromNow()),
+ mLastVsyncTime(TimeStamp::Now()),
+ mLastVsyncOutputTime(TimeStamp::Now()),
+ mIsObservingVsync(false),
+ mRendersDelayedByVsyncReasons(wr::RenderReasons::NONE),
+ mVsyncNotificationsSkipped(0),
+ mWidget(aWidget),
+ mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor"),
+ mCurrentCompositeTask(nullptr),
+ mCurrentCompositeTaskReasons(wr::RenderReasons::NONE),
+ mCurrentVRTaskMonitor("CurrentVRTaskMonitor"),
+ mCurrentVRTask(nullptr) {
+ mVsyncObserver = new Observer(this);
+
+ // mAsapScheduling is set on the main thread during init,
+ // but is only accessed after on the compositor thread.
+ mAsapScheduling =
+ StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
+ gfxPlatform::IsInLayoutAsapMode();
+}
+
+CompositorVsyncScheduler::~CompositorVsyncScheduler() {
+ MOZ_ASSERT(!mIsObservingVsync);
+ MOZ_ASSERT(!mVsyncObserver);
+ // The CompositorVsyncDispatcher is cleaned up before this in the
+ // nsBaseWidget, which stops vsync listeners
+ mVsyncSchedulerOwner = nullptr;
+}
+
+void CompositorVsyncScheduler::Destroy() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ if (!mVsyncObserver) {
+ // Destroy was already called on this object.
+ return;
+ }
+ UnobserveVsync();
+ mVsyncObserver->Destroy();
+ mVsyncObserver = nullptr;
+
+ mCompositeRequestedAt = TimeStamp();
+ CancelCurrentCompositeTask();
+ CancelCurrentVRTask();
+}
+
+void CompositorVsyncScheduler::PostCompositeTask(const VsyncEvent& aVsyncEvent,
+ wr::RenderReasons aReasons) {
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ mCurrentCompositeTaskReasons = mCurrentCompositeTaskReasons | aReasons;
+ if (mCurrentCompositeTask == nullptr && CompositorThread()) {
+ RefPtr<CancelableRunnable> task =
+ NewCancelableRunnableMethod<VsyncEvent, wr::RenderReasons>(
+ "layers::CompositorVsyncScheduler::Composite", this,
+ &CompositorVsyncScheduler::Composite, aVsyncEvent, aReasons);
+ mCurrentCompositeTask = task;
+ CompositorThread()->Dispatch(task.forget());
+ }
+}
+
+void CompositorVsyncScheduler::PostVRTask(TimeStamp aTimestamp) {
+ MonitorAutoLock lockVR(mCurrentVRTaskMonitor);
+ if (mCurrentVRTask == nullptr && CompositorThread()) {
+ RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>(
+ "layers::CompositorVsyncScheduler::DispatchVREvents", this,
+ &CompositorVsyncScheduler::DispatchVREvents, aTimestamp);
+ mCurrentVRTask = task;
+ CompositorThread()->Dispatch(task.forget());
+ }
+}
+
+void CompositorVsyncScheduler::ScheduleComposition(wr::RenderReasons aReasons) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!mVsyncObserver) {
+ // Destroy was already called on this object.
+ return;
+ }
+
+ // Make a synthetic vsync event for the calls to PostCompositeTask below.
+ TimeStamp vsyncTime = TimeStamp::Now();
+ TimeStamp outputTime = vsyncTime + mVsyncSchedulerOwner->GetVsyncInterval();
+ VsyncEvent vsyncEvent(VsyncId(), vsyncTime, outputTime);
+
+ if (mAsapScheduling) {
+ // Used only for performance testing purposes, and when recording/replaying
+ // to ensure that graphics are up to date.
+ PostCompositeTask(vsyncEvent, aReasons);
+ } else {
+ if (!mCompositeRequestedAt) {
+ mCompositeRequestedAt = TimeStamp::Now();
+ }
+ if (!mIsObservingVsync && mCompositeRequestedAt) {
+ ObserveVsync();
+ // Starting to observe vsync is an async operation that goes
+ // through the main thread of the UI process. It's possible that
+ // we're blocking there waiting on a composite, so schedule an initial
+ // one now to get things started.
+ PostCompositeTask(vsyncEvent,
+ aReasons | wr::RenderReasons::START_OBSERVING_VSYNC);
+ } else {
+ mRendersDelayedByVsyncReasons = aReasons;
+ }
+ }
+}
+
+void CompositorVsyncScheduler::NotifyVsync(const VsyncEvent& aVsync) {
+ // Called from the vsync dispatch thread. When in the GPU Process, that's
+ // the same as the compositor thread.
+#ifdef DEBUG
+# ifdef MOZ_WAYLAND
+ // On Wayland, we dispatch vsync from the main thread, without a GPU process.
+ // To allow this, we skip the following asserts if we're currently utilizing
+ // the Wayland backend. The IsParentProcess guard is needed to ensure that
+ // we don't accidentally attempt to initialize the gfxPlatform in the GPU
+ // process on X11.
+ if (!XRE_IsParentProcess() ||
+ !gfxPlatformGtk::GetPlatform()->IsWaylandDisplay())
+# endif // MOZ_WAYLAND
+ {
+ MOZ_ASSERT_IF(XRE_IsParentProcess(),
+ !CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(!NS_IsMainThread());
+ }
+
+ MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU,
+ CompositorThreadHolder::IsInCompositorThread());
+#endif // DEBUG
+
+#if defined(MOZ_WIDGET_ANDROID)
+ gfx::VRManager* vm = gfx::VRManager::Get();
+ if (!vm->IsPresenting()) {
+ PostCompositeTask(aVsync, wr::RenderReasons::VSYNC);
+ }
+#else
+ PostCompositeTask(aVsync, wr::RenderReasons::VSYNC);
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ PostVRTask(aVsync.mTime);
+}
+
+void CompositorVsyncScheduler::CancelCurrentVRTask() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
+ NS_IsMainThread());
+ MonitorAutoLock lock(mCurrentVRTaskMonitor);
+ if (mCurrentVRTask) {
+ mCurrentVRTask->Cancel();
+ mCurrentVRTask = nullptr;
+ }
+}
+
+wr::RenderReasons CompositorVsyncScheduler::CancelCurrentCompositeTask() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() ||
+ NS_IsMainThread());
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ wr::RenderReasons canceledTaskRenderReasons = mCurrentCompositeTaskReasons;
+ mCurrentCompositeTaskReasons = wr::RenderReasons::NONE;
+ if (mCurrentCompositeTask) {
+ mCurrentCompositeTask->Cancel();
+ mCurrentCompositeTask = nullptr;
+ }
+
+ return canceledTaskRenderReasons;
+}
+
+void CompositorVsyncScheduler::Composite(const VsyncEvent& aVsyncEvent,
+ wr::RenderReasons aReasons) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(mVsyncSchedulerOwner);
+
+ { // scope lock
+ MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
+ aReasons =
+ aReasons | mCurrentCompositeTaskReasons | mRendersDelayedByVsyncReasons;
+ mCurrentCompositeTaskReasons = wr::RenderReasons::NONE;
+ mRendersDelayedByVsyncReasons = wr::RenderReasons::NONE;
+ mCurrentCompositeTask = nullptr;
+ }
+
+ mLastVsyncTime = aVsyncEvent.mTime;
+ mLastVsyncOutputTime = aVsyncEvent.mOutputTime;
+ mLastVsyncId = aVsyncEvent.mId;
+
+ if (!mAsapScheduling) {
+ // Some early exit conditions if we're not in ASAP mode
+ if (aVsyncEvent.mTime < mLastComposeTime.Time()) {
+ // We can sometimes get vsync timestamps that are in the past
+ // compared to the last compose with force composites.
+ // In those cases, wait until the next vsync;
+ return;
+ }
+
+ if (mVsyncSchedulerOwner->IsPendingComposite()) {
+ // If previous composite is still on going, finish it and wait for the
+ // next vsync.
+ mVsyncSchedulerOwner->FinishPendingComposite();
+ return;
+ }
+ }
+
+ if (mCompositeRequestedAt || mAsapScheduling) {
+ mCompositeRequestedAt = TimeStamp();
+ mLastComposeTime = SampleTime::FromVsync(aVsyncEvent.mTime);
+
+ // Tell the owner to do a composite
+ mVsyncSchedulerOwner->CompositeToTarget(aVsyncEvent.mId, aReasons, nullptr,
+ nullptr);
+
+ mVsyncNotificationsSkipped = 0;
+
+ TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncEvent.mTime;
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME,
+ compositeFrameTotal.ToMilliseconds());
+ } else if (mVsyncNotificationsSkipped++ >
+ StaticPrefs::gfx_vsync_compositor_unobserve_count_AtStartup()) {
+ UnobserveVsync();
+ }
+}
+
+void CompositorVsyncScheduler::ForceComposeToTarget(wr::RenderReasons aReasons,
+ gfx::DrawTarget* aTarget,
+ const IntRect* aRect) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+ /**
+ * bug 1138502 - There are cases such as during long-running window resizing
+ * events where we receive many force-composites. We also continue to get
+ * vsync notifications. Because the force-composites trigger compositing and
+ * clear the mCompositeRequestedAt timestamp, the vsync notifications will not
+ * need to do anything and so will increment the mVsyncNotificationsSkipped
+ * counter to indicate the vsync was ignored. If this happens enough times, we
+ * will disable listening for vsync entirely. On the next force-composite we
+ * will enable listening for vsync again, and continued force-composites and
+ * vsyncs will cause oscillation between observing vsync and not. On some
+ * platforms, enabling/disabling vsync is not free and this oscillating
+ * behavior causes a performance hit. In order to avoid this problem, we reset
+ * the mVsyncNotificationsSkipped counter to keep vsync enabled.
+ */
+ mVsyncNotificationsSkipped = 0;
+
+ mLastComposeTime = SampleTime::FromNow();
+ MOZ_ASSERT(mVsyncSchedulerOwner);
+ mVsyncSchedulerOwner->CompositeToTarget(VsyncId(), aReasons, aTarget, aRect);
+}
+
+bool CompositorVsyncScheduler::NeedsComposite() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return (bool)mCompositeRequestedAt;
+}
+
+bool CompositorVsyncScheduler::FlushPendingComposite() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (mCompositeRequestedAt) {
+ wr::RenderReasons reasons = CancelCurrentCompositeTask();
+ ForceComposeToTarget(reasons, nullptr, nullptr);
+ return true;
+ }
+ return false;
+}
+
+void CompositorVsyncScheduler::ObserveVsync() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mWidget->ObserveVsync(mVsyncObserver);
+ mIsObservingVsync = true;
+}
+
+void CompositorVsyncScheduler::UnobserveVsync() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mWidget->ObserveVsync(nullptr);
+ mIsObservingVsync = false;
+}
+
+void CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp) {
+ {
+ MonitorAutoLock lock(mCurrentVRTaskMonitor);
+ mCurrentVRTask = nullptr;
+ }
+ // This only allows to be called by CompositorVsyncScheduler::PostVRTask()
+ // When the process is going to shutdown, the runnable has chance to be
+ // executed by other threads, we only want it to be run in the compositor
+ // thread.
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ return;
+ }
+
+ VRManager* vm = VRManager::Get();
+ vm->NotifyVsync(aVsyncTimestamp);
+}
+
+const SampleTime& CompositorVsyncScheduler::GetLastComposeTime() const {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mLastComposeTime;
+}
+
+const TimeStamp& CompositorVsyncScheduler::GetLastVsyncTime() const {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mLastVsyncTime;
+}
+
+const TimeStamp& CompositorVsyncScheduler::GetLastVsyncOutputTime() const {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mLastVsyncOutputTime;
+}
+
+const VsyncId& CompositorVsyncScheduler::GetLastVsyncId() const {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ return mLastVsyncId;
+}
+
+void CompositorVsyncScheduler::UpdateLastComposeTime() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ mLastComposeTime = SampleTime::FromNow();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.h b/gfx/layers/ipc/CompositorVsyncScheduler.h
new file mode 100644
index 0000000000..da0d3d453a
--- /dev/null
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.h
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorVsyncScheduler_h
+#define mozilla_layers_CompositorVsyncScheduler_h
+
+#include <stdint.h> // for uint64_t
+
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/Monitor.h" // for Monitor
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/SampleTime.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+class CancelableRunnable;
+class Runnable;
+
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class CompositorVsyncSchedulerOwner;
+
+/**
+ * Manages the vsync (de)registration and tracking on behalf of the
+ * compositor when it need to paint.
+ * Turns vsync notifications into scheduled composites.
+ **/
+class CompositorVsyncScheduler {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler)
+
+ public:
+ CompositorVsyncScheduler(CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner,
+ widget::CompositorWidget* aWidget);
+
+ /**
+ * Notify this class of a vsync. This will trigger a composite if one is
+ * needed. This must be called from the vsync dispatch thread.
+ */
+ void NotifyVsync(const VsyncEvent& aVsync);
+
+ /**
+ * Do cleanup. This must be called on the compositor thread.
+ */
+ void Destroy();
+
+ /**
+ * Notify this class that a composition is needed. This will trigger a
+ * composition soon (likely at the next vsync). This must be called on the
+ * compositor thread.
+ */
+ void ScheduleComposition(wr::RenderReasons aReasons);
+
+ /**
+ * Cancel any composite task that has been scheduled but hasn't run yet.
+ *
+ * Returns the render reasons of the canceled task if any.
+ */
+ wr::RenderReasons CancelCurrentCompositeTask();
+
+ /**
+ * Check if a composite is pending. This is generally true between a call
+ * to ScheduleComposition() and the time the composite happens.
+ */
+ bool NeedsComposite();
+
+ /**
+ * Force a composite to happen right away, without waiting for the next vsync.
+ * This must be called on the compositor thread.
+ */
+ void ForceComposeToTarget(wr::RenderReasons aReasons,
+ gfx::DrawTarget* aTarget,
+ const gfx::IntRect* aRect);
+
+ /**
+ * If a composite is pending, force it to trigger right away. This must be
+ * called on the compositor thread. Returns true if there was a composite
+ * flushed.
+ */
+ bool FlushPendingComposite();
+
+ /**
+ * Return the vsync timestamp of the last or ongoing composite. Must be called
+ * on the compositor thread.
+ */
+ const SampleTime& GetLastComposeTime() const;
+
+ /**
+ * Return the vsync timestamp and id of the most recently received
+ * vsync event. Must be called on the compositor thread.
+ */
+ const TimeStamp& GetLastVsyncTime() const;
+ const TimeStamp& GetLastVsyncOutputTime() const;
+ const VsyncId& GetLastVsyncId() const;
+
+ /**
+ * Update LastCompose TimeStamp to current timestamp.
+ * The function is typically used when composition is handled outside the
+ * CompositorVsyncScheduler.
+ */
+ void UpdateLastComposeTime();
+
+ private:
+ virtual ~CompositorVsyncScheduler();
+
+ // Post a task to run Composite() on the compositor thread, if there isn't
+ // such a task already queued. Can be called from any thread.
+ void PostCompositeTask(const VsyncEvent& aVsyncEvent,
+ wr::RenderReasons aReasons);
+
+ // Post a task to run DispatchVREvents() on the VR thread, if there isn't
+ // such a task already queued. Can be called from any thread.
+ void PostVRTask(TimeStamp aTimestamp);
+
+ /**
+ * Cancel any VR task that has been scheduled but hasn't run yet.
+ */
+ void CancelCurrentVRTask();
+
+ // This gets run at vsync time and "does" a composite (which really means
+ // update internal state and call the owner to do the composite).
+ void Composite(const VsyncEvent& aVsyncEvent, wr::RenderReasons aReasons);
+
+ void ObserveVsync();
+ void UnobserveVsync();
+
+ void DispatchVREvents(TimeStamp aVsyncTimestamp);
+
+ class Observer final : public VsyncObserver {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler::Observer,
+ override)
+
+ public:
+ explicit Observer(CompositorVsyncScheduler* aOwner);
+ void NotifyVsync(const VsyncEvent& aVsync) override;
+ void Destroy();
+
+ private:
+ virtual ~Observer();
+
+ Mutex mMutex;
+ // Hold raw pointer to avoid mutual reference.
+ CompositorVsyncScheduler* mOwner;
+ };
+
+ CompositorVsyncSchedulerOwner* mVsyncSchedulerOwner;
+ SampleTime mLastComposeTime;
+ TimeStamp mLastVsyncTime;
+ TimeStamp mLastVsyncOutputTime;
+ VsyncId mLastVsyncId;
+
+ bool mAsapScheduling;
+ bool mIsObservingVsync;
+ // Accessed on the compositor thread.
+ wr::RenderReasons mRendersDelayedByVsyncReasons;
+ TimeStamp mCompositeRequestedAt;
+ int32_t mVsyncNotificationsSkipped;
+ widget::CompositorWidget* mWidget;
+ RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver;
+
+ mozilla::Monitor mCurrentCompositeTaskMonitor;
+ RefPtr<CancelableRunnable> mCurrentCompositeTask
+ MOZ_GUARDED_BY(mCurrentCompositeTaskMonitor);
+ // Accessed on multiple threads, guarded by mCurrentCompositeTaskMonitor.
+ wr::RenderReasons mCurrentCompositeTaskReasons
+ MOZ_GUARDED_BY(mCurrentCompositeTaskMonitor);
+
+ mozilla::Monitor mCurrentVRTaskMonitor;
+ RefPtr<CancelableRunnable> mCurrentVRTask
+ MOZ_GUARDED_BY(mCurrentVRTaskMonitor);
+};
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorVsyncScheduler_h
diff --git a/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h b/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h
new file mode 100644
index 0000000000..ac147fd241
--- /dev/null
+++ b/gfx/layers/ipc/CompositorVsyncSchedulerOwner.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CompositorVsyncSchedulerOwner_h
+#define mozilla_layers_CompositorVsyncSchedulerOwner_h
+
+#include "mozilla/VsyncDispatcher.h"
+#include "mozilla/webrender/webrender_ffi.h"
+
+namespace mozilla {
+
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class CompositorVsyncSchedulerOwner {
+ public:
+ virtual bool IsPendingComposite() = 0;
+ virtual void FinishPendingComposite() = 0;
+ virtual void CompositeToTarget(VsyncId aId, wr::RenderReasons aReasons,
+ gfx::DrawTarget* aTarget,
+ const gfx::IntRect* aRect = nullptr) = 0;
+ virtual TimeDuration GetVsyncInterval() const = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorVsyncSchedulerOwner_h
diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
new file mode 100644
index 0000000000..4936cbbbb8
--- /dev/null
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
@@ -0,0 +1,438 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/ContentCompositorBridgeParent.h"
+
+#include <stdint.h> // for uint64_t
+
+#include "apz/src/APZCTreeManager.h" // for APZCTreeManager
+#include "gfxUtils.h"
+#ifdef XP_WIN
+# include "mozilla/gfx/DeviceManagerDx.h" // for DeviceManagerDx
+# include "mozilla/layers/ImageDataSerializer.h"
+#endif
+#include "mozilla/layers/AnimationHelper.h" // for CompositorAnimationStorage
+#include "mozilla/layers/APZCTreeManagerParent.h" // for APZCTreeManagerParent
+#include "mozilla/layers/APZUpdater.h" // for APZUpdater
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/layers/CompositorOptions.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsTArray.h" // for nsTArray
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#include "mozilla/Unused.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/BaseProfilerMarkerTypes.h"
+#include "GeckoProfiler.h"
+
+namespace mozilla::layers {
+
+void EraseLayerState(LayersId aId);
+
+void ContentCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+ mCanSend = false;
+
+ // We must keep this object alive untill the code handling message
+ // reception is finished on this thread.
+ GetCurrentSerialEventTarget()->Dispatch(NewRunnableMethod(
+ "layers::ContentCompositorBridgeParent::DeferredDestroy", this,
+ &ContentCompositorBridgeParent::DeferredDestroy));
+}
+
+PAPZCTreeManagerParent*
+ContentCompositorBridgeParent::AllocPAPZCTreeManagerParent(
+ const LayersId& aLayersId) {
+ // Check to see if this child process has access to this layer tree.
+ if (!LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, OtherPid())) {
+ NS_ERROR(
+ "Unexpected layers id in AllocPAPZCTreeManagerParent; dropping "
+ "message...");
+ return nullptr;
+ }
+
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state =
+ CompositorBridgeParent::sIndirectLayerTrees[aLayersId];
+
+ // If the widget has shutdown its compositor, we may not have had a chance yet
+ // to unmap our layers id, and we could get here without a parent compositor.
+ // In this case return an empty APZCTM.
+ if (!state.mParent) {
+ // Note: we immediately call ClearTree since otherwise the APZCTM will
+ // retain a reference to itself, through the checkerboard observer.
+ LayersId dummyId{0};
+ const bool connectedToWebRender = false;
+ RefPtr<APZCTreeManager> temp = APZCTreeManager::Create(dummyId);
+ RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp, connectedToWebRender);
+ tempUpdater->ClearTree(dummyId);
+ return new APZCTreeManagerParent(aLayersId, temp, tempUpdater);
+ }
+
+ // If we do not have APZ enabled, we should gracefully fail.
+ if (!state.mParent->GetOptions().UseAPZ()) {
+ return nullptr;
+ }
+
+ state.mParent->AllocateAPZCTreeManagerParent(lock, aLayersId, state);
+ return state.mApzcTreeManagerParent;
+}
+
+bool ContentCompositorBridgeParent::DeallocPAPZCTreeManagerParent(
+ PAPZCTreeManagerParent* aActor) {
+ APZCTreeManagerParent* parent = static_cast<APZCTreeManagerParent*>(aActor);
+
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ auto iter =
+ CompositorBridgeParent::sIndirectLayerTrees.find(parent->GetLayersId());
+ if (iter != CompositorBridgeParent::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();
+
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ CompositorBridgeParent::LayerTreeState& state =
+ CompositorBridgeParent::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
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ MOZ_ASSERT(CompositorBridgeParent::sIndirectLayerTrees.find(layersId) !=
+ CompositorBridgeParent::sIndirectLayerTrees.end());
+ MOZ_ASSERT(
+ CompositorBridgeParent::sIndirectLayerTrees[layersId].mWrBridge ==
+ nullptr);
+ cbp = CompositorBridgeParent::sIndirectLayerTrees[layersId].mParent;
+ if (cbp) {
+ root = CompositorBridgeParent::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
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ CompositorBridgeParent::sIndirectLayerTrees[layersId]
+ .mContentCompositorBridgeParent = this;
+ CompositorBridgeParent::sIndirectLayerTrees[layersId].mWrBridge = parent;
+ }
+
+ return parent;
+}
+
+bool ContentCompositorBridgeParent::DeallocPWebRenderBridgeParent(
+ PWebRenderBridgeParent* aActor) {
+ WebRenderBridgeParent* parent = static_cast<WebRenderBridgeParent*>(aActor);
+ EraseLayerState(wr::AsLayersId(parent->PipelineId()));
+ parent->Release(); // IPDL reference
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvNotifyChildCreated(
+ const LayersId& child, CompositorOptions* aOptions) {
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ for (auto it = CompositorBridgeParent::sIndirectLayerTrees.begin();
+ it != CompositorBridgeParent::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::RecvNotifyMemoryPressure() {
+ // This can only be called from the browser process.
+ return IPC_FAIL_NO_REASON(this);
+}
+
+mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvCheckContentOnlyTDR(
+ const uint32_t& sequenceNum, bool* isContentOnlyTDR) {
+ *isContentOnlyTDR = false;
+#ifdef XP_WIN
+ gfx::ContentDeviceData compositor;
+
+ gfx::DeviceManagerDx* dm = gfx::DeviceManagerDx::Get();
+
+ // Check that the D3D11 device sequence numbers match.
+ gfx::D3D11DeviceStatus status;
+ dm->ExportDeviceInfo(&status);
+
+ if (sequenceNum == static_cast<uint32_t>(status.sequenceNumber()) &&
+ !dm->HasDeviceReset()) {
+ *isContentOnlyTDR = true;
+ }
+
+#endif
+ return IPC_OK();
+};
+
+void ContentCompositorBridgeParent::DidCompositeLocked(
+ LayersId aId, const VsyncId& aVsyncId, TimeStamp& aCompositeStart,
+ TimeStamp& aCompositeEnd) {
+ CompositorBridgeParent::sIndirectLayerTreesLock.AssertCurrentThreadOwns();
+ if (CompositorBridgeParent::sIndirectLayerTrees[aId].mWrBridge) {
+ MOZ_ASSERT(false); // this should never get called for a WR compositor
+ }
+}
+
+bool ContentCompositorBridgeParent::SetTestSampleTime(const LayersId& aId,
+ const TimeStamp& aTime) {
+ MOZ_ASSERT(aId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aId);
+ if (!state) {
+ return false;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ return state->mParent->SetTestSampleTime(aId, aTime);
+}
+
+void ContentCompositorBridgeParent::LeaveTestMode(const LayersId& aId) {
+ MOZ_ASSERT(aId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aId);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->LeaveTestMode(aId);
+}
+
+void ContentCompositorBridgeParent::SetTestAsyncScrollOffset(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
+}
+
+void ContentCompositorBridgeParent::SetTestAsyncZoom(
+ const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state) {
+ return;
+ }
+
+ MOZ_ASSERT(state->mParent);
+ state->mParent->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
+}
+
+void ContentCompositorBridgeParent::FlushApzRepaints(
+ const LayersId& aLayersId) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->FlushApzRepaints(aLayersId);
+}
+
+void ContentCompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->GetAPZTestData(aLayersId, aOutData);
+}
+
+void ContentCompositorBridgeParent::GetFrameUniformity(
+ const LayersId& aLayersId, FrameUniformityData* aOutData) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->GetFrameUniformity(aLayersId, aOutData);
+}
+
+void ContentCompositorBridgeParent::SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) {
+ MOZ_ASSERT(aLayersId.IsValid());
+ const CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ state->mParent->SetConfirmedTargetAPZC(aLayersId, aInputBlockId,
+ std::move(aTargets));
+}
+
+void ContentCompositorBridgeParent::DeferredDestroy() { mSelfRef = nullptr; }
+
+ContentCompositorBridgeParent::~ContentCompositorBridgeParent() {
+ MOZ_ASSERT(XRE_GetIOMessageLoop());
+}
+
+PTextureParent* ContentCompositorBridgeParent::AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ CompositorBridgeParent::LayerTreeState* state = nullptr;
+
+ StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
+ auto itr = CompositorBridgeParent::sIndirectLayerTrees.find(aId);
+ if (CompositorBridgeParent::sIndirectLayerTrees.end() != itr) {
+ state = &itr->second;
+ }
+
+ TextureFlags flags = aFlags;
+
+ LayersBackend actualBackend = LayersBackend::LAYERS_NONE;
+ if (!state) {
+ // The compositor was recreated, and we're receiving layers updates for a
+ // a layer manager that will soon be discarded or invalidated. We can't
+ // return null because this will mess up deserialization later and we'll
+ // kill the content process. Instead, we signal that the underlying
+ // TextureHost should not attempt to access the compositor.
+ flags |= TextureFlags::INVALID_COMPOSITOR;
+ } else if (actualBackend != LayersBackend::LAYERS_NONE &&
+ aLayersBackend != actualBackend) {
+ gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch)
+ << "Texture backend is wrong";
+ }
+
+ return TextureHost::CreateIPDLActor(
+ this, aSharedData, std::move(aReadLock), aLayersBackend, aFlags,
+ mCompositorManager->GetContentId(), aSerial, aExternalImageId);
+}
+
+bool ContentCompositorBridgeParent::DeallocPTextureParent(
+ PTextureParent* actor) {
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+bool ContentCompositorBridgeParent::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void ContentCompositorBridgeParent::ObserveLayersUpdate(LayersId aLayersId,
+ bool aActive) {
+ MOZ_ASSERT(aLayersId.IsValid());
+
+ CompositorBridgeParent::LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+ if (!state || !state->mParent) {
+ return;
+ }
+
+ Unused << state->mParent->SendObserveLayersUpdate(aLayersId, aActive);
+}
+
+} // namespace mozilla::layers
diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.h b/gfx/layers/ipc/ContentCompositorBridgeParent.h
new file mode 100644
index 0000000000..2425b33c7b
--- /dev/null
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.h
@@ -0,0 +1,175 @@
+/* -*- 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/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla::layers {
+
+class CompositorOptions;
+
+/**
+ * This class handles layer updates pushed directly from child processes to
+ * the compositor thread. It's associated with a CompositorBridgeParent on the
+ * compositor thread. While it uses the PCompositorBridge protocol to manage
+ * these updates, it doesn't actually drive compositing itself. For that it
+ * hands off work to the CompositorBridgeParent it's associated with.
+ */
+class ContentCompositorBridgeParent final : public CompositorBridgeParentBase {
+ friend class CompositorBridgeParent;
+
+ public:
+ explicit ContentCompositorBridgeParent(CompositorManagerParent* aManager)
+ : CompositorBridgeParentBase(aManager), mDestroyCalled(false) {}
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // FIXME/bug 774388: work out what shutdown protocol we need.
+ mozilla::ipc::IPCResult RecvInitialize(
+ const LayersId& aRootLayerTreeId) override {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mozilla::ipc::IPCResult RecvWillClose() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvPause() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvRequestFxrOutput() override {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mozilla::ipc::IPCResult RecvResume() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvResumeAsync() override { return IPC_OK(); }
+ mozilla::ipc::IPCResult RecvNotifyChildCreated(
+ const LayersId& child, CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvMapAndNotifyChildCreated(
+ const LayersId& child, const base::ProcessId& pid,
+ CompositorOptions* aOptions) override;
+ mozilla::ipc::IPCResult RecvNotifyChildRecreated(
+ const LayersId& child, CompositorOptions* aOptions) override {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mozilla::ipc::IPCResult RecvAdoptChild(const LayersId& child) override {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ mozilla::ipc::IPCResult RecvFlushRendering(
+ const wr::RenderReasons&) override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvFlushRenderingAsync(
+ const wr::RenderReasons&) override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvForcePresent(const wr::RenderReasons&) override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvWaitOnTransactionProcessed() override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvStartFrameTimeRecording(
+ const int32_t& aBufferSize, uint32_t* aOutStartIndex) override {
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvStopFrameTimeRecording(
+ const uint32_t& aStartIndex, nsTArray<float>* intervals) override {
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult RecvNotifyMemoryPressure() override;
+
+ mozilla::ipc::IPCResult RecvCheckContentOnlyTDR(
+ const uint32_t& sequenceNum, bool* isContentOnlyTDR) override;
+
+ mozilla::ipc::IPCResult RecvBeginRecording(
+ const TimeStamp& aRecordingStart,
+ BeginRecordingResolver&& aResolve) override {
+ aResolve(false);
+ return IPC_OK();
+ }
+
+ mozilla::ipc::IPCResult RecvEndRecording(
+ EndRecordingResolver&& aResolve) override {
+ aResolve(Nothing());
+ return IPC_OK();
+ }
+
+ bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override;
+ void LeaveTestMode(const LayersId& aId) override;
+ void SetTestAsyncScrollOffset(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const CSSPoint& aPoint) override;
+ void SetTestAsyncZoom(const LayersId& aLayersId,
+ const ScrollableLayerGuid::ViewID& aScrollId,
+ const LayerToParentLayerScale& aZoom) override;
+ void FlushApzRepaints(const LayersId& aLayersId) override;
+ void GetAPZTestData(const LayersId& aLayersId,
+ APZTestData* aOutData) override;
+ void GetFrameUniformity(const LayersId& aLayersId,
+ FrameUniformityData* aOutData) override;
+ void SetConfirmedTargetAPZC(
+ const LayersId& aLayersId, const uint64_t& aInputBlockId,
+ nsTArray<ScrollableLayerGuid>&& aTargets) override;
+
+ // Use DidCompositeLocked if you already hold a lock on
+ // sIndirectLayerTreesLock; Otherwise use DidComposite, which would request
+ // the lock automatically.
+ void DidCompositeLocked(LayersId aId, const VsyncId& aVsyncId,
+ TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
+
+ PTextureParent* AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const LayersId& aId, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) override;
+
+ bool DeallocPTextureParent(PTextureParent* actor) override;
+
+ bool IsSameProcess() const override;
+
+ PCompositorWidgetParent* AllocPCompositorWidgetParent(
+ const CompositorWidgetInitData& aInitData) override {
+ // Not allowed.
+ return nullptr;
+ }
+ bool DeallocPCompositorWidgetParent(
+ PCompositorWidgetParent* aActor) override {
+ // Not allowed.
+ return false;
+ }
+
+ PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(
+ const LayersId& aLayersId) override;
+ bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
+
+ PAPZParent* AllocPAPZParent(const LayersId& aLayersId) override;
+ bool DeallocPAPZParent(PAPZParent* aActor) override;
+
+ PWebRenderBridgeParent* AllocPWebRenderBridgeParent(
+ const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize,
+ const WindowKind& aWindowKind) override;
+ bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
+
+ void ObserveLayersUpdate(LayersId aLayersId, bool aActive) override;
+
+ bool IsRemote() const override { return true; }
+
+ private:
+ // Private destructor, to discourage deletion outside of Release():
+ virtual ~ContentCompositorBridgeParent();
+
+ void DeferredDestroy();
+
+ // There can be many CPCPs, and IPDL-generated code doesn't hold a
+ // reference to top-level actors. So we hold a reference to
+ // ourself. This is released (deferred) in ActorDestroy().
+ RefPtr<ContentCompositorBridgeParent> mSelfRef;
+
+ bool mDestroyCalled;
+};
+
+} // namespace mozilla::layers
+
+#endif // mozilla_layers_ContentCompositorBridgeParent_h
diff --git a/gfx/layers/ipc/ISurfaceAllocator.cpp b/gfx/layers/ipc/ISurfaceAllocator.cpp
new file mode 100644
index 0000000000..9e3a66f977
--- /dev/null
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ISurfaceAllocator.h"
+
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/TextureHost.h" // for TextureHost
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/CompositableForwarder.h"
+
+namespace mozilla {
+namespace layers {
+
+NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter)
+
+mozilla::Atomic<ptrdiff_t> GfxMemoryImageReporter::sAmount(0);
+
+void HostIPCAllocator::SendPendingAsyncMessages() {
+ if (mPendingAsyncMessage.empty()) {
+ return;
+ }
+
+ // Some type of AsyncParentMessageData message could have
+ // one file descriptor (e.g. OpDeliverFence).
+ // A number of file descriptors per gecko ipc message have a limitation
+ // on XP_UNIX (MACOSX or LINUX).
+ static const uint32_t kMaxMessageNumber =
+ IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE;
+
+ nsTArray<AsyncParentMessageData> messages;
+ messages.SetCapacity(mPendingAsyncMessage.size());
+ for (size_t i = 0; i < mPendingAsyncMessage.size(); i++) {
+ messages.AppendElement(mPendingAsyncMessage[i]);
+ // Limit maximum number of messages.
+ if (messages.Length() >= kMaxMessageNumber) {
+ SendAsyncMessage(messages);
+ // Initialize Messages.
+ messages.Clear();
+ }
+ }
+
+ if (messages.Length() > 0) {
+ SendAsyncMessage(messages);
+ }
+ mPendingAsyncMessage.clear();
+}
+
+// XXX - We should actually figure out the minimum shmem allocation size on
+// a certain platform and use that.
+const uint32_t sShmemPageSize = 4096;
+
+#ifdef DEBUG
+const uint32_t sSupportedBlockSize = 4;
+#endif
+
+FixedSizeSmallShmemSectionAllocator::FixedSizeSmallShmemSectionAllocator(
+ LayersIPCChannel* aShmProvider)
+ : mShmProvider(aShmProvider) {
+ MOZ_ASSERT(mShmProvider);
+}
+
+FixedSizeSmallShmemSectionAllocator::~FixedSizeSmallShmemSectionAllocator() {
+ ShrinkShmemSectionHeap();
+}
+
+bool FixedSizeSmallShmemSectionAllocator::IPCOpen() const {
+ return mShmProvider->IPCOpen();
+}
+
+bool FixedSizeSmallShmemSectionAllocator::AllocShmemSection(
+ uint32_t aSize, ShmemSection* aShmemSection) {
+ // For now we only support sizes of 4. If we want to support different sizes
+ // some more complicated bookkeeping should be added.
+ NS_ASSERT_OWNINGTHREAD(FixedSizeSmallShmemSectionAllocator);
+ MOZ_ASSERT(aSize == sSupportedBlockSize);
+ MOZ_ASSERT(aShmemSection);
+
+ if (!IPCOpen()) {
+ gfxCriticalError() << "Attempt to allocate a ShmemSection after shutdown.";
+ return false;
+ }
+
+ uint32_t allocationSize = (aSize + sizeof(ShmemSectionHeapAllocation));
+
+ for (size_t i = 0; i < mUsedShmems.size(); i++) {
+ ShmemSectionHeapHeader* header =
+ mUsedShmems[i].get<ShmemSectionHeapHeader>();
+ if ((header->mAllocatedBlocks + 1) * allocationSize +
+ sizeof(ShmemSectionHeapHeader) <
+ sShmemPageSize) {
+ aShmemSection->shmem() = mUsedShmems[i];
+ MOZ_ASSERT(mUsedShmems[i].IsWritable());
+ break;
+ }
+ }
+
+ if (!aShmemSection->shmem().IsWritable()) {
+ ipc::Shmem tmp;
+ if (!mShmProvider->AllocUnsafeShmem(sShmemPageSize, &tmp)) {
+ return false;
+ }
+
+ ShmemSectionHeapHeader* header = tmp.get<ShmemSectionHeapHeader>();
+ header->mTotalBlocks = 0;
+ header->mAllocatedBlocks = 0;
+
+ mUsedShmems.push_back(tmp);
+ aShmemSection->shmem() = tmp;
+ }
+
+ MOZ_ASSERT(aShmemSection->shmem().IsWritable());
+
+ ShmemSectionHeapHeader* header =
+ aShmemSection->shmem().get<ShmemSectionHeapHeader>();
+ uint8_t* heap =
+ aShmemSection->shmem().get<uint8_t>() + sizeof(ShmemSectionHeapHeader);
+
+ ShmemSectionHeapAllocation* allocHeader = nullptr;
+
+ if (header->mTotalBlocks > header->mAllocatedBlocks) {
+ // Search for the first available block.
+ for (size_t i = 0; i < header->mTotalBlocks; i++) {
+ allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+
+ if (allocHeader->mStatus == STATUS_FREED) {
+ break;
+ }
+ heap += allocationSize;
+ }
+ MOZ_ASSERT(allocHeader && allocHeader->mStatus == STATUS_FREED);
+ MOZ_ASSERT(allocHeader->mSize == sSupportedBlockSize);
+ } else {
+ heap += header->mTotalBlocks * allocationSize;
+
+ header->mTotalBlocks++;
+ allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+ allocHeader->mSize = aSize;
+ }
+
+ MOZ_ASSERT(allocHeader);
+ header->mAllocatedBlocks++;
+ allocHeader->mStatus = STATUS_ALLOCATED;
+
+ aShmemSection->size() = aSize;
+ aShmemSection->offset() = (heap + sizeof(ShmemSectionHeapAllocation)) -
+ aShmemSection->shmem().get<uint8_t>();
+ ShrinkShmemSectionHeap();
+ return true;
+}
+
+void FixedSizeSmallShmemSectionAllocator::FreeShmemSection(
+ mozilla::layers::ShmemSection& aShmemSection) {
+ MOZ_ASSERT(aShmemSection.size() == sSupportedBlockSize);
+ MOZ_ASSERT(aShmemSection.offset() < sShmemPageSize - sSupportedBlockSize);
+
+ if (!aShmemSection.shmem().IsWritable()) {
+ return;
+ }
+
+ ShmemSectionHeapAllocation* allocHeader =
+ reinterpret_cast<ShmemSectionHeapAllocation*>(
+ aShmemSection.shmem().get<char>() + aShmemSection.offset() -
+ sizeof(ShmemSectionHeapAllocation));
+
+ MOZ_ASSERT(allocHeader->mSize == aShmemSection.size());
+
+ DebugOnly<bool> success =
+ allocHeader->mStatus.compareExchange(STATUS_ALLOCATED, STATUS_FREED);
+ // If this fails something really weird is going on.
+ MOZ_ASSERT(success);
+
+ ShmemSectionHeapHeader* header =
+ aShmemSection.shmem().get<ShmemSectionHeapHeader>();
+ header->mAllocatedBlocks--;
+}
+
+void FixedSizeSmallShmemSectionAllocator::DeallocShmemSection(
+ mozilla::layers::ShmemSection& aShmemSection) {
+ NS_ASSERT_OWNINGTHREAD(FixedSizeSmallShmemSectionAllocator);
+
+ if (!IPCOpen()) {
+ gfxCriticalNote << "Attempt to dealloc a ShmemSections after shutdown.";
+ return;
+ }
+
+ FreeShmemSection(aShmemSection);
+ ShrinkShmemSectionHeap();
+}
+
+void FixedSizeSmallShmemSectionAllocator::ShrinkShmemSectionHeap() {
+ NS_ASSERT_OWNINGTHREAD(FixedSizeSmallShmemSectionAllocator);
+
+ 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..7faa3aeab9
--- /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/dom/ipc/IdType.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/RefPtr.h"
+#include "nsIMemoryReporter.h" // for nsIMemoryReporter
+#include "mozilla/Atomics.h" // for Atomic
+#include "mozilla/layers/LayersMessages.h" // for ShmemSection
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+class IShmemAllocator;
+} // namespace ipc
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class CompositableForwarder;
+class CompositorBridgeParentBase;
+class TextureForwarder;
+
+class ShmemSectionAllocator;
+class LegacySurfaceDescriptorAllocator;
+class ClientIPCAllocator;
+class HostIPCAllocator;
+class LayersIPCChannel;
+
+enum BufferCapabilities {
+ DEFAULT_BUFFER_CAPS = 0,
+ /**
+ * The allocated buffer must be efficiently mappable as a DataSourceSurface.
+ */
+ MAP_AS_IMAGE_SURFACE = 1 << 0,
+ /**
+ * The allocated buffer will be used for GL rendering only
+ */
+ USING_GL_RENDERING_ONLY = 1 << 1
+};
+
+class SurfaceDescriptor;
+
+/**
+ * An interface used to create and destroy surfaces that are shared with the
+ * Compositor process (using shmem, or other platform specific memory)
+ *
+ * Most of the methods here correspond to methods that are implemented by IPDL
+ * actors without a common polymorphic interface.
+ * These methods should be only called in the ipdl implementor's thread, unless
+ * specified otherwise in the implementing class.
+ */
+class ISurfaceAllocator {
+ public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(ISurfaceAllocator)
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ISurfaceAllocator)
+
+ ISurfaceAllocator() = default;
+
+ // down-casting
+
+ virtual mozilla::ipc::IShmemAllocator* AsShmemAllocator() { return nullptr; }
+
+ virtual ShmemSectionAllocator* AsShmemSectionAllocator() { return nullptr; }
+
+ virtual CompositableForwarder* AsCompositableForwarder() { return nullptr; }
+
+ virtual TextureForwarder* GetTextureForwarder() { return nullptr; }
+
+ virtual ClientIPCAllocator* AsClientAllocator() { return nullptr; }
+
+ virtual HostIPCAllocator* AsHostIPCAllocator() { return nullptr; }
+
+ virtual LegacySurfaceDescriptorAllocator*
+ AsLegacySurfaceDescriptorAllocator() {
+ return nullptr;
+ }
+
+ virtual CompositorBridgeParentBase* AsCompositorBridgeParentBase() {
+ return nullptr;
+ }
+
+ // ipc info
+
+ virtual bool IPCOpen() const { return true; }
+
+ virtual bool IsSameProcess() const = 0;
+
+ virtual bool UsesImageBridge() const { return false; }
+
+ virtual bool UsesWebRenderBridge() const { return false; }
+
+ virtual dom::ContentParentId GetContentId() { return dom::ContentParentId(); }
+
+ protected:
+ void Finalize() {}
+
+ virtual ~ISurfaceAllocator() = default;
+};
+
+/// Methods that are specific to the client/child side.
+class ClientIPCAllocator : public ISurfaceAllocator {
+ public:
+ ClientIPCAllocator() = default;
+
+ ClientIPCAllocator* AsClientAllocator() override { return this; }
+
+ virtual base::ProcessId GetParentPid() const = 0;
+
+ virtual MessageLoop* GetMessageLoop() const = 0;
+
+ virtual void CancelWaitForNotifyNotUsed(uint64_t aTextureId) = 0;
+};
+
+/// Methods that are specific to the host/parent side.
+class HostIPCAllocator : public ISurfaceAllocator {
+ public:
+ HostIPCAllocator() = default;
+
+ HostIPCAllocator* AsHostIPCAllocator() override { return this; }
+
+ /**
+ * Get child side's process Id.
+ */
+ virtual base::ProcessId GetChildProcessId() = 0;
+
+ virtual void NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) = 0;
+
+ virtual void SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) = 0;
+
+ virtual void SendPendingAsyncMessages();
+
+ virtual void SetAboutToSendAsyncMessages() {
+ mAboutToSendAsyncMessages = true;
+ }
+
+ bool IsAboutToSendAsyncMessages() { return mAboutToSendAsyncMessages; }
+
+ protected:
+ std::vector<AsyncParentMessageData> mPendingAsyncMessage;
+ bool mAboutToSendAsyncMessages = false;
+};
+
+/// An allocator that can group allocations in bigger chunks of shared memory.
+///
+/// The allocated shmem sections can only be deallocated by the same allocator
+/// instance (and only in the child process).
+class ShmemSectionAllocator {
+ public:
+ virtual bool AllocShmemSection(uint32_t aSize,
+ ShmemSection* aShmemSection) = 0;
+
+ virtual void DeallocShmemSection(ShmemSection& aShmemSection) = 0;
+
+ virtual void MemoryPressure() {}
+};
+
+/// Some old stuff that's still around and used for screenshots.
+///
+/// New code should not need this (see TextureClient).
+class LegacySurfaceDescriptorAllocator {
+ public:
+ virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ SurfaceDescriptor* aBuffer) = 0;
+
+ virtual bool AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
+ gfxContentType aContent,
+ uint32_t aCaps,
+ SurfaceDescriptor* aBuffer) = 0;
+
+ virtual void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) = 0;
+};
+
+bool IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface);
+
+already_AddRefed<gfx::DataSourceSurface> GetSurfaceForDescriptor(
+ const SurfaceDescriptor& aDescriptor);
+
+uint8_t* GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor);
+
+void DestroySurfaceDescriptor(mozilla::ipc::IShmemAllocator* aAllocator,
+ SurfaceDescriptor* aSurface);
+
+class GfxMemoryImageReporter final : public nsIMemoryReporter {
+ ~GfxMemoryImageReporter() = default;
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ GfxMemoryImageReporter() {
+#ifdef DEBUG
+ // There must be only one instance of this class, due to |sAmount|
+ // being static.
+ static bool hasRun = false;
+ MOZ_ASSERT(!hasRun);
+ hasRun = true;
+#endif
+ }
+
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
+ MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
+
+ static void DidAlloc(void* aPointer) {
+ sAmount += MallocSizeOfOnAlloc(aPointer);
+ }
+
+ static void WillFree(void* aPointer) {
+ sAmount -= MallocSizeOfOnFree(aPointer);
+ }
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override {
+ MOZ_COLLECT_REPORT(
+ "explicit/gfx/heap-textures", KIND_HEAP, UNITS_BYTES, sAmount,
+ "Heap memory shared between threads by texture clients and hosts.");
+
+ return NS_OK;
+ }
+
+ private:
+ // Typically we use |size_t| in memory reporters, but in the past this
+ // variable has sometimes gone negative due to missing DidAlloc() calls.
+ // Therefore, we use a signed type so that any such negative values show up
+ // as negative in about:memory, rather than as enormous positive numbers.
+ static mozilla::Atomic<ptrdiff_t> sAmount;
+};
+
+/// A simple shmem section allocator that can only allocate small
+/// fixed size elements (only intended to be used to store tile
+/// copy-on-write locks for now).
+class FixedSizeSmallShmemSectionAllocator final : public ShmemSectionAllocator {
+ public:
+ NS_DECL_OWNINGTHREAD
+
+ 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..32489afabd
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -0,0 +1,975 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageBridgeChild.h"
+
+#include <vector> // for vector
+
+#include "ImageBridgeParent.h" // for ImageBridgeParent
+#include "ImageContainer.h" // for ImageContainer
+#include "SynchronousTask.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
+#include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/LayersMessages.h" // for CompositableOperation
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
+#include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "transport/runnable_utils.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindowInner.h"
+#include "nsISupportsImpl.h" // for ImageContainer::AddRef, etc
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsTArrayForwardDeclare.h" // for AutoTArray
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "WindowRenderer.h"
+
+#if defined(XP_WIN)
+# include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::media;
+
+typedef std::vector<CompositableOperation> OpVector;
+typedef nsTArray<OpDestroy> OpDestroyVector;
+
+struct CompositableTransaction {
+ CompositableTransaction() : mFinished(true) {}
+ ~CompositableTransaction() { End(); }
+ bool Finished() const { return mFinished; }
+ void Begin() {
+ MOZ_ASSERT(mFinished);
+ mFinished = false;
+ }
+ void End() {
+ mFinished = true;
+ mOperations.clear();
+ mDestroyedActors.Clear();
+ }
+ bool IsEmpty() const {
+ return mOperations.empty() && mDestroyedActors.IsEmpty();
+ }
+ void AddNoSwapEdit(const CompositableOperation& op) {
+ MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
+ mOperations.push_back(op);
+ }
+
+ OpVector mOperations;
+ OpDestroyVector mDestroyedActors;
+
+ bool mFinished;
+};
+
+struct AutoEndTransaction final {
+ explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
+ ~AutoEndTransaction() { mTxn->End(); }
+ CompositableTransaction* mTxn;
+};
+
+void ImageBridgeChild::UseTextures(
+ CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) {
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aCompositable->GetIPCHandle());
+ MOZ_ASSERT(aCompositable->IsConnected());
+
+ AutoTArray<TimedTexture, 4> textures;
+
+ for (auto& t : aTextures) {
+ MOZ_ASSERT(t.mTextureClient);
+ MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
+
+ if (!t.mTextureClient->IsSharedWithCompositor()) {
+ return;
+ }
+
+ bool readLocked = t.mTextureClient->OnForwardedToHost();
+
+ textures.AppendElement(TimedTexture(
+ WrapNotNull(t.mTextureClient->GetIPDLActor()), t.mTimeStamp,
+ t.mPictureRect, t.mFrameID, t.mProducerID, readLocked));
+
+ // Wait end of usage on host side if TextureFlags::RECYCLE is set
+ HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
+ }
+ mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(),
+ OpUseTexture(textures)));
+}
+
+void ImageBridgeChild::UseRemoteTexture(
+ CompositableClient* aCompositable, const RemoteTextureId aTextureId,
+ const RemoteTextureOwnerId aOwnerId, const gfx::IntSize aSize,
+ const TextureFlags aFlags, const RefPtr<FwdTransactionTracker>& aTracker) {
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(aCompositable->GetIPCHandle());
+ MOZ_ASSERT(aCompositable->IsConnected());
+
+ mTxn->AddNoSwapEdit(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpUseRemoteTexture(aTextureId, aOwnerId, aSize, aFlags)));
+ TrackFwdTransaction(aTracker);
+}
+
+void ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(
+ TextureClient* aClient) {
+ if (!aClient) {
+ return;
+ }
+
+ // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE or
+ // TextureFlags::WAIT_HOST_USAGE_END is set on ImageBridge.
+ bool waitNotifyNotUsed =
+ aClient->GetFlags() & TextureFlags::RECYCLE ||
+ aClient->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END;
+ if (!waitNotifyNotUsed) {
+ return;
+ }
+
+ aClient->SetLastFwdTransactionId(GetFwdTransactionId());
+ mTexturesWaitingNotifyNotUsed.emplace(aClient->GetSerial(), aClient);
+}
+
+void ImageBridgeChild::NotifyNotUsed(uint64_t aTextureId,
+ uint64_t aFwdTransactionId) {
+ auto it = mTexturesWaitingNotifyNotUsed.find(aTextureId);
+ if (it != mTexturesWaitingNotifyNotUsed.end()) {
+ if (aFwdTransactionId < it->second->GetLastFwdTransactionId()) {
+ // Released on host side, but client already requested newer use texture.
+ return;
+ }
+ mTexturesWaitingNotifyNotUsed.erase(it);
+ }
+}
+
+void ImageBridgeChild::CancelWaitForNotifyNotUsed(uint64_t aTextureId) {
+ MOZ_ASSERT(InImageBridgeChildThread());
+ mTexturesWaitingNotifyNotUsed.erase(aTextureId);
+}
+
+// Singleton
+static StaticMutex sImageBridgeSingletonLock MOZ_UNANNOTATED;
+static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
+static StaticRefPtr<nsIThread> sImageBridgeChildThread;
+
+// dispatched function
+void ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask) {
+ AutoCompleteTask complete(aTask);
+
+ MOZ_ASSERT(InImageBridgeChildThread(),
+ "Should be in ImageBridgeChild thread.");
+
+ MediaSystemResourceManager::Shutdown();
+
+ // Force all managed protocols to shut themselves down cleanly
+ nsTArray<PTextureChild*> textures;
+ ManagedPTextureChild(textures);
+ for (int i = textures.Length() - 1; i >= 0; --i) {
+ RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
+ if (client) {
+ client->Destroy();
+ }
+ }
+
+ if (mCanSend) {
+ SendWillClose();
+ }
+ MarkShutDown();
+
+ // From now on, no message can be sent through the image bridge from the
+ // client side except the final Stop message.
+}
+
+// dispatched function
+void ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask) {
+ AutoCompleteTask complete(aTask);
+
+ MOZ_ASSERT(InImageBridgeChildThread(),
+ "Should be in ImageBridgeChild thread.");
+
+ mSectionAllocator = nullptr;
+
+ if (!mDestroyed) {
+ Close();
+ }
+}
+
+void ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mCanSend = false;
+ mDestroyed = true;
+ {
+ MutexAutoLock lock(mContainerMapLock);
+ mImageContainerListeners.clear();
+ }
+}
+
+void ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer) {
+ AutoCompleteTask complete(aTask);
+ *result = CreateImageClientNow(aType, aImageContainer);
+}
+
+ImageBridgeChild::ImageBridgeChild(uint32_t aNamespace)
+ : mNamespace(aNamespace),
+ mCanSend(false),
+ mDestroyed(false),
+ mFwdTransactionCounter(this),
+ mContainerMapLock("ImageBridgeChild.mContainerMapLock") {
+ MOZ_ASSERT(mNamespace);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mTxn = new CompositableTransaction();
+}
+
+ImageBridgeChild::~ImageBridgeChild() { delete mTxn; }
+
+void ImageBridgeChild::MarkShutDown() {
+ mTexturesWaitingNotifyNotUsed.clear();
+
+ mCanSend = false;
+}
+
+void ImageBridgeChild::Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) {
+ MOZ_ASSERT(aCompositable);
+ MOZ_ASSERT(InImageBridgeChildThread());
+ MOZ_ASSERT(CanSend());
+
+ CompositableHandle handle = CompositableHandle::GetNext();
+
+ // ImageClient of ImageContainer provides aImageContainer.
+ // But offscreen canvas does not provide it.
+ if (aImageContainer) {
+ MutexAutoLock lock(mContainerMapLock);
+ MOZ_ASSERT(mImageContainerListeners.find(uint64_t(handle)) ==
+ mImageContainerListeners.end());
+ mImageContainerListeners.emplace(
+ uint64_t(handle), aImageContainer->GetImageContainerListener());
+ }
+
+ aCompositable->InitIPDL(handle);
+ SendNewCompositable(handle, aCompositable->GetTextureInfo());
+}
+
+void ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle) {
+ MutexAutoLock lock(mContainerMapLock);
+ mImageContainerListeners.erase(aHandle.Value());
+}
+
+/* static */
+RefPtr<ImageBridgeChild> ImageBridgeChild::GetSingleton() {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ return sImageBridgeChildSingleton;
+}
+
+void ImageBridgeChild::UpdateImageClient(RefPtr<ImageContainer> aContainer) {
+ if (!aContainer) {
+ return;
+ }
+
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable =
+ WrapRunnable(RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::UpdateImageClient, aContainer);
+ GetThread()->Dispatch(runnable.forget());
+ return;
+ }
+
+ if (!CanSend()) {
+ return;
+ }
+
+ RefPtr<ImageClient> client = aContainer->GetImageClient();
+ if (NS_WARN_IF(!client)) {
+ return;
+ }
+
+ // If the client has become disconnected before this event was dispatched,
+ // early return now.
+ if (!client->IsConnected()) {
+ return;
+ }
+
+ BeginTransaction();
+ client->UpdateImage(aContainer);
+ EndTransaction();
+}
+
+void ImageBridgeChild::UpdateCompositable(
+ const RefPtr<ImageContainer> aContainer, const RemoteTextureId aTextureId,
+ const RemoteTextureOwnerId aOwnerId, const gfx::IntSize aSize,
+ const TextureFlags aFlags, const RefPtr<FwdTransactionTracker> aTracker) {
+ if (!aContainer) {
+ return;
+ }
+
+ if (!InImageBridgeChildThread()) {
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::UpdateCompositable,
+ aContainer, aTextureId, aOwnerId, aSize, aFlags, aTracker);
+ GetThread()->Dispatch(runnable.forget());
+ return;
+ }
+
+ if (!CanSend()) {
+ return;
+ }
+
+ RefPtr<ImageClient> client = aContainer->GetImageClient();
+ if (NS_WARN_IF(!client)) {
+ return;
+ }
+
+ // If the client has become disconnected before this event was dispatched,
+ // early return now.
+ if (!client->IsConnected()) {
+ return;
+ }
+
+ BeginTransaction();
+ UseRemoteTexture(client, aTextureId, aOwnerId, aSize, aFlags, aTracker);
+ 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::SyncWithCompositor(const Maybe<uint64_t>& aWindowID) {
+ if (NS_WARN_IF(InImageBridgeChildThread())) {
+ MOZ_ASSERT_UNREACHABLE("Cannot call on ImageBridge thread!");
+ return;
+ }
+
+ if (!aWindowID) {
+ return;
+ }
+
+ const auto fnSyncWithWindow = [&]() {
+ if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(*aWindowID)) {
+ if (auto* widget = window->GetNearestWidget()) {
+ if (auto* renderer = widget->GetWindowRenderer()) {
+ if (auto* kc = renderer->AsKnowsCompositor()) {
+ kc->SyncWithCompositor();
+ }
+ }
+ }
+ }
+ };
+
+ if (NS_IsMainThread()) {
+ fnSyncWithWindow();
+ return;
+ }
+
+ SynchronousTask task("SyncWithCompositor Lock");
+ RefPtr<Runnable> runnable =
+ NS_NewRunnableFunction("ImageBridgeChild::SyncWithCompositor", [&]() {
+ AutoCompleteTask complete(&task);
+ fnSyncWithWindow();
+ });
+ NS_DispatchToMainThread(runnable.forget());
+ task.Wait();
+}
+
+void ImageBridgeChild::BeginTransaction() {
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
+ UpdateFwdTransactionId();
+ mTxn->Begin();
+}
+
+void ImageBridgeChild::EndTransaction() {
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
+
+ AutoEndTransaction _(mTxn);
+
+ if (mTxn->IsEmpty()) {
+ return;
+ }
+
+ AutoTArray<CompositableOperation, 10> cset;
+ cset.SetCapacity(mTxn->mOperations.size());
+ if (!mTxn->mOperations.empty()) {
+ cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
+ }
+
+ if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
+ NS_WARNING("could not send async texture transaction");
+ return;
+ }
+}
+
+bool ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
+ uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ gfxPlatform::GetPlatform();
+
+ if (!sImageBridgeChildThread) {
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_NewNamedThread("ImageBridgeChld", getter_AddRefs(thread));
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to start ImageBridgeChild thread!");
+ sImageBridgeChildThread = thread.forget();
+ }
+
+ RefPtr<ImageBridgeChild> child = new ImageBridgeChild(aNamespace);
+
+ child->GetThread()->Dispatch(NS_NewRunnableFunction(
+ "layers::ImageBridgeChild::Bind",
+ [child, endpoint = std::move(aEndpoint)]() mutable {
+ child->Bind(std::move(endpoint));
+ }));
+
+ // Assign this after so other threads can't post messages before we connect to
+ // IPDL.
+ {
+ StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+ sImageBridgeChildSingleton = child;
+ }
+
+ return true;
+}
+
+bool ImageBridgeChild::ReinitForContent(Endpoint<PImageBridgeChild>&& aEndpoint,
+ uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Note that at this point, ActorDestroy may not have been called yet,
+ // meaning mCanSend is still true. In this case we will try to send a
+ // synchronous WillClose message to the parent, and will certainly get a
+ // false result and a MsgDropped processing error. This is okay.
+ ShutdownSingleton();
+
+ return InitForContent(std::move(aEndpoint), aNamespace);
+}
+
+void ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ return;
+ }
+
+ mSectionAllocator = MakeUnique<FixedSizeSmallShmemSectionAllocator>(this);
+ mCanSend = true;
+}
+
+void ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent) {
+ Open(aParent, aParent->GetThread(), mozilla::ipc::ChildSide);
+
+ mSectionAllocator = MakeUnique<FixedSizeSmallShmemSectionAllocator>(this);
+ 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();
+}
+
+FixedSizeSmallShmemSectionAllocator* ImageBridgeChild::GetTileLockAllocator() {
+ return mSectionAllocator.get();
+}
+
+nsISerialEventTarget* ImageBridgeChild::GetThread() const {
+ return sImageBridgeChildThread;
+}
+
+/* static */
+void ImageBridgeChild::IdentifyCompositorTextureHost(
+ const TextureFactoryIdentifier& aIdentifier) {
+ if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+ child->UpdateTextureFactoryIdentifier(aIdentifier);
+ }
+}
+
+void ImageBridgeChild::UpdateTextureFactoryIdentifier(
+ const TextureFactoryIdentifier& aIdentifier) {
+ IdentifyTextureHost(aIdentifier);
+}
+
+RefPtr<ImageClient> ImageBridgeChild::CreateImageClient(
+ CompositableType aType, ImageContainer* aImageContainer) {
+ if (InImageBridgeChildThread()) {
+ return CreateImageClientNow(aType, aImageContainer);
+ }
+
+ SynchronousTask task("CreateImageClient Lock");
+
+ RefPtr<ImageClient> result = nullptr;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::CreateImageClientSync,
+ &task, &result, aType, aImageContainer);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+
+ return result;
+}
+
+RefPtr<ImageClient> ImageBridgeChild::CreateImageClientNow(
+ CompositableType aType, ImageContainer* aImageContainer) {
+ MOZ_ASSERT(InImageBridgeChildThread());
+ if (!CanSend()) {
+ return nullptr;
+ }
+
+ RefPtr<ImageClient> client =
+ ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
+ MOZ_ASSERT(client, "failed to create ImageClient");
+ if (client) {
+ client->Connect(aImageContainer);
+ }
+ return client;
+}
+
+bool ImageBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
+ if (!InImageBridgeChildThread()) {
+ return DispatchAllocShmemInternal(aSize, aShmem,
+ true); // true: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::AllocUnsafeShmem(aSize, aShmem);
+}
+
+bool ImageBridgeChild::AllocShmem(size_t aSize, ipc::Shmem* aShmem) {
+ if (!InImageBridgeChildThread()) {
+ return DispatchAllocShmemInternal(aSize, aShmem,
+ false); // false: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::AllocShmem(aSize, aShmem);
+}
+
+void ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
+ ipc::Shmem* aShmem, bool aUnsafe,
+ bool* aSuccess) {
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ bool ok = false;
+ if (aUnsafe) {
+ ok = AllocUnsafeShmem(aSize, aShmem);
+ } else {
+ ok = AllocShmem(aSize, aShmem);
+ }
+ *aSuccess = ok;
+}
+
+bool ImageBridgeChild::DispatchAllocShmemInternal(size_t aSize,
+ ipc::Shmem* aShmem,
+ bool aUnsafe) {
+ SynchronousTask task("AllocatorProxy alloc");
+
+ bool success = false;
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyAllocShmemNow,
+ &task, aSize, aShmem, aUnsafe, &success);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+
+ return success;
+}
+
+void ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
+ ipc::Shmem* aShmem, bool* aResult) {
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+ *aResult = DeallocShmem(*aShmem);
+}
+
+bool ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem) {
+ if (InImageBridgeChildThread()) {
+ if (!CanSend()) {
+ return false;
+ }
+ return PImageBridgeChild::DeallocShmem(aShmem);
+ }
+
+ // If we can't post a task, then we definitely cannot send, so there's
+ // no reason to queue up this send.
+ if (!CanPostTask()) {
+ return false;
+ }
+
+ SynchronousTask task("AllocatorProxy Dealloc");
+ bool result = false;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<ImageBridgeChild>(this), &ImageBridgeChild::ProxyDeallocShmemNow,
+ &task, &aShmem, &result);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+ return result;
+}
+
+PTextureChild* ImageBridgeChild::AllocPTextureChild(
+ const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&,
+ const TextureFlags&, const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(CanSend());
+ return TextureClient::CreateIPDLActor();
+}
+
+bool ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+PMediaSystemResourceManagerChild*
+ImageBridgeChild::AllocPMediaSystemResourceManagerChild() {
+ MOZ_ASSERT(CanSend());
+ return new mozilla::media::MediaSystemResourceManagerChild();
+}
+
+bool ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(
+ PMediaSystemResourceManagerChild* aActor) {
+ MOZ_ASSERT(aActor);
+ delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
+ return true;
+}
+
+mozilla::ipc::IPCResult ImageBridgeChild::RecvParentAsyncMessages(
+ nsTArray<AsyncParentMessageData>&& aMessages) {
+ for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
+ const AsyncParentMessageData& message = aMessages[i];
+
+ switch (message.type()) {
+ case AsyncParentMessageData::TOpNotifyNotUsed: {
+ const OpNotifyNotUsed& op = message.get_OpNotifyNotUsed();
+ NotifyNotUsed(op.TextureId(), op.fwdTransactionId());
+ break;
+ }
+ default:
+ NS_ERROR("unknown AsyncParentMessageData type");
+ return IPC_FAIL_NO_REASON(this);
+ }
+ }
+ return IPC_OK();
+}
+
+RefPtr<ImageContainerListener> ImageBridgeChild::FindListener(
+ const CompositableHandle& aHandle) {
+ RefPtr<ImageContainerListener> listener;
+ MutexAutoLock lock(mContainerMapLock);
+ auto it = mImageContainerListeners.find(aHandle.Value());
+ if (it != mImageContainerListeners.end()) {
+ listener = it->second;
+ }
+ return listener;
+}
+
+mozilla::ipc::IPCResult ImageBridgeChild::RecvDidComposite(
+ nsTArray<ImageCompositeNotification>&& aNotifications) {
+ for (auto& n : aNotifications) {
+ RefPtr<ImageContainerListener> listener = FindListener(n.compositable());
+ if (listener) {
+ listener->NotifyComposite(n);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ImageBridgeChild::RecvReportFramesDropped(
+ const CompositableHandle& aHandle, const uint32_t& aFrames) {
+ RefPtr<ImageContainerListener> listener = FindListener(aHandle);
+ if (listener) {
+ listener->NotifyDropped(aFrames);
+ }
+
+ return IPC_OK();
+}
+
+PTextureChild* ImageBridgeChild::CreateTexture(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ const dom::ContentParentId& aContentId, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(CanSend());
+ return SendPTextureConstructor(aSharedData, std::move(aReadLock),
+ aLayersBackend, aFlags, aSerial,
+ aExternalImageId);
+}
+
+static bool IBCAddOpDestroy(CompositableTransaction* aTxn,
+ const OpDestroy& op) {
+ if (aTxn->Finished()) {
+ return false;
+ }
+
+ aTxn->mDestroyedActors.AppendElement(op);
+ return true;
+}
+
+bool ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture) {
+ return IBCAddOpDestroy(mTxn, OpDestroy(WrapNotNull(aTexture)));
+}
+
+bool ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle) {
+ return IBCAddOpDestroy(mTxn, OpDestroy(aHandle));
+}
+
+void ImageBridgeChild::RemoveTextureFromCompositable(
+ CompositableClient* aCompositable, TextureClient* aTexture) {
+ MOZ_ASSERT(CanSend());
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->IsSharedWithCompositor());
+ MOZ_ASSERT(aCompositable->IsConnected());
+ if (!aTexture || !aTexture->IsSharedWithCompositor() ||
+ !aCompositable->IsConnected()) {
+ return;
+ }
+
+ mTxn->AddNoSwapEdit(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpRemoveTexture(WrapNotNull(aTexture->GetIPDLActor()))));
+}
+
+bool ImageBridgeChild::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+bool ImageBridgeChild::CanPostTask() const {
+ // During shutdown, the cycle collector may free objects that are holding a
+ // reference to ImageBridgeChild. Since this happens on the main thread,
+ // ImageBridgeChild will attempt to post a task to the ImageBridge thread.
+ // However the thread manager has already been shut down, so the task cannot
+ // post.
+ //
+ // It's okay if this races. We only care about the shutdown case where
+ // everything's happening on the main thread. Even if it races outside of
+ // shutdown, it's still harmless to post the task, since the task must
+ // check CanSend().
+ return !mDestroyed;
+}
+
+void ImageBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle) {
+ if (!InImageBridgeChildThread()) {
+ // If we can't post a task, then we definitely cannot send, so there's
+ // no reason to queue up this send.
+ if (!CanPostTask()) {
+ return;
+ }
+
+ RefPtr<Runnable> runnable =
+ WrapRunnable(RefPtr<ImageBridgeChild>(this),
+ &ImageBridgeChild::ReleaseCompositable, aHandle);
+ GetThread()->Dispatch(runnable.forget());
+ return;
+ }
+
+ if (!CanSend()) {
+ return;
+ }
+
+ if (!DestroyInTransaction(aHandle)) {
+ SendReleaseCompositable(aHandle);
+ }
+
+ {
+ MutexAutoLock lock(mContainerMapLock);
+ mImageContainerListeners.erase(aHandle.Value());
+ }
+}
+
+bool ImageBridgeChild::CanSend() const {
+ MOZ_ASSERT(InImageBridgeChildThread());
+ return mCanSend;
+}
+
+void ImageBridgeChild::HandleFatalError(const char* aMsg) {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+wr::MaybeExternalImageId ImageBridgeChild::GetNextExternalImageId() {
+ static uint32_t sNextID = 1;
+ ++sNextID;
+ MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
+
+ uint64_t imageId = mNamespace;
+ imageId = imageId << 32 | sNextID;
+ return Some(wr::ToExternalImageId(imageId));
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ImageBridgeChild.h b/gfx/layers/ipc/ImageBridgeChild.h
new file mode 100644
index 0000000000..d5a8b7199d
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -0,0 +1,380 @@
+/* -*- 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/UniquePtr.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();
+
+ FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() override;
+
+ /**
+ * Returns the ImageBridgeChild's thread.
+ *
+ * Can be called from any thread.
+ */
+ nsISerialEventTarget* GetThread() const override;
+
+ base::ProcessId GetParentPid() const override { return OtherPid(); }
+
+ void SyncWithCompositor(
+ const Maybe<uint64_t>& aWindowID = Nothing()) override;
+
+ PTextureChild* AllocPTextureChild(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+
+ bool DeallocPTextureChild(PTextureChild* actor);
+
+ PMediaSystemResourceManagerChild* AllocPMediaSystemResourceManagerChild();
+ bool DeallocPMediaSystemResourceManagerChild(
+ PMediaSystemResourceManagerChild* aActor);
+
+ mozilla::ipc::IPCResult RecvParentAsyncMessages(
+ nsTArray<AsyncParentMessageData>&& aMessages);
+
+ mozilla::ipc::IPCResult RecvDidComposite(
+ nsTArray<ImageCompositeNotification>&& aNotifications);
+
+ mozilla::ipc::IPCResult RecvReportFramesDropped(
+ const CompositableHandle& aHandle, const uint32_t& aFrames);
+
+ // Create an ImageClient from any thread.
+ RefPtr<ImageClient> CreateImageClient(CompositableType aType,
+ ImageContainer* aImageContainer);
+
+ // Create an ImageClient from the ImageBridge thread.
+ RefPtr<ImageClient> CreateImageClientNow(CompositableType aType,
+ ImageContainer* aImageContainer);
+
+ void UpdateImageClient(RefPtr<ImageContainer> aContainer);
+
+ void UpdateCompositable(const RefPtr<ImageContainer> aContainer,
+ const RemoteTextureId aTextureId,
+ const RemoteTextureOwnerId aOwnerId,
+ const gfx::IntSize aSize, const TextureFlags aFlags,
+ const RefPtr<FwdTransactionTracker> aTracker);
+
+ /**
+ * Flush all Images sent to CompositableHost.
+ */
+ void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
+
+ bool IPCOpen() const override { return mCanSend; }
+
+ private:
+ /**
+ * This must be called by the static function DeleteImageBridgeSync defined
+ * in ImageBridgeChild.cpp ONLY.
+ */
+ virtual ~ImageBridgeChild();
+
+ // Helpers for dispatching.
+ void CreateImageClientSync(SynchronousTask* aTask,
+ RefPtr<ImageClient>* result,
+ CompositableType aType,
+ ImageContainer* aImageContainer);
+
+ void FlushAllImagesSync(SynchronousTask* aTask, ImageClient* aClient,
+ ImageContainer* aContainer);
+
+ void ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
+ mozilla::ipc::Shmem* aShmem, bool aUnsafe,
+ bool* aSuccess);
+ void ProxyDeallocShmemNow(SynchronousTask* aTask, mozilla::ipc::Shmem* aShmem,
+ bool* aResult);
+
+ void UpdateTextureFactoryIdentifier(
+ const TextureFactoryIdentifier& aIdentifier);
+
+ public:
+ // CompositableForwarder
+
+ void Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) override;
+
+ bool UsesImageBridge() const override { return true; }
+
+ /**
+ * See CompositableForwarder::UseTextures
+ */
+ void UseTextures(CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) override;
+
+ void UseRemoteTexture(CompositableClient* aCompositable,
+ const RemoteTextureId aTextureId,
+ const RemoteTextureOwnerId aOwnerId,
+ const gfx::IntSize aSize, const TextureFlags aFlags,
+ const RefPtr<FwdTransactionTracker>& aTracker) override;
+
+ void ReleaseCompositable(const CompositableHandle& aHandle) override;
+
+ void ForgetImageContainer(const CompositableHandle& aHandle);
+
+ /**
+ * Hold TextureClient ref until end of usage on host side if
+ * TextureFlags::RECYCLE is set. Host side's usage is checked via
+ * CompositableRef.
+ */
+ void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient);
+
+ /**
+ * Notify id of Texture When host side end its use. Transaction id is used to
+ * make sure if there is no newer usage.
+ */
+ void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
+
+ void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override;
+
+ bool DestroyInTransaction(PTextureChild* aTexture) override;
+ bool DestroyInTransaction(const CompositableHandle& aHandle);
+
+ void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+ TextureClient* aTexture) override;
+
+ // ISurfaceAllocator
+
+ /**
+ * See ISurfaceAllocator.h
+ * Can be used from any thread.
+ * If used outside the ImageBridgeChild thread, it will proxy a synchronous
+ * call on the ImageBridgeChild thread.
+ */
+ bool AllocUnsafeShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+ bool AllocShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+
+ /**
+ * See ISurfaceAllocator.h
+ * Can be used from any thread.
+ * If used outside the ImageBridgeChild thread, it will proxy a synchronous
+ * call on the ImageBridgeChild thread.
+ */
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ PTextureChild* CreateTexture(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ const dom::ContentParentId& aContentId, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId) override;
+
+ bool IsSameProcess() const override;
+
+ FwdTransactionCounter& GetFwdTransactionCounter() override {
+ return mFwdTransactionCounter;
+ }
+
+ bool InForwarderThread() override { return InImageBridgeChildThread(); }
+
+ void HandleFatalError(const char* aMsg) override;
+
+ wr::MaybeExternalImageId GetNextExternalImageId() override;
+
+ protected:
+ explicit ImageBridgeChild(uint32_t aNamespace);
+ bool DispatchAllocShmemInternal(size_t aSize, Shmem* aShmem, bool aUnsafe);
+
+ void Bind(Endpoint<PImageBridgeChild>&& aEndpoint);
+ void BindSameProcess(RefPtr<ImageBridgeParent> aParent);
+
+ void SendImageBridgeThreadId();
+
+ void WillShutdown();
+ void ShutdownStep1(SynchronousTask* aTask);
+ void ShutdownStep2(SynchronousTask* aTask);
+ void MarkShutDown();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ bool CanSend() const;
+ bool CanPostTask() const;
+
+ static void ShutdownSingleton();
+
+ private:
+ uint32_t mNamespace;
+
+ CompositableTransaction* mTxn;
+ UniquePtr<FixedSizeSmallShmemSectionAllocator> mSectionAllocator;
+
+ mozilla::Atomic<bool> mCanSend;
+ mozilla::Atomic<bool> mDestroyed;
+
+ /**
+ * Transaction id of CompositableForwarder.
+ * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction()
+ * call.
+ */
+ FwdTransactionCounter mFwdTransactionCounter;
+
+ /**
+ * Hold TextureClients refs until end of their usages on host side.
+ * It defer calling of TextureClient recycle callback.
+ */
+ std::unordered_map<uint64_t, RefPtr<TextureClient>>
+ mTexturesWaitingNotifyNotUsed;
+
+ /**
+ * Mapping from async compositable IDs to image containers.
+ */
+ Mutex mContainerMapLock MOZ_UNANNOTATED;
+ std::unordered_map<uint64_t, RefPtr<ImageContainerListener>>
+ mImageContainerListeners;
+ RefPtr<ImageContainerListener> FindListener(
+ const CompositableHandle& aHandle);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp
new file mode 100644
index 0000000000..0f2bbee4f4
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -0,0 +1,440 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageBridgeParent.h"
+#include <stdint.h> // for uint64_t, uint32_t
+#include "CompositableHost.h" // for CompositableParent, Create
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, DeleteTask, etc
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/Hal.h" // for hal::SetCurrentThreadPriority()
+#include "mozilla/HalTypes.h" // for hal::THREAD_PRIORITY_COMPOSITOR
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
+#include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/CompositableTransactionParent.h"
+#include "mozilla/layers/LayersMessages.h" // for EditReply
+#include "mozilla/layers/PImageBridgeParent.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/RemoteTextureMap.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/Unused.h"
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for ImageBridgeParent::Release, etc
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "nsTArrayForwardDeclare.h" // for nsTArray
+#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
+#include "mozilla/layers/TextureHost.h"
+#include "nsThreadUtils.h"
+
+#if defined(XP_WIN)
+# include "mozilla/layers/TextureD3D11.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+using namespace mozilla::media;
+
+ImageBridgeParent::ImageBridgeMap ImageBridgeParent::sImageBridges;
+
+StaticAutoPtr<mozilla::Monitor> sImageBridgesLock;
+
+static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton;
+
+/* static */
+void ImageBridgeParent::Setup() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sImageBridgesLock) {
+ sImageBridgesLock = new Monitor("ImageBridges");
+ mozilla::ClearOnShutdown(&sImageBridgesLock);
+ }
+}
+
+ImageBridgeParent::ImageBridgeParent(nsISerialEventTarget* aThread,
+ ProcessId aChildProcessId,
+ dom::ContentParentId aContentId)
+ : mThread(aThread),
+ mContentId(aContentId),
+ mClosed(false),
+ mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ SetOtherProcessId(aChildProcessId);
+ mRemoteTextureTxnScheduler = RemoteTextureTxnScheduler::Create(this);
+}
+
+ImageBridgeParent::~ImageBridgeParent() = default;
+
+/* static */
+ImageBridgeParent* ImageBridgeParent::CreateSameProcess() {
+ base::ProcessId pid = base::GetCurrentProcId();
+ RefPtr<ImageBridgeParent> parent =
+ new ImageBridgeParent(CompositorThread(), pid, dom::ContentParentId());
+
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ MOZ_RELEASE_ASSERT(sImageBridges.count(pid) == 0);
+ sImageBridges[pid] = parent;
+ }
+
+ sImageBridgeParentSingleton = parent;
+ return parent;
+}
+
+/* static */
+bool ImageBridgeParent::CreateForGPUProcess(
+ Endpoint<PImageBridgeParent>&& aEndpoint) {
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
+
+ nsCOMPtr<nsISerialEventTarget> compositorThread = CompositorThread();
+ if (!compositorThread) {
+ return false;
+ }
+
+ RefPtr<ImageBridgeParent> parent = new ImageBridgeParent(
+ compositorThread, aEndpoint.OtherPid(), dom::ContentParentId());
+
+ compositorThread->Dispatch(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>(
+ "layers::ImageBridgeParent::Bind", parent, &ImageBridgeParent::Bind,
+ std::move(aEndpoint)));
+
+ sImageBridgeParentSingleton = parent;
+ return true;
+}
+
+/* static */
+void ImageBridgeParent::ShutdownInternal() {
+ // We make a copy because we don't want to hold the lock while closing and we
+ // don't want the object to get freed underneath us.
+ nsTArray<RefPtr<ImageBridgeParent>> actors;
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ for (const auto& iter : sImageBridges) {
+ actors.AppendElement(iter.second);
+ }
+ }
+
+ for (auto const& actor : actors) {
+ MOZ_RELEASE_ASSERT(!actor->mClosed);
+ actor->Close();
+ }
+
+ sImageBridgeParentSingleton = nullptr;
+}
+
+/* static */
+void ImageBridgeParent::Shutdown() {
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "ImageBridgeParent::Shutdown",
+ []() -> void { ImageBridgeParent::ShutdownInternal(); }));
+}
+
+void ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+ // Can't alloc/dealloc shmems from now on.
+ mClosed = true;
+
+ if (mRemoteTextureTxnScheduler) {
+ mRemoteTextureTxnScheduler = nullptr;
+ }
+ for (const auto& entry : mCompositables) {
+ entry.second->OnReleased();
+ }
+ mCompositables.clear();
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ sImageBridges.erase(OtherPid());
+ }
+ GetThread()->Dispatch(
+ NewRunnableMethod("layers::ImageBridgeParent::DeferredDestroy", this,
+ &ImageBridgeParent::DeferredDestroy));
+
+ // It is very important that this method gets called at shutdown (be it a
+ // clean or an abnormal shutdown), because DeferredDestroy is what clears
+ // mCompositorThreadHolder. If mCompositorThreadHolder is not null and
+ // ActorDestroy is not called, the ImageBridgeParent is leaked which causes
+ // the CompositorThreadHolder to be leaked and CompsoitorParent's shutdown
+ // ends up spinning the event loop forever, waiting for the compositor thread
+ // to terminate.
+}
+
+class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender final {
+ public:
+ explicit AutoImageBridgeParentAsyncMessageSender(
+ ImageBridgeParent* aImageBridge,
+ nsTArray<OpDestroy>* aToDestroy = nullptr)
+ : mImageBridge(aImageBridge), mToDestroy(aToDestroy) {
+ mImageBridge->SetAboutToSendAsyncMessages();
+ }
+
+ ~AutoImageBridgeParentAsyncMessageSender() {
+ mImageBridge->SendPendingAsyncMessages();
+ if (mToDestroy) {
+ for (const auto& op : *mToDestroy) {
+ mImageBridge->DestroyActor(op);
+ }
+ }
+ }
+
+ private:
+ ImageBridgeParent* mImageBridge;
+ nsTArray<OpDestroy>* mToDestroy;
+};
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvUpdate(
+ EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId) {
+ AUTO_PROFILER_TRACING_MARKER("Paint", "ImageBridgeTransaction", GRAPHICS);
+ AUTO_PROFILER_LABEL("ImageBridgeParent::RecvUpdate", GRAPHICS);
+
+ // This ensures that destroy operations are always processed. It is not safe
+ // to early-return from RecvUpdate without doing so.
+ AutoImageBridgeParentAsyncMessageSender autoAsyncMessageSender(this,
+ &aToDestroy);
+ UpdateFwdTransactionId(aFwdTransactionId);
+
+ auto result = IPC_OK();
+
+ for (const auto& edit : aEdits) {
+ RefPtr<CompositableHost> compositable =
+ FindCompositable(edit.compositable());
+ if (!compositable ||
+ !ReceiveCompositableUpdate(edit.detail(), WrapNotNull(compositable),
+ edit.compositable())) {
+ result = IPC_FAIL_NO_REASON(this);
+ break;
+ }
+ uint32_t dropped = compositable->GetDroppedFrames();
+ if (dropped) {
+ Unused << SendReportFramesDropped(edit.compositable(), dropped);
+ }
+ }
+
+ if (mRemoteTextureTxnScheduler) {
+ mRemoteTextureTxnScheduler->NotifyTxn(aFwdTransactionId);
+ }
+
+ return result;
+}
+
+/* static */
+bool ImageBridgeParent::CreateForContent(
+ Endpoint<PImageBridgeParent>&& aEndpoint, dom::ContentParentId aContentId) {
+ nsCOMPtr<nsISerialEventTarget> compositorThread = CompositorThread();
+ if (!compositorThread) {
+ return false;
+ }
+
+ RefPtr<ImageBridgeParent> bridge =
+ new ImageBridgeParent(compositorThread, aEndpoint.OtherPid(), aContentId);
+ compositorThread->Dispatch(NewRunnableMethod<Endpoint<PImageBridgeParent>&&>(
+ "layers::ImageBridgeParent::Bind", bridge, &ImageBridgeParent::Bind,
+ std::move(aEndpoint)));
+
+ return true;
+}
+
+void ImageBridgeParent::Bind(Endpoint<PImageBridgeParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) return;
+
+ // If the child process ID was reused by the OS before the ImageBridgeParent
+ // object was destroyed, we need to clean it up first.
+ RefPtr<ImageBridgeParent> oldActor;
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ ImageBridgeMap::const_iterator i = sImageBridges.find(OtherPid());
+ if (i != sImageBridges.end()) {
+ oldActor = i->second;
+ }
+ }
+
+ // We can't hold the lock during Close because it erases itself from the map.
+ if (oldActor) {
+ MOZ_RELEASE_ASSERT(!oldActor->mClosed);
+ oldActor->Close();
+ }
+
+ {
+ MonitorAutoLock lock(*sImageBridgesLock);
+ sImageBridges[OtherPid()] = this;
+ }
+}
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvWillClose() {
+ // If there is any texture still alive we have to force it to deallocate the
+ // device data (GL textures, etc.) now because shortly after SenStop() returns
+ // on the child side the widget will be destroyed along with it's associated
+ // GL context.
+ nsTArray<PTextureParent*> textures;
+ ManagedPTextureParent(textures);
+ for (unsigned int i = 0; i < textures.Length(); ++i) {
+ RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
+ tex->DeallocateDeviceData();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvNewCompositable(
+ const CompositableHandle& aHandle, const TextureInfo& aInfo) {
+ RefPtr<CompositableHost> host = AddCompositable(aHandle, aInfo);
+ if (!host) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ host->SetAsyncRef(AsyncCompositableRef(OtherPid(), aHandle));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ImageBridgeParent::RecvReleaseCompositable(
+ const CompositableHandle& aHandle) {
+ ReleaseCompositable(aHandle);
+ return IPC_OK();
+}
+
+PTextureParent* ImageBridgeParent::AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const uint64_t& aSerial, const wr::MaybeExternalImageId& aExternalImageId) {
+ return TextureHost::CreateIPDLActor(this, aSharedData, std::move(aReadLock),
+ aLayersBackend, aFlags, mContentId,
+ aSerial, aExternalImageId);
+}
+
+bool ImageBridgeParent::DeallocPTextureParent(PTextureParent* actor) {
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+PMediaSystemResourceManagerParent*
+ImageBridgeParent::AllocPMediaSystemResourceManagerParent() {
+ return new mozilla::media::MediaSystemResourceManagerParent();
+}
+
+bool ImageBridgeParent::DeallocPMediaSystemResourceManagerParent(
+ PMediaSystemResourceManagerParent* aActor) {
+ MOZ_ASSERT(aActor);
+ delete static_cast<mozilla::media::MediaSystemResourceManagerParent*>(aActor);
+ return true;
+}
+
+void ImageBridgeParent::SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) {
+ mozilla::Unused << SendParentAsyncMessages(aMessage);
+}
+
+class ProcessIdComparator {
+ public:
+ bool Equals(const ImageCompositeNotificationInfo& aA,
+ const ImageCompositeNotificationInfo& aB) const {
+ return aA.mImageBridgeProcessId == aB.mImageBridgeProcessId;
+ }
+ bool LessThan(const ImageCompositeNotificationInfo& aA,
+ const ImageCompositeNotificationInfo& aB) const {
+ return aA.mImageBridgeProcessId < aB.mImageBridgeProcessId;
+ }
+};
+
+/* static */
+bool ImageBridgeParent::NotifyImageComposites(
+ nsTArray<ImageCompositeNotificationInfo>& aNotifications) {
+ // Group the notifications by destination process ID and then send the
+ // notifications in one message per group.
+ aNotifications.Sort(ProcessIdComparator());
+ uint32_t i = 0;
+ bool ok = true;
+ while (i < aNotifications.Length()) {
+ AutoTArray<ImageCompositeNotification, 1> notifications;
+ notifications.AppendElement(aNotifications[i].mNotification);
+ uint32_t end = i + 1;
+ MOZ_ASSERT(aNotifications[i].mNotification.compositable());
+ ProcessId pid = aNotifications[i].mImageBridgeProcessId;
+ while (end < aNotifications.Length() &&
+ aNotifications[end].mImageBridgeProcessId == pid) {
+ notifications.AppendElement(aNotifications[end].mNotification);
+ ++end;
+ }
+ RefPtr<ImageBridgeParent> bridge = GetInstance(pid);
+ if (!bridge || bridge->mClosed) {
+ i = end;
+ continue;
+ }
+ bridge->SendPendingAsyncMessages();
+ if (!bridge->SendDidComposite(notifications)) {
+ ok = false;
+ }
+ i = end;
+ }
+ return ok;
+}
+
+void ImageBridgeParent::DeferredDestroy() { mCompositorThreadHolder = nullptr; }
+
+already_AddRefed<ImageBridgeParent> ImageBridgeParent::GetInstance(
+ ProcessId aId) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MonitorAutoLock lock(*sImageBridgesLock);
+ ImageBridgeMap::const_iterator i = sImageBridges.find(aId);
+ if (i == sImageBridges.end()) {
+ NS_WARNING("Cannot find image bridge for process!");
+ return nullptr;
+ }
+ RefPtr<ImageBridgeParent> bridge = i->second;
+ return bridge.forget();
+}
+
+bool ImageBridgeParent::AllocShmem(size_t aSize, ipc::Shmem* aShmem) {
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::AllocShmem(aSize, aShmem);
+}
+
+bool ImageBridgeParent::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::AllocUnsafeShmem(aSize, aShmem);
+}
+
+bool ImageBridgeParent::DeallocShmem(ipc::Shmem& aShmem) {
+ if (mClosed) {
+ return false;
+ }
+ return PImageBridgeParent::DeallocShmem(aShmem);
+}
+
+bool ImageBridgeParent::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void ImageBridgeParent::NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) {
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
+ if (!texture) {
+ return;
+ }
+
+ if (!(texture->GetFlags() & TextureFlags::RECYCLE) &&
+ !(texture->GetFlags() & TextureFlags::WAIT_HOST_USAGE_END)) {
+ return;
+ }
+
+ uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
+ mPendingAsyncMessage.push_back(OpNotifyNotUsed(textureId, aTransactionId));
+
+ if (!IsAboutToSendAsyncMessages()) {
+ SendPendingAsyncMessages();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/ImageBridgeParent.h b/gfx/layers/ipc/ImageBridgeParent.h
new file mode 100644
index 0000000000..0267be3eaa
--- /dev/null
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gfx_layers_ipc_ImageBridgeParent_h_
+#define gfx_layers_ipc_ImageBridgeParent_h_
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint64_t
+#include "CompositableTransactionParent.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/PImageBridgeParent.h"
+#include "nsISupportsImpl.h"
+#include "nsTArrayForwardDeclare.h" // for nsTArray
+
+namespace mozilla {
+namespace ipc {
+class Shmem;
+} // namespace ipc
+
+namespace layers {
+
+struct ImageCompositeNotificationInfo;
+class RemoteTextureTxnScheduler;
+
+/**
+ * ImageBridgeParent is the manager Protocol of async Compositables.
+ */
+class ImageBridgeParent final : public PImageBridgeParent,
+ public CompositableParentManager,
+ public mozilla::ipc::IShmemAllocator {
+ public:
+ typedef nsTArray<CompositableOperation> EditArray;
+ typedef nsTArray<OpDestroy> OpDestroyArray;
+
+ protected:
+ ImageBridgeParent(nsISerialEventTarget* aThread, ProcessId aChildProcessId,
+ dom::ContentParentId aContentId);
+
+ public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override {
+ return ISurfaceAllocator::AddRef();
+ }
+ NS_IMETHOD_(MozExternalRefCountType) Release() override {
+ return ISurfaceAllocator::Release();
+ }
+
+ /**
+ * Creates the globals of ImageBridgeParent.
+ */
+ static void Setup();
+
+ static ImageBridgeParent* CreateSameProcess();
+ static bool CreateForGPUProcess(Endpoint<PImageBridgeParent>&& aEndpoint);
+ static bool CreateForContent(Endpoint<PImageBridgeParent>&& aEndpoint,
+ dom::ContentParentId aContentId);
+ static void Shutdown();
+
+ IShmemAllocator* AsShmemAllocator() override { return this; }
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // CompositableParentManager
+ void SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) override;
+
+ void NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) override;
+
+ base::ProcessId GetChildProcessId() override { return OtherPid(); }
+ dom::ContentParentId GetContentId() override { return mContentId; }
+
+ // PImageBridge
+ mozilla::ipc::IPCResult RecvUpdate(EditArray&& aEdits,
+ OpDestroyArray&& aToDestroy,
+ const uint64_t& aFwdTransactionId);
+
+ PTextureParent* AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const uint64_t& aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+ bool DeallocPTextureParent(PTextureParent* actor);
+
+ mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
+ const TextureInfo& aInfo);
+ mozilla::ipc::IPCResult RecvReleaseCompositable(
+ const CompositableHandle& aHandle);
+
+ PMediaSystemResourceManagerParent* AllocPMediaSystemResourceManagerParent();
+ bool DeallocPMediaSystemResourceManagerParent(
+ PMediaSystemResourceManagerParent* aActor);
+
+ // Shutdown step 1
+ mozilla::ipc::IPCResult RecvWillClose();
+
+ nsISerialEventTarget* GetThread() const { return mThread; }
+
+ // IShmemAllocator
+
+ bool AllocShmem(size_t aSize, ipc::Shmem* aShmem) override;
+
+ bool AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) override;
+
+ bool DeallocShmem(ipc::Shmem& aShmem) override;
+
+ bool IsSameProcess() const override;
+
+ static already_AddRefed<ImageBridgeParent> GetInstance(ProcessId aId);
+
+ static bool NotifyImageComposites(
+ nsTArray<ImageCompositeNotificationInfo>& aNotifications);
+
+ bool UsesImageBridge() const override { return true; }
+
+ bool IPCOpen() const override { return !mClosed; }
+
+ protected:
+ void Bind(Endpoint<PImageBridgeParent>&& aEndpoint);
+
+ private:
+ virtual ~ImageBridgeParent();
+
+ static void ShutdownInternal();
+
+ void DeferredDestroy();
+ nsCOMPtr<nsISerialEventTarget> mThread;
+
+ dom::ContentParentId mContentId;
+
+ bool mClosed;
+
+ /**
+ * Map of all living ImageBridgeParent instances
+ */
+ typedef std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeMap;
+ static ImageBridgeMap sImageBridges;
+
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
+
+ RefPtr<RemoteTextureTxnScheduler> mRemoteTextureTxnScheduler;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // gfx_layers_ipc_ImageBridgeParent_h_
diff --git a/gfx/layers/ipc/KnowsCompositor.cpp b/gfx/layers/ipc/KnowsCompositor.cpp
new file mode 100644
index 0000000000..8099c4de88
--- /dev/null
+++ b/gfx/layers/ipc/KnowsCompositor.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "KnowsCompositor.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+namespace mozilla::layers {
+
+void KnowsCompositor::IdentifyTextureHost(
+ const TextureFactoryIdentifier& aIdentifier) {
+ auto lock = mData.Lock();
+ lock.ref().mTextureFactoryIdentifier = aIdentifier;
+
+ lock.ref().mSyncObject =
+ SyncObjectClient::CreateSyncObjectClientForContentDevice(
+ aIdentifier.mSyncHandle);
+}
+
+KnowsCompositor::KnowsCompositor()
+ : mData("KnowsCompositorMutex"), mSerial(++sSerialCounter) {}
+
+KnowsCompositor::~KnowsCompositor() = default;
+
+KnowsCompositorMediaProxy::KnowsCompositorMediaProxy(
+ const TextureFactoryIdentifier& aIdentifier) {
+ auto lock = mData.Lock();
+ lock.ref().mTextureFactoryIdentifier = aIdentifier;
+ // overwrite mSerial's value set by the parent class because we use the same
+ // serial as the KnowsCompositor we are proxying.
+ mThreadSafeAllocator = ImageBridgeChild::GetSingleton();
+ lock.ref().mSyncObject = mThreadSafeAllocator->GetSyncObject();
+}
+
+KnowsCompositorMediaProxy::~KnowsCompositorMediaProxy() = default;
+
+TextureForwarder* KnowsCompositorMediaProxy::GetTextureForwarder() {
+ return mThreadSafeAllocator->GetTextureForwarder();
+}
+
+LayersIPCActor* KnowsCompositorMediaProxy::GetLayersIPCActor() {
+ return mThreadSafeAllocator->GetLayersIPCActor();
+}
+
+void KnowsCompositorMediaProxy::SyncWithCompositor(
+ const Maybe<uint64_t>& aWindowID) {
+ mThreadSafeAllocator->SyncWithCompositor(aWindowID);
+}
+
+bool IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface) {
+ return aSurface.type() != SurfaceDescriptor::T__None &&
+ aSurface.type() != SurfaceDescriptor::Tnull_t;
+}
+
+uint8_t* GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor) {
+ MOZ_ASSERT(IsSurfaceDescriptorValid(aDescriptor));
+ MOZ_RELEASE_ASSERT(
+ aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer,
+ "GFX: surface descriptor is not the right type.");
+
+ auto memOrShmem = aDescriptor.get_SurfaceDescriptorBuffer().data();
+ if (memOrShmem.type() == MemoryOrShmem::TShmem) {
+ return memOrShmem.get_Shmem().get<uint8_t>();
+ } else {
+ return reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
+ }
+}
+
+already_AddRefed<gfx::DataSourceSurface> GetSurfaceForDescriptor(
+ const SurfaceDescriptor& aDescriptor) {
+ if (aDescriptor.type() != SurfaceDescriptor::TSurfaceDescriptorBuffer) {
+ return nullptr;
+ }
+ uint8_t* data = GetAddressFromDescriptor(aDescriptor);
+ auto rgb =
+ aDescriptor.get_SurfaceDescriptorBuffer().desc().get_RGBDescriptor();
+ uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
+ return gfx::Factory::CreateWrappingDataSourceSurface(data, stride, rgb.size(),
+ rgb.format());
+}
+
+void DestroySurfaceDescriptor(ipc::IShmemAllocator* aAllocator,
+ SurfaceDescriptor* aSurface) {
+ MOZ_ASSERT(aSurface);
+
+ SurfaceDescriptorBuffer& desc = aSurface->get_SurfaceDescriptorBuffer();
+ switch (desc.data().type()) {
+ case MemoryOrShmem::TShmem: {
+ aAllocator->DeallocShmem(desc.data().get_Shmem());
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ uint8_t* ptr = (uint8_t*)desc.data().get_uintptr_t();
+ GfxMemoryImageReporter::WillFree(ptr);
+ delete[] ptr;
+ break;
+ }
+ default:
+ MOZ_CRASH("surface type not implemented!");
+ }
+ *aSurface = SurfaceDescriptor();
+}
+
+} // namespace mozilla::layers
diff --git a/gfx/layers/ipc/KnowsCompositor.h b/gfx/layers/ipc/KnowsCompositor.h
new file mode 100644
index 0000000000..2f79a3c171
--- /dev/null
+++ b/gfx/layers/ipc/KnowsCompositor.h
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_KNOWSCOMPOSITOR
+#define MOZILLA_LAYERS_KNOWSCOMPOSITOR
+
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/layers/SyncObject.h"
+
+namespace mozilla::layers {
+
+class TextureForwarder;
+class LayersIPCActor;
+class ImageBridgeChild;
+
+/**
+ * An abstract interface for classes that are tied to a specific Compositor
+ * across IPDL and uses TextureFactoryIdentifier to describe this Compositor.
+ */
+class KnowsCompositor {
+ public:
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ KnowsCompositor();
+ virtual ~KnowsCompositor();
+
+ void IdentifyTextureHost(const TextureFactoryIdentifier& aIdentifier);
+
+ // The sync object for the global content device.
+ RefPtr<SyncObjectClient> GetSyncObject() {
+ auto lock = mData.Lock();
+ if (lock.ref().mSyncObject) {
+ lock.ref().mSyncObject->EnsureInitialized();
+ }
+ return lock.ref().mSyncObject;
+ }
+
+ /// And by "thread-safe" here we merely mean "okay to hold strong references
+ /// to from multiple threads". Not all methods actually are thread-safe.
+ virtual bool IsThreadSafe() const { return true; }
+
+ virtual RefPtr<KnowsCompositor> GetForMedia() {
+ return RefPtr<KnowsCompositor>(this);
+ }
+
+ int32_t GetMaxTextureSize() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mMaxTextureSize;
+ }
+
+ /**
+ * Returns the type of backend that is used off the main thread.
+ * We only don't allow changing the backend type at runtime so this value can
+ * be queried once and will not change until Gecko is restarted.
+ */
+ LayersBackend GetCompositorBackendType() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mParentBackend;
+ }
+
+ WebRenderCompositor GetWebRenderCompositorType() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor;
+ }
+
+ bool SupportsTextureBlitting() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mSupportsTextureBlitting;
+ }
+
+ bool SupportsPartialUploads() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mSupportsPartialUploads;
+ }
+
+ bool SupportsComponentAlpha() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mSupportsComponentAlpha;
+ }
+
+ bool SupportsD3D11NV12() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mSupportsD3D11NV12;
+ }
+
+ bool SupportsD3D11() const {
+ auto lock = mData.Lock();
+ return SupportsD3D11(lock.ref().mTextureFactoryIdentifier);
+ }
+
+ static bool SupportsD3D11(
+ const TextureFactoryIdentifier aTextureFactoryIdentifier) {
+ return aTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_WR &&
+ (aTextureFactoryIdentifier.mCompositorUseANGLE ||
+ aTextureFactoryIdentifier.mWebRenderCompositor ==
+ layers::WebRenderCompositor::D3D11);
+ }
+
+ bool GetCompositorUseANGLE() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mCompositorUseANGLE;
+ }
+
+ bool GetCompositorUseDComp() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mCompositorUseDComp;
+ }
+
+ bool GetUseCompositorWnd() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mUseCompositorWnd;
+ }
+
+ WebRenderBackend GetWebRenderBackend() const {
+ auto lock = mData.Lock();
+ MOZ_ASSERT(lock.ref().mTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_WR);
+ return lock.ref().mTextureFactoryIdentifier.mWebRenderBackend;
+ }
+
+ bool UsingHardwareWebRender() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_WR &&
+ lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
+ WebRenderBackend::HARDWARE;
+ }
+
+ bool UsingSoftwareWebRender() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_WR &&
+ lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
+ WebRenderBackend::SOFTWARE;
+ }
+
+ bool UsingSoftwareWebRenderD3D11() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_WR &&
+ lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
+ WebRenderBackend::SOFTWARE &&
+ lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor ==
+ layers::WebRenderCompositor::D3D11;
+ }
+
+ bool UsingSoftwareWebRenderOpenGL() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier.mParentBackend ==
+ layers::LayersBackend::LAYERS_WR &&
+ lock.ref().mTextureFactoryIdentifier.mWebRenderBackend ==
+ WebRenderBackend::SOFTWARE &&
+ lock.ref().mTextureFactoryIdentifier.mWebRenderCompositor ==
+ layers::WebRenderCompositor::OPENGL;
+ }
+
+ TextureFactoryIdentifier GetTextureFactoryIdentifier() const {
+ auto lock = mData.Lock();
+ return lock.ref().mTextureFactoryIdentifier;
+ }
+
+ int32_t GetSerial() const { return mSerial; }
+
+ /**
+ * Sends a synchronous ping to the compsoitor.
+ *
+ * This is bad for performance and should only be called as a last resort if
+ * the compositor may be blocked for a long period of time, to avoid that the
+ * content process accumulates resource allocations that the compositor is not
+ * consuming and releasing.
+ */
+ virtual void SyncWithCompositor(
+ const Maybe<uint64_t>& aWindowID = Nothing()) {
+ MOZ_ASSERT_UNREACHABLE("Unimplemented");
+ }
+
+ /**
+ * Helpers for finding other related interface. These are infallible.
+ */
+ virtual TextureForwarder* GetTextureForwarder() = 0;
+ virtual LayersIPCActor* GetLayersIPCActor() = 0;
+
+ 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;
+
+ void SyncWithCompositor(
+ const Maybe<uint64_t>& aWindowID = Nothing()) override;
+
+ protected:
+ virtual ~KnowsCompositorMediaProxy();
+
+ RefPtr<ImageBridgeChild> mThreadSafeAllocator;
+};
+
+} // namespace mozilla::layers
+
+#endif
diff --git a/gfx/layers/ipc/LayerTreeOwnerTracker.cpp b/gfx/layers/ipc/LayerTreeOwnerTracker.cpp
new file mode 100644
index 0000000000..ab3551ad29
--- /dev/null
+++ b/gfx/layers/ipc/LayerTreeOwnerTracker.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LayerTreeOwnerTracker.h"
+
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/gfx/GPUChild.h" // for GPUChild
+#include "mozilla/gfx/GPUProcessManager.h" // for GPUProcessManager
+
+#include <functional>
+#include <utility> // for std::make_pair
+
+namespace mozilla {
+namespace layers {
+
+static StaticAutoPtr<LayerTreeOwnerTracker> sSingleton;
+
+LayerTreeOwnerTracker::LayerTreeOwnerTracker()
+ : mLayerIdsLock("LayerTreeOwnerTrackerLock") {}
+
+void LayerTreeOwnerTracker::Initialize() {
+ MOZ_ASSERT(!sSingleton);
+ sSingleton = new LayerTreeOwnerTracker();
+}
+
+void LayerTreeOwnerTracker::Shutdown() { sSingleton = nullptr; }
+
+LayerTreeOwnerTracker* LayerTreeOwnerTracker::Get() { return sSingleton; }
+
+void LayerTreeOwnerTracker::Map(LayersId aLayersId,
+ base::ProcessId aProcessId) {
+ MutexAutoLock lock(mLayerIdsLock);
+
+ // Add the mapping to the list
+ mLayerIds[aLayersId] = aProcessId;
+}
+
+void LayerTreeOwnerTracker::Unmap(LayersId aLayersId,
+ base::ProcessId aProcessId) {
+ MutexAutoLock lock(mLayerIdsLock);
+
+ MOZ_ASSERT(mLayerIds[aLayersId] == aProcessId);
+ mLayerIds.erase(aLayersId);
+}
+
+bool LayerTreeOwnerTracker::IsMapped(LayersId aLayersId,
+ base::ProcessId aProcessId) {
+ MutexAutoLock lock(mLayerIdsLock);
+
+ auto iter = mLayerIds.find(aLayersId);
+ return iter != mLayerIds.end() && iter->second == aProcessId;
+}
+
+void LayerTreeOwnerTracker::Iterate(
+ const std::function<void(LayersId aLayersId, base::ProcessId aProcessId)>&
+ aCallback) {
+ MutexAutoLock lock(mLayerIdsLock);
+
+ for (const auto& iter : mLayerIds) {
+ aCallback(iter.first, iter.second);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/LayerTreeOwnerTracker.h b/gfx/layers/ipc/LayerTreeOwnerTracker.h
new file mode 100644
index 0000000000..651c695309
--- /dev/null
+++ b/gfx/layers/ipc/LayerTreeOwnerTracker.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_LayerTreeOwnerTracker_h
+#define mozilla_layers_LayerTreeOwnerTracker_h
+
+#include "base/process.h" // for base::ProcessId
+#include "LayersTypes.h" // for LayersId
+#include "mozilla/Mutex.h" // for mozilla::Mutex
+
+#include <functional>
+#include <map>
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+}
+
+namespace layers {
+
+/**
+ * A utility class for tracking which content processes should be allowed
+ * to access which layer trees.
+ *
+ * ProcessId's are used to track which content process can access the layer
+ * tree, and in the case of nested browser's we use the top level content
+ * processes' ProcessId.
+ *
+ * This class is only available in the main process and gpu process. Mappings
+ * are synced from main process to the gpu process. The actual syncing happens
+ * in GPUProcessManager, and so this class should not be used directly.
+ */
+class LayerTreeOwnerTracker final {
+ public:
+ static void Initialize();
+ static void Shutdown();
+ static LayerTreeOwnerTracker* Get();
+
+ /**
+ * Map aLayersId and aProcessId together so that that process
+ * can access that layer tree.
+ */
+ void Map(LayersId aLayersId, base::ProcessId aProcessId);
+
+ /**
+ * Remove an existing mapping.
+ */
+ void Unmap(LayersId aLayersId, base::ProcessId aProcessId);
+
+ /**
+ * Checks whether it is okay for aProcessId to access aLayersId.
+ */
+ bool IsMapped(LayersId aLayersId, base::ProcessId aProcessId);
+
+ void Iterate(
+ const std::function<void(LayersId aLayersId, base::ProcessId aProcessId)>&
+ aCallback);
+
+ private:
+ LayerTreeOwnerTracker();
+
+ mozilla::Mutex mLayerIdsLock MOZ_UNANNOTATED;
+ std::map<LayersId, base::ProcessId> mLayerIds;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_LayerTreeOwnerTracker_h
diff --git a/gfx/layers/ipc/LayersMessageUtils.h b/gfx/layers/ipc/LayersMessageUtils.h
new file mode 100644
index 0000000000..d869d4ed83
--- /dev/null
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -0,0 +1,1180 @@
+/* -*- 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/ScrollSnapInfo.h"
+#include "mozilla/ServoBindings.h"
+#include "mozilla/ipc/ByteBuf.h"
+#include "mozilla/ipc/ProtocolMessageUtils.h"
+#include "mozilla/layers/APZInputBridge.h"
+#include "mozilla/layers/AsyncDragMetrics.h"
+#include "mozilla/layers/CompositorOptions.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/FocusTarget.h"
+#include "mozilla/layers/GeckoContentControllerTypes.h"
+#include "mozilla/layers/KeyboardMap.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/MatrixMessage.h"
+#include "mozilla/layers/OverlayInfo.h"
+#include "mozilla/layers/RepaintRequest.h"
+#include "mozilla/layers/ScrollbarData.h"
+#include "nsSize.h"
+#include "mozilla/layers/DoubleTapToZoom.h"
+
+// For ParamTraits, could be moved to cpp file
+#include "ipc/nsGUIEventIPC.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "mozilla/ipc/ByteBufUtils.h"
+
+#ifdef _MSC_VER
+# pragma warning(disable : 4800)
+#endif
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::LayersId>
+ : public PlainOldDataSerializer<mozilla::layers::LayersId> {};
+
+template <typename T>
+struct ParamTraits<mozilla::layers::BaseTransactionId<T>>
+ : public PlainOldDataSerializer<mozilla::layers::BaseTransactionId<T>> {};
+
+template <>
+struct ParamTraits<mozilla::VsyncId>
+ : public PlainOldDataSerializer<mozilla::VsyncId> {};
+
+template <>
+struct ParamTraits<mozilla::VsyncEvent> {
+ typedef mozilla::VsyncEvent paramType;
+
+ static void Write(MessageWriter* writer, const paramType& param) {
+ WriteParam(writer, param.mId);
+ WriteParam(writer, param.mTime);
+ WriteParam(writer, param.mOutputTime);
+ }
+ static bool Read(MessageReader* reader, paramType* result) {
+ return ReadParam(reader, &result->mId) &&
+ ReadParam(reader, &result->mTime) &&
+ ReadParam(reader, &result->mOutputTime);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::MatrixMessage> {
+ typedef mozilla::layers::MatrixMessage paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mMatrix);
+ WriteParam(aWriter, aParam.mTopLevelViewportVisibleRectInBrowserCoords);
+ WriteParam(aWriter, aParam.mLayersId);
+ }
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mMatrix) &&
+ ReadParam(aReader,
+ &aResult->mTopLevelViewportVisibleRectInBrowserCoords) &&
+ ReadParam(aReader, &aResult->mLayersId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::WindowKind>
+ : public ContiguousEnumSerializer<mozilla::layers::WindowKind,
+ mozilla::layers::WindowKind::MAIN,
+ mozilla::layers::WindowKind::LAST> {};
+
+template <>
+struct ParamTraits<mozilla::layers::LayersBackend>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::LayersBackend,
+ mozilla::layers::LayersBackend::LAYERS_NONE,
+ mozilla::layers::LayersBackend::LAYERS_LAST> {};
+
+template <>
+struct ParamTraits<mozilla::layers::WebRenderBackend>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::WebRenderBackend,
+ mozilla::layers::WebRenderBackend::HARDWARE,
+ mozilla::layers::WebRenderBackend::LAST> {};
+
+template <>
+struct ParamTraits<mozilla::layers::WebRenderCompositor>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::WebRenderCompositor,
+ mozilla::layers::WebRenderCompositor::DRAW,
+ mozilla::layers::WebRenderCompositor::LAST> {};
+
+template <>
+struct ParamTraits<mozilla::layers::TextureType>
+ : public ContiguousEnumSerializer<mozilla::layers::TextureType,
+ mozilla::layers::TextureType::Unknown,
+ mozilla::layers::TextureType::Last> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScaleMode>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::ScaleMode, mozilla::layers::ScaleMode::SCALE_NONE,
+ mozilla::layers::kHighestScaleMode> {};
+
+template <>
+struct ParamTraits<mozilla::StyleScrollSnapStrictness>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::StyleScrollSnapStrictness,
+ mozilla::StyleScrollSnapStrictness::None,
+ mozilla::StyleScrollSnapStrictness::Proximity> {};
+
+template <>
+struct ParamTraits<mozilla::layers::TextureFlags>
+ : public BitFlagsEnumSerializer<mozilla::layers::TextureFlags,
+ mozilla::layers::TextureFlags::ALL_BITS> {};
+
+template <>
+struct ParamTraits<mozilla::layers::DiagnosticTypes>
+ : public BitFlagsEnumSerializer<
+ mozilla::layers::DiagnosticTypes,
+ mozilla::layers::DiagnosticTypes::ALL_BITS> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollDirection>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::ScrollDirection,
+ mozilla::layers::ScrollDirection::eVertical,
+ mozilla::layers::kHighestScrollDirection> {};
+
+template <>
+struct ParamTraits<mozilla::layers::FrameMetrics::ScrollOffsetUpdateType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::FrameMetrics::ScrollOffsetUpdateType,
+ mozilla::layers::FrameMetrics::ScrollOffsetUpdateType::eNone,
+ mozilla::layers::FrameMetrics::sHighestScrollOffsetUpdateType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::RepaintRequest::ScrollOffsetUpdateType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::RepaintRequest::ScrollOffsetUpdateType,
+ mozilla::layers::RepaintRequest::ScrollOffsetUpdateType::eNone,
+ mozilla::layers::RepaintRequest::sHighestScrollOffsetUpdateType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::OverscrollBehavior>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::OverscrollBehavior,
+ mozilla::layers::OverscrollBehavior::Auto,
+ mozilla::layers::kHighestOverscrollBehavior> {};
+
+template <>
+struct ParamTraits<mozilla::layers::LayerHandle> {
+ typedef mozilla::layers::LayerHandle paramType;
+
+ static void Write(MessageWriter* writer, const paramType& param) {
+ WriteParam(writer, param.mHandle);
+ }
+ static bool Read(MessageReader* reader, paramType* result) {
+ return ReadParam(reader, &result->mHandle);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositableHandle> {
+ typedef mozilla::layers::CompositableHandle paramType;
+
+ static void Write(MessageWriter* writer, const paramType& param) {
+ WriteParam(writer, param.mHandle);
+ }
+ static bool Read(MessageReader* reader, paramType* result) {
+ return ReadParam(reader, &result->mHandle);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositableHandleOwner>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::CompositableHandleOwner,
+ mozilla::layers::CompositableHandleOwner::WebRenderBridge,
+ mozilla::layers::CompositableHandleOwner::ImageBridge> {};
+
+template <>
+struct ParamTraits<mozilla::layers::RemoteTextureId> {
+ typedef mozilla::layers::RemoteTextureId paramType;
+
+ static void Write(MessageWriter* writer, const paramType& param) {
+ WriteParam(writer, param.mId);
+ }
+ static bool Read(MessageReader* reader, paramType* result) {
+ return ReadParam(reader, &result->mId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::RemoteTextureOwnerId> {
+ typedef mozilla::layers::RemoteTextureOwnerId paramType;
+
+ static void Write(MessageWriter* writer, const paramType& param) {
+ WriteParam(writer, param.mId);
+ }
+ static bool Read(MessageReader* reader, paramType* result) {
+ return ReadParam(reader, &result->mId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::GpuProcessTextureId> {
+ typedef mozilla::layers::GpuProcessTextureId paramType;
+
+ static void Write(MessageWriter* writer, const paramType& param) {
+ WriteParam(writer, param.mId);
+ }
+ static bool Read(MessageReader* reader, paramType* result) {
+ return ReadParam(reader, &result->mId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::GpuProcessQueryId> {
+ typedef mozilla::layers::GpuProcessQueryId paramType;
+
+ static void Write(MessageWriter* writer, const paramType& param) {
+ WriteParam(writer, param.mId);
+ }
+ static bool Read(MessageReader* reader, paramType* result) {
+ return ReadParam(reader, &result->mId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::FrameMetrics>
+ : BitfieldHelper<mozilla::layers::FrameMetrics> {
+ typedef mozilla::layers::FrameMetrics paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mScrollId);
+ WriteParam(aWriter, aParam.mPresShellResolution);
+ WriteParam(aWriter, aParam.mCompositionBounds);
+ WriteParam(aWriter, aParam.mCompositionBoundsWidthIgnoringScrollbars);
+ WriteParam(aWriter, aParam.mDisplayPort);
+ WriteParam(aWriter, aParam.mScrollableRect);
+ WriteParam(aWriter, aParam.mCumulativeResolution);
+ WriteParam(aWriter, aParam.mDevPixelsPerCSSPixel);
+ WriteParam(aWriter, aParam.mScrollOffset);
+ WriteParam(aWriter, aParam.mZoom);
+ WriteParam(aWriter, aParam.mScrollGeneration);
+ WriteParam(aWriter, aParam.mBoundingCompositionSize);
+ WriteParam(aWriter, aParam.mPresShellId);
+ WriteParam(aWriter, aParam.mLayoutViewport);
+ WriteParam(aWriter, aParam.mTransformToAncestorScale);
+ WriteParam(aWriter, aParam.mPaintRequestTime);
+ WriteParam(aWriter, aParam.mVisualDestination);
+ WriteParam(aWriter, aParam.mVisualScrollUpdateType);
+ WriteParam(aWriter, aParam.mFixedLayerMargins);
+ WriteParam(aWriter, aParam.mCompositionSizeWithoutDynamicToolbar);
+ WriteParam(aWriter, aParam.mIsRootContent);
+ WriteParam(aWriter, aParam.mIsScrollInfoLayer);
+ WriteParam(aWriter, aParam.mHasNonZeroDisplayPortMargins);
+ WriteParam(aWriter, aParam.mMinimalDisplayPort);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (
+ ReadParam(aReader, &aResult->mScrollId) &&
+ ReadParam(aReader, &aResult->mPresShellResolution) &&
+ ReadParam(aReader, &aResult->mCompositionBounds) &&
+ ReadParam(aReader,
+ &aResult->mCompositionBoundsWidthIgnoringScrollbars) &&
+ ReadParam(aReader, &aResult->mDisplayPort) &&
+ ReadParam(aReader, &aResult->mScrollableRect) &&
+ ReadParam(aReader, &aResult->mCumulativeResolution) &&
+ ReadParam(aReader, &aResult->mDevPixelsPerCSSPixel) &&
+ ReadParam(aReader, &aResult->mScrollOffset) &&
+ ReadParam(aReader, &aResult->mZoom) &&
+ ReadParam(aReader, &aResult->mScrollGeneration) &&
+ ReadParam(aReader, &aResult->mBoundingCompositionSize) &&
+ ReadParam(aReader, &aResult->mPresShellId) &&
+ ReadParam(aReader, &aResult->mLayoutViewport) &&
+ ReadParam(aReader, &aResult->mTransformToAncestorScale) &&
+ ReadParam(aReader, &aResult->mPaintRequestTime) &&
+ ReadParam(aReader, &aResult->mVisualDestination) &&
+ ReadParam(aReader, &aResult->mVisualScrollUpdateType) &&
+ ReadParam(aReader, &aResult->mFixedLayerMargins) &&
+ ReadParam(aReader, &aResult->mCompositionSizeWithoutDynamicToolbar) &&
+ ReadBoolForBitfield(aReader, aResult, &paramType::SetIsRootContent) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetIsScrollInfoLayer) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetHasNonZeroDisplayPortMargins) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetMinimalDisplayPort));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::APZScrollAnimationType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::APZScrollAnimationType, mozilla::APZScrollAnimationType::No,
+ mozilla::APZScrollAnimationType::TriggeredByUserInput> {};
+
+template <>
+struct ParamTraits<mozilla::ScrollSnapTargetIds> {
+ typedef mozilla::ScrollSnapTargetIds paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mIdsOnX);
+ WriteParam(aWriter, aParam.mIdsOnY);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mIdsOnX) &&
+ ReadParam(aReader, &aResult->mIdsOnY);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::RepaintRequest>
+ : BitfieldHelper<mozilla::layers::RepaintRequest> {
+ typedef mozilla::layers::RepaintRequest paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mScrollId);
+ WriteParam(aWriter, aParam.mPresShellResolution);
+ WriteParam(aWriter, aParam.mCompositionBounds);
+ WriteParam(aWriter, aParam.mCumulativeResolution);
+ WriteParam(aWriter, aParam.mDevPixelsPerCSSPixel);
+ WriteParam(aWriter, aParam.mScrollOffset);
+ WriteParam(aWriter, aParam.mZoom);
+ WriteParam(aWriter, aParam.mScrollGeneration);
+ WriteParam(aWriter, aParam.mScrollGenerationOnApz);
+ WriteParam(aWriter, aParam.mDisplayPortMargins);
+ WriteParam(aWriter, aParam.mPresShellId);
+ WriteParam(aWriter, aParam.mLayoutViewport);
+ WriteParam(aWriter, aParam.mTransformToAncestorScale);
+ WriteParam(aWriter, aParam.mPaintRequestTime);
+ WriteParam(aWriter, aParam.mScrollUpdateType);
+ WriteParam(aWriter, aParam.mScrollAnimationType);
+ WriteParam(aWriter, aParam.mLastSnapTargetIds);
+ WriteParam(aWriter, aParam.mIsRootContent);
+ WriteParam(aWriter, aParam.mIsScrollInfoLayer);
+ WriteParam(aWriter, aParam.mIsInScrollingGesture);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (
+ ReadParam(aReader, &aResult->mScrollId) &&
+ ReadParam(aReader, &aResult->mPresShellResolution) &&
+ ReadParam(aReader, &aResult->mCompositionBounds) &&
+ ReadParam(aReader, &aResult->mCumulativeResolution) &&
+ ReadParam(aReader, &aResult->mDevPixelsPerCSSPixel) &&
+ ReadParam(aReader, &aResult->mScrollOffset) &&
+ ReadParam(aReader, &aResult->mZoom) &&
+ ReadParam(aReader, &aResult->mScrollGeneration) &&
+ ReadParam(aReader, &aResult->mScrollGenerationOnApz) &&
+ ReadParam(aReader, &aResult->mDisplayPortMargins) &&
+ ReadParam(aReader, &aResult->mPresShellId) &&
+ ReadParam(aReader, &aResult->mLayoutViewport) &&
+ ReadParam(aReader, &aResult->mTransformToAncestorScale) &&
+ ReadParam(aReader, &aResult->mPaintRequestTime) &&
+ ReadParam(aReader, &aResult->mScrollUpdateType) &&
+ ReadParam(aReader, &aResult->mScrollAnimationType) &&
+ ReadParam(aReader, &aResult->mLastSnapTargetIds) &&
+ ReadBoolForBitfield(aReader, aResult, &paramType::SetIsRootContent) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetIsScrollInfoLayer) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetIsInScrollingGesture));
+ }
+};
+
+template <>
+struct ParamTraits<nsSize> {
+ typedef nsSize paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.width);
+ WriteParam(aWriter, aParam.height);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->width) &&
+ ReadParam(aReader, &aResult->height);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::StyleScrollSnapStop>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::StyleScrollSnapStop, mozilla::StyleScrollSnapStop::Normal,
+ mozilla::StyleScrollSnapStop::Always> {};
+
+template <>
+struct ParamTraits<mozilla::ScrollSnapTargetId>
+ : public PlainOldDataSerializer<mozilla::ScrollSnapTargetId> {};
+
+template <>
+struct ParamTraits<mozilla::SnapPoint> {
+ typedef mozilla::SnapPoint paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mX);
+ WriteParam(aWriter, aParam.mY);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mX) && ReadParam(aReader, &aResult->mY);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::ScrollSnapInfo::SnapTarget> {
+ typedef mozilla::ScrollSnapInfo::SnapTarget paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mSnapPoint);
+ WriteParam(aWriter, aParam.mSnapArea);
+ WriteParam(aWriter, aParam.mScrollSnapStop);
+ WriteParam(aWriter, aParam.mTargetId);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mSnapPoint) &&
+ ReadParam(aReader, &aResult->mSnapArea) &&
+ ReadParam(aReader, &aResult->mScrollSnapStop) &&
+ ReadParam(aReader, &aResult->mTargetId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::ScrollSnapInfo::ScrollSnapRange> {
+ typedef mozilla::ScrollSnapInfo::ScrollSnapRange paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mDirection);
+ WriteParam(aWriter, aParam.mSnapArea);
+ WriteParam(aWriter, aParam.mTargetId);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mDirection) &&
+ ReadParam(aReader, &aResult->mSnapArea) &&
+ ReadParam(aReader, &aResult->mTargetId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::ScrollSnapInfo> {
+ typedef mozilla::ScrollSnapInfo paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mScrollSnapStrictnessX);
+ WriteParam(aWriter, aParam.mScrollSnapStrictnessY);
+ WriteParam(aWriter, aParam.mSnapTargets);
+ WriteParam(aWriter, aParam.mXRangeWiderThanSnapport);
+ WriteParam(aWriter, aParam.mYRangeWiderThanSnapport);
+ WriteParam(aWriter, aParam.mSnapportSize);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (ReadParam(aReader, &aResult->mScrollSnapStrictnessX) &&
+ ReadParam(aReader, &aResult->mScrollSnapStrictnessY) &&
+ ReadParam(aReader, &aResult->mSnapTargets) &&
+ ReadParam(aReader, &aResult->mXRangeWiderThanSnapport) &&
+ ReadParam(aReader, &aResult->mYRangeWiderThanSnapport) &&
+ ReadParam(aReader, &aResult->mSnapportSize));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::OverscrollBehaviorInfo> {
+ // Not using PlainOldDataSerializer so we get enum validation
+ // for the members.
+
+ typedef mozilla::layers::OverscrollBehaviorInfo paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mBehaviorX);
+ WriteParam(aWriter, aParam.mBehaviorY);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (ReadParam(aReader, &aResult->mBehaviorX) &&
+ ReadParam(aReader, &aResult->mBehaviorY));
+ }
+};
+
+template <typename T>
+struct ParamTraits<mozilla::ScrollGeneration<T>>
+ : PlainOldDataSerializer<mozilla::ScrollGeneration<T>> {};
+
+template <>
+struct ParamTraits<mozilla::ScrollUpdateType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::ScrollUpdateType, mozilla::ScrollUpdateType::Absolute,
+ mozilla::ScrollUpdateType::MergeableAbsolute> {};
+
+template <>
+struct ParamTraits<mozilla::ScrollMode>
+ : public ContiguousEnumSerializerInclusive<mozilla::ScrollMode,
+ mozilla::ScrollMode::Instant,
+ mozilla::ScrollMode::Normal> {};
+
+template <>
+struct ParamTraits<mozilla::ScrollOrigin>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::ScrollOrigin, mozilla::ScrollOrigin::None,
+ mozilla::ScrollOrigin::Scrollbars> {};
+
+template <>
+struct ParamTraits<mozilla::ScrollTriggeredByScript>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::ScrollTriggeredByScript,
+ mozilla::ScrollTriggeredByScript::No,
+ mozilla::ScrollTriggeredByScript::Yes> {};
+
+template <>
+struct ParamTraits<mozilla::ScrollPositionUpdate> {
+ typedef mozilla::ScrollPositionUpdate paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mScrollGeneration);
+ WriteParam(aWriter, aParam.mType);
+ WriteParam(aWriter, aParam.mScrollMode);
+ WriteParam(aWriter, aParam.mScrollOrigin);
+ WriteParam(aWriter, aParam.mDestination);
+ WriteParam(aWriter, aParam.mSource);
+ WriteParam(aWriter, aParam.mDelta);
+ WriteParam(aWriter, aParam.mTriggeredByScript);
+ WriteParam(aWriter, aParam.mSnapTargetIds);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mScrollGeneration) &&
+ ReadParam(aReader, &aResult->mType) &&
+ ReadParam(aReader, &aResult->mScrollMode) &&
+ ReadParam(aReader, &aResult->mScrollOrigin) &&
+ ReadParam(aReader, &aResult->mDestination) &&
+ ReadParam(aReader, &aResult->mSource) &&
+ ReadParam(aReader, &aResult->mDelta) &&
+ ReadParam(aReader, &aResult->mTriggeredByScript) &&
+ ReadParam(aReader, &aResult->mSnapTargetIds);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollMetadata>
+ : BitfieldHelper<mozilla::layers::ScrollMetadata> {
+ typedef mozilla::layers::ScrollMetadata paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mMetrics);
+ WriteParam(aWriter, aParam.mSnapInfo);
+ WriteParam(aWriter, aParam.mScrollParentId);
+ WriteParam(aWriter, aParam.GetContentDescription());
+ WriteParam(aWriter, aParam.mLineScrollAmount);
+ WriteParam(aWriter, aParam.mPageScrollAmount);
+ WriteParam(aWriter, aParam.mHasScrollgrab);
+ WriteParam(aWriter, aParam.mIsLayersIdRoot);
+ WriteParam(aWriter, aParam.mIsAutoDirRootContentRTL);
+ WriteParam(aWriter, aParam.mForceDisableApz);
+ WriteParam(aWriter, aParam.mResolutionUpdated);
+ WriteParam(aWriter, aParam.mIsRDMTouchSimulationActive);
+ WriteParam(aWriter, aParam.mDidContentGetPainted);
+ WriteParam(aWriter, aParam.mForceMousewheelAutodir);
+ WriteParam(aWriter, aParam.mForceMousewheelAutodirHonourRoot);
+ WriteParam(aWriter, aParam.mIsPaginatedPresentation);
+ WriteParam(aWriter, aParam.mDisregardedDirection);
+ WriteParam(aWriter, aParam.mOverscrollBehavior);
+ WriteParam(aWriter, aParam.mScrollUpdates);
+ }
+
+ static bool ReadContentDescription(MessageReader* aReader,
+ paramType* aResult) {
+ nsCString str;
+ if (!ReadParam(aReader, &str)) {
+ return false;
+ }
+ aResult->SetContentDescription(str);
+ return true;
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (ReadParam(aReader, &aResult->mMetrics) &&
+ ReadParam(aReader, &aResult->mSnapInfo) &&
+ ReadParam(aReader, &aResult->mScrollParentId) &&
+ ReadContentDescription(aReader, aResult) &&
+ ReadParam(aReader, &aResult->mLineScrollAmount) &&
+ ReadParam(aReader, &aResult->mPageScrollAmount) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetHasScrollgrab) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetIsLayersIdRoot) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetIsAutoDirRootContentRTL) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetForceDisableApz) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetResolutionUpdated) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetIsRDMTouchSimulationActive)) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetDidContentGetPainted) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetForceMousewheelAutodir) &&
+ ReadBoolForBitfield(
+ aReader, aResult,
+ &paramType::SetForceMousewheelAutodirHonourRoot) &&
+ ReadBoolForBitfield(aReader, aResult,
+ &paramType::SetIsPaginatedPresentation) &&
+ ReadParam(aReader, &aResult->mDisregardedDirection) &&
+ ReadParam(aReader, &aResult->mOverscrollBehavior) &&
+ ReadParam(aReader, &aResult->mScrollUpdates);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::TextureFactoryIdentifier> {
+ typedef mozilla::layers::TextureFactoryIdentifier paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mParentBackend);
+ WriteParam(aWriter, aParam.mWebRenderBackend);
+ WriteParam(aWriter, aParam.mWebRenderCompositor);
+ WriteParam(aWriter, aParam.mParentProcessType);
+ WriteParam(aWriter, aParam.mMaxTextureSize);
+ WriteParam(aWriter, aParam.mCompositorUseANGLE);
+ WriteParam(aWriter, aParam.mCompositorUseDComp);
+ WriteParam(aWriter, aParam.mUseCompositorWnd);
+ WriteParam(aWriter, aParam.mSupportsTextureBlitting);
+ WriteParam(aWriter, aParam.mSupportsPartialUploads);
+ WriteParam(aWriter, aParam.mSupportsComponentAlpha);
+ WriteParam(aWriter, aParam.mSupportsD3D11NV12);
+ WriteParam(aWriter, aParam.mSyncHandle);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ bool result = ReadParam(aReader, &aResult->mParentBackend) &&
+ ReadParam(aReader, &aResult->mWebRenderBackend) &&
+ ReadParam(aReader, &aResult->mWebRenderCompositor) &&
+ ReadParam(aReader, &aResult->mParentProcessType) &&
+ ReadParam(aReader, &aResult->mMaxTextureSize) &&
+ ReadParam(aReader, &aResult->mCompositorUseANGLE) &&
+ ReadParam(aReader, &aResult->mCompositorUseDComp) &&
+ ReadParam(aReader, &aResult->mUseCompositorWnd) &&
+ ReadParam(aReader, &aResult->mSupportsTextureBlitting) &&
+ ReadParam(aReader, &aResult->mSupportsPartialUploads) &&
+ ReadParam(aReader, &aResult->mSupportsComponentAlpha) &&
+ ReadParam(aReader, &aResult->mSupportsD3D11NV12) &&
+ ReadParam(aReader, &aResult->mSyncHandle);
+ return result;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::TextureInfo> {
+ typedef mozilla::layers::TextureInfo paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mCompositableType);
+ WriteParam(aWriter, aParam.mTextureFlags);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mCompositableType) &&
+ ReadParam(aReader, &aResult->mTextureFlags);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositableType>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::CompositableType,
+ mozilla::layers::CompositableType::UNKNOWN,
+ mozilla::layers::CompositableType::COUNT> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollableLayerGuid> {
+ typedef mozilla::layers::ScrollableLayerGuid paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mLayersId);
+ WriteParam(aWriter, aParam.mPresShellId);
+ WriteParam(aWriter, aParam.mScrollId);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (ReadParam(aReader, &aResult->mLayersId) &&
+ ReadParam(aReader, &aResult->mPresShellId) &&
+ ReadParam(aReader, &aResult->mScrollId));
+ }
+};
+
+template <>
+struct ParamTraits<nsEventStatus>
+ : public ContiguousEnumSerializer<nsEventStatus, nsEventStatus_eIgnore,
+ nsEventStatus_eSentinel> {};
+
+template <>
+struct ParamTraits<mozilla::layers::APZHandledPlace>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::APZHandledPlace,
+ mozilla::layers::APZHandledPlace::Unhandled,
+ mozilla::layers::APZHandledPlace::Last> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollDirections> {
+ typedef mozilla::layers::ScrollDirections paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.serialize());
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ uint8_t value;
+ if (!ReadParam(aReader, &value)) {
+ return false;
+ }
+ aResult->deserialize(value);
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::APZHandledResult> {
+ typedef mozilla::layers::APZHandledResult paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mPlace);
+ WriteParam(aWriter, aParam.mScrollableDirections);
+ WriteParam(aWriter, aParam.mOverscrollDirections);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (ReadParam(aReader, &aResult->mPlace) &&
+ ReadParam(aReader, &aResult->mScrollableDirections) &&
+ ReadParam(aReader, &aResult->mOverscrollDirections));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::APZEventResult> {
+ typedef mozilla::layers::APZEventResult paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.GetStatus());
+ WriteParam(aWriter, aParam.GetHandledResult());
+ WriteParam(aWriter, aParam.mTargetGuid);
+ WriteParam(aWriter, aParam.mInputBlockId);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ nsEventStatus status;
+ if (!ReadParam(aReader, &status)) {
+ return false;
+ }
+ aResult->UpdateStatus(status);
+
+ mozilla::Maybe<mozilla::layers::APZHandledResult> handledResult;
+ if (!ReadParam(aReader, &handledResult)) {
+ return false;
+ }
+ aResult->UpdateHandledResult(handledResult);
+
+ return (ReadParam(aReader, &aResult->mTargetGuid) &&
+ ReadParam(aReader, &aResult->mInputBlockId));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::ZoomConstraints> {
+ typedef mozilla::layers::ZoomConstraints paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mAllowZoom);
+ WriteParam(aWriter, aParam.mAllowDoubleTapZoom);
+ WriteParam(aWriter, aParam.mMinZoom);
+ WriteParam(aWriter, aParam.mMaxZoom);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (ReadParam(aReader, &aResult->mAllowZoom) &&
+ ReadParam(aReader, &aResult->mAllowDoubleTapZoom) &&
+ ReadParam(aReader, &aResult->mMinZoom) &&
+ ReadParam(aReader, &aResult->mMaxZoom));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::FocusTarget::ScrollTargets> {
+ typedef mozilla::layers::FocusTarget::ScrollTargets paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mHorizontal);
+ WriteParam(aWriter, aParam.mVertical);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mHorizontal) &&
+ ReadParam(aReader, &aResult->mVertical);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::FocusTarget::NoFocusTarget>
+ : public EmptyStructSerializer<
+ mozilla::layers::FocusTarget::NoFocusTarget> {};
+
+template <>
+struct ParamTraits<mozilla::layers::FocusTarget> {
+ typedef mozilla::layers::FocusTarget paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mSequenceNumber);
+ WriteParam(aWriter, aParam.mFocusHasKeyEventListeners);
+ WriteParam(aWriter, aParam.mData);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!ReadParam(aReader, &aResult->mSequenceNumber) ||
+ !ReadParam(aReader, &aResult->mFocusHasKeyEventListeners) ||
+ !ReadParam(aReader, &aResult->mData)) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<
+ mozilla::layers::KeyboardScrollAction::KeyboardScrollActionType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::KeyboardScrollAction::KeyboardScrollActionType,
+ mozilla::layers::KeyboardScrollAction::KeyboardScrollActionType::
+ eScrollCharacter,
+ mozilla::layers::KeyboardScrollAction::
+ sHighestKeyboardScrollActionType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::KeyboardScrollAction> {
+ typedef mozilla::layers::KeyboardScrollAction paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mType);
+ WriteParam(aWriter, aParam.mForward);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mType) &&
+ ReadParam(aReader, &aResult->mForward);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::KeyboardShortcut> {
+ typedef mozilla::layers::KeyboardShortcut paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mAction);
+ WriteParam(aWriter, aParam.mKeyCode);
+ WriteParam(aWriter, aParam.mCharCode);
+ WriteParam(aWriter, aParam.mModifiers);
+ WriteParam(aWriter, aParam.mModifiersMask);
+ WriteParam(aWriter, aParam.mEventType);
+ WriteParam(aWriter, aParam.mDispatchToContent);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mAction) &&
+ ReadParam(aReader, &aResult->mKeyCode) &&
+ ReadParam(aReader, &aResult->mCharCode) &&
+ ReadParam(aReader, &aResult->mModifiers) &&
+ ReadParam(aReader, &aResult->mModifiersMask) &&
+ ReadParam(aReader, &aResult->mEventType) &&
+ ReadParam(aReader, &aResult->mDispatchToContent);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::KeyboardMap> {
+ typedef mozilla::layers::KeyboardMap paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.Shortcuts());
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ nsTArray<mozilla::layers::KeyboardShortcut> shortcuts;
+ if (!ReadParam(aReader, &shortcuts)) {
+ return false;
+ }
+ *aResult = mozilla::layers::KeyboardMap(std::move(shortcuts));
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::GeckoContentController_TapType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::GeckoContentController_TapType,
+ mozilla::layers::GeckoContentController_TapType::eSingleTap,
+ mozilla::layers::kHighestGeckoContentController_TapType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::GeckoContentController_APZStateChange>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::GeckoContentController_APZStateChange,
+ mozilla::layers::GeckoContentController_APZStateChange::
+ eTransformBegin,
+ mozilla::layers::kHighestGeckoContentController_APZStateChange> {};
+
+template <>
+struct ParamTraits<mozilla::layers::EventRegionsOverride>
+ : public BitFlagsEnumSerializer<
+ mozilla::layers::EventRegionsOverride,
+ mozilla::layers::EventRegionsOverride::ALL_BITS> {};
+
+template <>
+struct ParamTraits<mozilla::layers::AsyncDragMetrics> {
+ typedef mozilla::layers::AsyncDragMetrics paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mViewId);
+ WriteParam(aWriter, aParam.mPresShellId);
+ WriteParam(aWriter, aParam.mDragStartSequenceNumber);
+ WriteParam(aWriter, aParam.mScrollbarDragOffset);
+ WriteParam(aWriter, aParam.mDirection);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (ReadParam(aReader, &aResult->mViewId) &&
+ ReadParam(aReader, &aResult->mPresShellId) &&
+ ReadParam(aReader, &aResult->mDragStartSequenceNumber) &&
+ ReadParam(aReader, &aResult->mScrollbarDragOffset) &&
+ ReadParam(aReader, &aResult->mDirection));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::BrowserGestureResponse>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::BrowserGestureResponse,
+ mozilla::layers::BrowserGestureResponse::NotConsumed,
+ mozilla::layers::BrowserGestureResponse::Consumed> {};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositorOptions> {
+ typedef mozilla::layers::CompositorOptions paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mUseAPZ);
+ WriteParam(aWriter, aParam.mUseSoftwareWebRender);
+ WriteParam(aWriter, aParam.mAllowSoftwareWebRenderD3D11);
+ WriteParam(aWriter, aParam.mAllowSoftwareWebRenderOGL);
+ WriteParam(aWriter, aParam.mInitiallyPaused);
+ WriteParam(aWriter, aParam.mNeedFastSnaphot);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mUseAPZ) &&
+ ReadParam(aReader, &aResult->mUseSoftwareWebRender) &&
+ ReadParam(aReader, &aResult->mAllowSoftwareWebRenderD3D11) &&
+ ReadParam(aReader, &aResult->mAllowSoftwareWebRenderOGL) &&
+ ReadParam(aReader, &aResult->mInitiallyPaused) &&
+ ReadParam(aReader, &aResult->mNeedFastSnaphot);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::OverlaySupportType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::OverlaySupportType,
+ mozilla::layers::OverlaySupportType::None,
+ mozilla::layers::OverlaySupportType::MAX> {};
+
+template <>
+struct ParamTraits<mozilla::layers::OverlayInfo> {
+ typedef mozilla::layers::OverlayInfo paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mSupportsOverlays);
+ WriteParam(aWriter, aParam.mNv12Overlay);
+ WriteParam(aWriter, aParam.mYuy2Overlay);
+ WriteParam(aWriter, aParam.mBgra8Overlay);
+ WriteParam(aWriter, aParam.mRgb10a2Overlay);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mSupportsOverlays) &&
+ ReadParam(aReader, &aResult->mNv12Overlay) &&
+ ReadParam(aReader, &aResult->mYuy2Overlay) &&
+ ReadParam(aReader, &aResult->mBgra8Overlay) &&
+ ReadParam(aReader, &aResult->mRgb10a2Overlay);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::SwapChainInfo> {
+ typedef mozilla::layers::SwapChainInfo paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mTearingSupported);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mTearingSupported);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollbarLayerType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::ScrollbarLayerType,
+ mozilla::layers::ScrollbarLayerType::None,
+ mozilla::layers::kHighestScrollbarLayerType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ScrollbarData> {
+ typedef mozilla::layers::ScrollbarData paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mDirection);
+ WriteParam(aWriter, aParam.mScrollbarLayerType);
+ WriteParam(aWriter, aParam.mThumbRatio);
+ WriteParam(aWriter, aParam.mThumbStart);
+ WriteParam(aWriter, aParam.mThumbLength);
+ WriteParam(aWriter, aParam.mThumbMinLength);
+ WriteParam(aWriter, aParam.mThumbIsAsyncDraggable);
+ WriteParam(aWriter, aParam.mScrollTrackStart);
+ WriteParam(aWriter, aParam.mScrollTrackLength);
+ WriteParam(aWriter, aParam.mTargetViewId);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mDirection) &&
+ ReadParam(aReader, &aResult->mScrollbarLayerType) &&
+ ReadParam(aReader, &aResult->mThumbRatio) &&
+ ReadParam(aReader, &aResult->mThumbStart) &&
+ ReadParam(aReader, &aResult->mThumbLength) &&
+ ReadParam(aReader, &aResult->mThumbMinLength) &&
+ ReadParam(aReader, &aResult->mThumbIsAsyncDraggable) &&
+ ReadParam(aReader, &aResult->mScrollTrackStart) &&
+ ReadParam(aReader, &aResult->mScrollTrackLength) &&
+ ReadParam(aReader, &aResult->mTargetViewId);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositionPayloadType>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::CompositionPayloadType,
+ mozilla::layers::CompositionPayloadType::eKeyPress,
+ mozilla::layers::kHighestCompositionPayloadType> {};
+
+template <>
+struct ParamTraits<mozilla::layers::CompositionPayload> {
+ typedef mozilla::layers::CompositionPayload paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mType);
+ WriteParam(aWriter, aParam.mTimeStamp);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mType) &&
+ ReadParam(aReader, &aResult->mTimeStamp);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::CantZoomOutBehavior>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::layers::CantZoomOutBehavior,
+ mozilla::layers::CantZoomOutBehavior::Nothing,
+ mozilla::layers::CantZoomOutBehavior::ZoomIn> {};
+
+template <>
+struct ParamTraits<mozilla::layers::ZoomTarget> {
+ typedef mozilla::layers::ZoomTarget paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.targetRect);
+ WriteParam(aWriter, aParam.cantZoomOutBehavior);
+ WriteParam(aWriter, aParam.elementBoundingRect);
+ WriteParam(aWriter, aParam.documentRelativePointerPosition);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (ReadParam(aReader, &aResult->targetRect) &&
+ ReadParam(aReader, &aResult->cantZoomOutBehavior) &&
+ ReadParam(aReader, &aResult->elementBoundingRect) &&
+ ReadParam(aReader, &aResult->documentRelativePointerPosition));
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::DoubleTapToZoomMetrics> {
+ typedef mozilla::layers::DoubleTapToZoomMetrics paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mVisualViewport);
+ WriteParam(aWriter, aParam.mRootScrollableRect);
+ WriteParam(aWriter, aParam.mTransformMatrix);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return (ReadParam(aReader, &aResult->mVisualViewport) &&
+ ReadParam(aReader, &aResult->mRootScrollableRect) &&
+ ReadParam(aReader, &aResult->mTransformMatrix));
+ }
+};
+
+#define IMPL_PARAMTRAITS_BY_SERDE(type_) \
+ template <> \
+ struct ParamTraits<mozilla::type_> { \
+ typedef mozilla::type_ paramType; \
+ static void Write(MessageWriter* aWriter, const paramType& aParam) { \
+ mozilla::ipc::ByteBuf v; \
+ mozilla::DebugOnly<bool> rv = Servo_##type_##_Serialize(&aParam, &v); \
+ MOZ_ASSERT(rv, "Serialize ##type_## failed"); \
+ WriteParam(aWriter, std::move(v)); \
+ } \
+ static ReadResult<paramType> Read(MessageReader* aReader) { \
+ mozilla::ipc::ByteBuf in; \
+ ReadResult<paramType> result; \
+ if (!ReadParam(aReader, &in) || !in.mData) { \
+ return result; \
+ } \
+ /* TODO: Should be able to initialize `result` in-place instead */ \
+ mozilla::AlignedStorage2<paramType> value; \
+ if (!Servo_##type_##_Deserialize(&in, value.addr())) { \
+ return result; \
+ } \
+ result = std::move(*value.addr()); \
+ value.addr()->~paramType(); \
+ return result; \
+ } \
+ };
+
+IMPL_PARAMTRAITS_BY_SERDE(LengthPercentage)
+IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetPath)
+IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetRotate)
+IMPL_PARAMTRAITS_BY_SERDE(StylePositionOrAuto)
+IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetPosition)
+IMPL_PARAMTRAITS_BY_SERDE(StyleRotate)
+IMPL_PARAMTRAITS_BY_SERDE(StyleScale)
+IMPL_PARAMTRAITS_BY_SERDE(StyleTranslate)
+IMPL_PARAMTRAITS_BY_SERDE(StyleTransform)
+IMPL_PARAMTRAITS_BY_SERDE(StyleComputedTimingFunction)
+
+} /* namespace IPC */
+
+#endif /* mozilla_layers_LayersMessageUtils */
diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh
new file mode 100644
index 0000000000..a0e4173507
--- /dev/null
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include protocol PCompositorBridge;
+include protocol PTexture;
+
+include "gfxipc/ShadowLayerUtils.h";
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+
+using mozilla::gfx::Glyph from "mozilla/gfx/2D.h";
+using mozilla::gfx::SamplingFilter from "mozilla/gfx/2D.h";
+using struct mozilla::gfx::DeviceColor from "mozilla/gfx/2D.h";
+using mozilla::gfx::Point from "mozilla/gfx/Point.h";
+using mozilla::gfx::Point3D from "mozilla/gfx/Point.h";
+using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
+using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using mozilla::SideBits from "mozilla/gfx/Types.h";
+using nscolor from "nsColor.h";
+using nscoord from "nsCoord.h";
+using struct nsRect from "nsRect.h";
+using struct nsPoint from "nsPoint.h";
+using mozilla::TimeDuration from "mozilla/TimeStamp.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using mozilla::ScreenRotation from "mozilla/WidgetUtils.h";
+using nsCSSPropertyID from "nsCSSPropertyID.h";
+using mozilla::hal::ScreenOrientation from "mozilla/HalIPCUtils.h";
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::CSSCoord from "Units.h";
+using mozilla::CSSPoint from "Units.h";
+using mozilla::CSSRect from "Units.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
+using mozilla::LayerMargin from "Units.h";
+using mozilla::LayerPoint from "Units.h";
+using mozilla::LayerCoord from "Units.h";
+using mozilla::LayerSize from "Units.h";
+using mozilla::LayerRect from "Units.h";
+using mozilla::LayerIntSize from "Units.h";
+using mozilla::LayerIntRect from "Units.h";
+using mozilla::LayerIntRegion from "Units.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+using mozilla::ParentLayerIntRect from "Units.h";
+using mozilla::ParentLayerRect from "Units.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
+using mozilla::LayoutDevicePoint from "Units.h";
+using mozilla::LayoutDeviceRect from "Units.h";
+using mozilla::layers::ScaleMode from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::ScrollDirection from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositionPayload from "mozilla/layers/LayersTypes.h";
+[MoveOnly] using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::RemoteTextureId from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::RemoteTextureOwnerId from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
+using mozilla::LengthPercentage from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleOffsetPath from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleOffsetRotate from "mozilla/ServoStyleConsts.h";
+using mozilla::StylePositionOrAuto from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleOffsetPosition from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleRotate from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleScale from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleTranslate from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleTransform from "mozilla/ServoStyleConsts.h";
+using mozilla::StyleComputedTimingFunction from "mozilla/ServoStyleConsts.h";
+
+namespace mozilla {
+namespace layers {
+
+struct TargetConfig {
+ IntRect naturalBounds;
+ ScreenRotation rotation;
+ ScreenOrientation orientation;
+ nsIntRegion clearRegion;
+};
+
+struct OpAttachCompositable {
+ LayerHandle layer;
+ CompositableHandle compositable;
+};
+
+struct OpAttachAsyncCompositable {
+ LayerHandle layer;
+ CompositableHandle compositable;
+};
+struct LayerColor { DeviceColor value; };
+
+[Comparable] union Animatable {
+ null_t;
+ float;
+ nscolor;
+ StyleRotate;
+ StyleScale;
+ StyleTranslate;
+ StyleTransform;
+ StyleOffsetPath;
+ LengthPercentage;
+ StyleOffsetRotate;
+ StylePositionOrAuto;
+ StyleOffsetPosition;
+};
+
+struct AnimationSegment {
+ Animatable startState;
+ Animatable endState;
+ float startPortion;
+ float endPortion;
+ uint8_t startComposite;
+ uint8_t endComposite;
+ StyleComputedTimingFunction? sampleFn;
+};
+
+[Comparable] struct MotionPathData {
+ // The transform-origin property for motion in css pixels
+ CSSPoint origin;
+ // The adjustment for the anchor point of motion path.
+ CSSPoint anchorAdjustment;
+ // The coord box of the containing block.
+ nsRect coordBox;
+ // The current position of this transfromed box in the coordinate system of
+ // its containing block.
+ nsPoint currentPosition;
+ // The reference length for computing ray(contain) (in css pixels).
+ CSSCoord rayContainReferenceLength;
+ // The resolved border-radius of its containing block. If the array size is
+ // zero, we don't have radius.
+ nscoord[] coordBoxInsetRadii;
+};
+
+[Comparable] struct PartialPrerenderData {
+ LayoutDeviceRect rect;
+ SideBits overflowedSides;
+ // the scroll id of the nearest scrollable frame of this partial prerender
+ // data.
+ ViewID scrollId;
+ // The clip rectangle of the nearest scrollable frame.
+ // NOTE: This should be used only for fallback cases where APZ is not enabled.
+ ParentLayerRect clipRect;
+ // a transform from the coordinate space of the animated element to a
+ // coordinate space where the `clipRect` can be applied.
+ Matrix4x4 transformInClip; // Used only for WebRender.
+ // the position relative to the reference frame of the animated transform
+ // element in the element coordinate space.
+ LayoutDevicePoint position; // Used only for WebRender.
+};
+
+// Transforms need extra information to correctly convert the list of transform
+// functions to a Matrix4x4 that can be applied directly to the layer.
+[Comparable] struct TransformData {
+ // the origin of the frame being transformed in app units
+ nsPoint origin;
+ // the transform-origin property for the transform in device pixels
+ Point3D transformOrigin;
+ nsRect bounds;
+ int32_t appUnitsPerDevPixel;
+ MotionPathData? motionPathData;
+ PartialPrerenderData? partialPrerenderData;
+};
+
+// The scroll timeline information.
+struct ScrollTimelineOptions {
+ // The source of the scroll-timeline.
+ ViewID source;
+ // The physical direction.
+ ScrollDirection axis;
+};
+
+struct Animation {
+ // The zero time of this Animation's timeline. May be null if isNotPlaying is
+ // true.
+ TimeStamp originTime;
+ // The start time is relative to the originTime. This allows us to represent
+ // start times in the distant past that cannot be expressed using a TimeStamp.
+ TimeDuration? startTime;
+ TimeDuration delay;
+ TimeDuration endDelay;
+ // The value of the animation's current time at the moment it was sent to the
+ // compositor. This value will be used for below cases:
+ // 1) Animations that are play-pending. Initially these animations will have a
+ // null |startTime|. Once the animation is ready to start (i.e. painting
+ // has finished), we calculate an appropriate value of |startTime| such
+ // that playback begins from |holdTime|.
+ // 2) Not playing animations (e.g. paused and finished animations). In this
+ // case the |holdTime| represents the current time the animation will
+ // maintain.
+ TimeDuration holdTime;
+ TimeDuration duration;
+ // For each frame, the interpolation point is computed based on the
+ // startTime, the direction, the duration, and the current time.
+ // The segments must uniquely cover the portion from 0.0 to 1.0
+ AnimationSegment[] segments;
+ // Number of times to repeat the animation, including positive infinity.
+ // Values <= 0 mean the animation will not play (although events are still
+ // dispatched on the main thread).
+ float iterations;
+ float iterationStart;
+ // This uses the NS_STYLE_ANIMATION_DIRECTION_* constants.
+ uint8_t direction;
+ // This uses dom::FillMode.
+ uint8_t fillMode;
+ nsCSSPropertyID property;
+ float playbackRate;
+ // When performing an asynchronous update to the playbackRate, |playbackRate|
+ // above is the updated playbackRate while |previousPlaybackRate| is the
+ // existing playbackRate. This is used by AnimationInfo to update the
+ // startTime based on the 'readyTime' (timestamp at the end of painting)
+ // and is not used beyond that point.
+ //
+ // It is set to numeric_limits<float>::quiet_NaN() when no asynchronous update
+ // to the playbackRate is being performed.
+ float previousPlaybackRate;
+ // This is used in the transformed progress calculation.
+ StyleComputedTimingFunction? easingFunction;
+ uint8_t iterationComposite;
+ // True if the animation has a fixed current time (e.g. paused and
+ // forward-filling animations).
+ bool isNotPlaying;
+ // True if this is not an animating property. For some transform-like
+ // properties, we just send their baseStyles for merging with other animating
+ // properties. In this case, we don't have animation information on this
+ // property, and so don't need to do interpolation.
+ bool isNotAnimating;
+ // The base style that animations should composite with. This is only set for
+ // 1. scroll animations with positive delays, or
+ // 2. animations with a composite mode of additive or accumulate, and only for
+ // the first animation in the set (i.e. the animation that is lowest in the
+ // stack).
+ // In all other cases the value is null_t.
+ Animatable baseStyle;
+ // An optional data specific for transform like properies.
+ TransformData? transformData;
+ // If this is present, the animation is driven by a ScrollTimeline, and
+ // this structure contains information about that timeline.
+ ScrollTimelineOptions? scrollTimelineOptions;
+};
+
+struct CompositorAnimations {
+ Animation[] animations;
+ // This id is used to map the layer animations between content
+ // and compositor side
+ uint64_t id;
+};
+
+struct ShmemSection {
+ Shmem shmem;
+ uint32_t offset;
+ uint32_t size;
+};
+
+struct CrossProcessSemaphoreDescriptor {
+ CrossProcessSemaphoreHandle sem;
+};
+
+union ReadLockDescriptor {
+ ShmemSection;
+ CrossProcessSemaphoreDescriptor;
+ uintptr_t;
+ null_t;
+};
+
+/**
+ * Tells the CompositableHost to remove the corresponding TextureHost
+ */
+struct OpRemoveTexture {
+ PTexture texture;
+};
+
+struct TimedTexture {
+ PTexture texture;
+ TimeStamp timeStamp;
+ IntRect picture;
+ uint32_t frameID;
+ uint32_t producerID;
+ bool readLocked;
+};
+
+/**
+ * Tells the compositor-side which textures to use (for example, as front buffer
+ * if there are several textures for double buffering).
+ * This provides a list of textures with timestamps, ordered by timestamp.
+ * The newest texture whose timestamp is <= the current time is rendered
+ * (where null is considered less than every other timestamp). If there is no
+ * such texture, the first texture is rendered.
+ * The first timestamp value can be null, but the others must not be.
+ * The list must not be empty.
+ */
+struct OpUseTexture {
+ TimedTexture[] textures;
+};
+
+struct OpUseRemoteTexture {
+ RemoteTextureId textureId;
+ RemoteTextureOwnerId ownerId;
+ IntSize size;
+ TextureFlags textureFlags;
+};
+
+struct OpEnableRemoteTexturePushCallback {
+ RemoteTextureOwnerId ownerId;
+ IntSize size;
+ TextureFlags textureFlags;
+};
+
+struct OpNotifyNotUsed {
+ uint64_t TextureId;
+ uint64_t fwdTransactionId;
+};
+
+union CompositableOperationDetail {
+ OpRemoveTexture;
+
+ OpUseTexture;
+
+ OpUseRemoteTexture;
+
+ OpEnableRemoteTexturePushCallback;
+};
+
+struct CompositableOperation {
+ CompositableHandle compositable;
+ CompositableOperationDetail detail;
+};
+
+// Operations related to destroying resources, always handled after the other
+// operations for safety.
+union OpDestroy {
+ PTexture;
+ CompositableHandle;
+};
+
+// Replies to operations
+
+struct OpContentBufferSwap {
+ CompositableHandle compositable;
+ nsIntRegion frontUpdatedRegion;
+};
+
+/**
+ * An ImageCompositeNotification is sent the first time a particular
+ * image is composited by an ImageHost.
+ */
+struct ImageCompositeNotification {
+ CompositableHandle compositable;
+ TimeStamp imageTimeStamp;
+ TimeStamp firstCompositeTimeStamp;
+ uint32_t frameID;
+ uint32_t producerID;
+};
+
+union AsyncParentMessageData {
+ OpNotifyNotUsed;
+};
+
+union OMTAValue {
+ null_t;
+ nscolor;
+ float;
+ Matrix4x4;
+};
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh
new file mode 100644
index 0000000000..e38b2c74dc
--- /dev/null
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -0,0 +1,217 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "gfxipc/ShadowLayerUtils.h";
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+using gfxPoint from "gfxPoint.h";
+using nsIntRegion from "nsRegion.h";
+using mozilla::StereoMode from "ImageTypes.h";
+using struct mozilla::null_t from "mozilla/ipc/IPCCore.h";
+using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h";
+using mozilla::gfx::YUVColorSpace from "mozilla/gfx/Types.h";
+using mozilla::gfx::ChromaSubsampling from "mozilla/gfx/Types.h";
+using mozilla::gfx::ColorDepth from "mozilla/gfx/Types.h";
+using mozilla::gfx::ColorRange from "mozilla/gfx/Types.h";
+using mozilla::gfx::ColorSpace2 from "mozilla/gfx/Types.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
+using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using mozilla::gfx::FenceInfo from "mozilla/gfx/FileHandleWrapper.h";
+[RefCounted] using mozilla::gfx::FileHandleWrapper from "mozilla/gfx/FileHandleWrapper.h";
+[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using gfxImageFormat from "gfxTypes.h";
+using mozilla::layers::MaybeVideoBridgeSource from "mozilla/layers/VideoBridgeUtils.h";
+using mozilla::layers::RemoteTextureId from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::RemoteTextureOwnerId from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::GpuProcessTextureId from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::GpuProcessQueryId from "mozilla/layers/LayersTypes.h";
+using mozilla::wr::ExternalImageSource from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+[Comparable] struct SurfaceDescriptorD3D10 {
+ nullable FileHandleWrapper handle;
+ GpuProcessTextureId? gpuProcessTextureId;
+ uint32_t arrayIndex;
+ SurfaceFormat format;
+ IntSize size;
+ ColorSpace2 colorSpace;
+ ColorRange colorRange;
+ bool hasKeyedMutex;
+ FenceInfo? fenceInfo;
+ GpuProcessQueryId? gpuProcessQueryId;
+};
+
+[Comparable] struct SurfaceDescriptorDXGIYCbCr {
+ nullable FileHandleWrapper handleY;
+ nullable FileHandleWrapper handleCb;
+ nullable FileHandleWrapper handleCr;
+ IntSize size;
+ IntSize sizeY;
+ IntSize sizeCbCr;
+ ColorDepth colorDepth;
+ YUVColorSpace yUVColorSpace;
+ ColorRange colorRange;
+};
+
+[Comparable] struct SurfaceDescriptorMacIOSurface {
+ uint32_t surfaceId;
+ bool isOpaque;
+ YUVColorSpace yUVColorSpace;
+};
+
+[Comparable] struct SurfaceDescriptorDMABuf {
+ uint32_t bufferType;
+ uint64_t[] modifier;
+ uint32_t flags;
+ FileDescriptor[] fds;
+ uint32_t[] width;
+ uint32_t[] height;
+ uint32_t[] widthAligned;
+ uint32_t[] heightAligned;
+ uint32_t[] format;
+ uint32_t[] strides;
+ uint32_t[] offsets;
+ YUVColorSpace yUVColorSpace;
+ ColorRange colorRange;
+ FileDescriptor[] fence;
+ uint32_t uid;
+ FileDescriptor[] refCount;
+};
+
+[Comparable] struct SurfaceTextureDescriptor {
+ uint64_t handle;
+ IntSize size;
+ SurfaceFormat format;
+ bool continuous;
+ bool forceBT709ColorSpace;
+ Matrix4x4? transformOverride;
+};
+
+[Comparable] struct SurfaceDescriptorAndroidHardwareBuffer {
+ uint64_t bufferId;
+ IntSize size;
+ SurfaceFormat format;
+};
+
+[Comparable] struct EGLImageDescriptor {
+ uintptr_t image; // `EGLImage` is a `void*`.
+ uintptr_t fence;
+ IntSize size;
+ bool hasAlpha;
+};
+
+[Comparable] struct SurfaceDescriptorSharedGLTexture {
+ uint32_t texture;
+ uint32_t target;
+ uintptr_t fence;
+ IntSize size;
+ bool hasAlpha;
+};
+
+[Comparable] struct SurfaceDescriptorDcompSurface {
+ FileDescriptor handle;
+ IntSize size;
+ SurfaceFormat format;
+};
+
+[Comparable] union RemoteDecoderVideoSubDescriptor {
+ SurfaceDescriptorD3D10;
+ SurfaceDescriptorDXGIYCbCr;
+ SurfaceDescriptorDMABuf;
+ SurfaceDescriptorMacIOSurface;
+ SurfaceDescriptorDcompSurface;
+ null_t;
+};
+
+[Comparable] struct SurfaceDescriptorRemoteDecoder {
+ uint64_t handle;
+ RemoteDecoderVideoSubDescriptor subdesc;
+ MaybeVideoBridgeSource source;
+};
+
+[Comparable] union SurfaceDescriptorGPUVideo {
+ SurfaceDescriptorRemoteDecoder;
+};
+
+[Comparable] struct RGBDescriptor {
+ IntSize size;
+ SurfaceFormat format;
+};
+
+[Comparable] struct YCbCrDescriptor {
+ IntRect display;
+ IntSize ySize;
+ uint32_t yStride;
+ IntSize cbCrSize;
+ uint32_t cbCrStride;
+ uint32_t yOffset;
+ uint32_t cbOffset;
+ uint32_t crOffset;
+ StereoMode stereoMode;
+ ColorDepth colorDepth;
+ YUVColorSpace yUVColorSpace;
+ ColorRange colorRange;
+ ChromaSubsampling chromaSubsampling;
+};
+
+[Comparable] union BufferDescriptor {
+ RGBDescriptor;
+ YCbCrDescriptor;
+};
+
+[Comparable] union MemoryOrShmem {
+ uintptr_t;
+ Shmem;
+};
+
+[Comparable] struct SurfaceDescriptorBuffer {
+ BufferDescriptor desc;
+ MemoryOrShmem data;
+};
+
+[Comparable] struct SurfaceDescriptorShared
+{
+ IntSize size;
+ int32_t stride;
+ SurfaceFormat format;
+ Handle handle;
+};
+
+[Comparable] struct SurfaceDescriptorExternalImage
+{
+ ExternalImageSource source;
+ ExternalImageId id;
+};
+
+[Comparable] struct SurfaceDescriptorRemoteTexture {
+ RemoteTextureId textureId;
+ RemoteTextureOwnerId ownerId;
+};
+
+[Comparable] union SurfaceDescriptor {
+ SurfaceDescriptorBuffer;
+ SurfaceDescriptorD3D10;
+ SurfaceDescriptorDXGIYCbCr;
+ SurfaceDescriptorDMABuf;
+ SurfaceTextureDescriptor;
+ SurfaceDescriptorAndroidHardwareBuffer;
+ EGLImageDescriptor;
+ SurfaceDescriptorMacIOSurface;
+ SurfaceDescriptorSharedGLTexture;
+ SurfaceDescriptorGPUVideo;
+ SurfaceDescriptorRemoteTexture;
+ SurfaceDescriptorDcompSurface;
+ SurfaceDescriptorExternalImage;
+ null_t;
+};
+
+} // namespace
+} // namespace
diff --git a/gfx/layers/ipc/PAPZ.ipdl b/gfx/layers/ipc/PAPZ.ipdl
new file mode 100644
index 0000000000..5525a25edd
--- /dev/null
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/layers/RemoteContentController.h";
+
+include protocol PCompositorBridge;
+
+using mozilla::CSSRect from "Units.h";
+using struct mozilla::layers::RepaintRequest from "mozilla/layers/RepaintRequest.h";
+using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::GeckoContentController_APZStateChange from "mozilla/layers/GeckoContentControllerTypes.h";
+using mozilla::layers::ScrollDirection from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::MatrixMessage from "mozilla/layers/MatrixMessage.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
+using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
+using class nsRegion from "nsRegion.h";
+
+namespace mozilla {
+namespace layers {
+
+
+/**
+ * PAPZ is a protocol for remoting a GeckoContentController. PAPZ lives on the
+ * PCompositorBridge protocol which either connects to the compositor thread
+ * in the main process, or to the compositor thread in the gpu processs.
+ *
+ * PAPZParent lives in the compositor thread, while PAPZChild lives wherever the remoted
+ * GeckoContentController lives (generally the main thread of the main or content process).
+ * RemoteContentController implements PAPZParent, while APZChild implements PAPZChild.
+ *
+ * PAPZ is always used for ContentProcessController and only used for ChromeProcessController
+ * when there is a gpu process, otherwhise ChromeProcessController is used directly on the
+ * compositor thread. Only the methods that are used by the [Chrome,Content]ProcessController
+ * are implemented. If a new method is needed then PAPZ, APZChild, and RemoteContentController
+ * must be updated to handle it.
+ */
+[ManualDealloc, ParentImpl="RemoteContentController"]
+sync protocol PAPZ
+{
+ manager PCompositorBridge;
+
+parent:
+ async __delete__();
+
+child:
+ async LayerTransforms(MatrixMessage[] aTransforms);
+
+ [Priority=control]
+ async RequestContentRepaint(RepaintRequest request);
+
+ async UpdateOverscrollVelocity(ScrollableLayerGuid aGuid, float aX, float aY, bool aIsRootContent);
+
+ async UpdateOverscrollOffset(ScrollableLayerGuid aGuid, float aX, float aY, bool aIsRootContent);
+
+ async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent);
+
+ async NotifyAPZStateChange(ScrollableLayerGuid aGuid, GeckoContentController_APZStateChange aChange, int aArg, uint64_t? aInputBlockId);
+
+ [Priority=control]
+ async NotifyFlushComplete();
+
+ async NotifyAsyncScrollbarDragInitiated(uint64_t aDragBlockId, ViewID aScrollId, ScrollDirection aDirection);
+
+ async NotifyAsyncScrollbarDragRejected(ViewID aScrollId);
+
+ async NotifyAsyncAutoscrollRejected(ViewID aScrollId);
+
+both:
+ async Destroy();
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PAPZCTreeManager.ipdl b/gfx/layers/ipc/PAPZCTreeManager.ipdl
new file mode 100644
index 0000000000..a08e2cfeb1
--- /dev/null
+++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "ipc/nsGUIEventIPC.h";
+
+include protocol PCompositorBridge;
+
+using mozilla::CSSRect from "Units.h";
+using mozilla::LayoutDeviceCoord from "Units.h";
+using mozilla::LayoutDevicePoint from "Units.h";
+using mozilla::ScreenPoint from "Units.h";
+using mozilla::layers::ZoomTarget from "mozilla/layers/DoubleTapToZoom.h";
+using mozilla::layers::DoubleTapToZoomMetrics from "mozilla/layers/DoubleTapToZoom.h";
+using mozilla::layers::ZoomConstraints from "mozilla/layers/ZoomConstraints.h";
+using mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
+using mozilla::layers::GeckoContentController_TapType from "mozilla/layers/GeckoContentControllerTypes.h";
+using mozilla::layers::APZHandledResult from "mozilla/layers/APZInputBridge.h";
+using mozilla::layers::BrowserGestureResponse from "mozilla/layers/APZInputBridge.h";
+using class mozilla::layers::KeyboardMap from "mozilla/layers/KeyboardMap.h";
+using mozilla::wr::RenderRoot from "mozilla/webrender/WebRenderTypes.h";
+
+using mozilla::Modifiers from "mozilla/EventForwards.h";
+using mozilla::PinchGestureInput::PinchGestureType from "InputData.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PAPZCTreeManager is a protocol for remoting an IAPZCTreeManager. PAPZCTreeManager
+ * lives on the PCompositorBridge protocol which either connects to the compositor
+ * thread in the main process, or to the compositor thread in the gpu processs.
+ *
+ * PAPZCTreeManagerParent lives in the compositor thread, while PAPZCTreeManagerChild
+ * lives in the main thread of the main or the content process. APZCTreeManagerParent
+ * and APZCTreeManagerChild implement this protocol.
+ */
+[ManualDealloc]
+protocol PAPZCTreeManager
+{
+manager PCompositorBridge;
+
+parent:
+
+ // These messages correspond to the methods
+ // on the IAPZCTreeManager interface
+
+ async ZoomToRect(ScrollableLayerGuid aGuid, ZoomTarget aZoomTarget, uint32_t Flags);
+
+ async ContentReceivedInputBlock(uint64_t aInputBlockId, bool PreventDefault);
+
+ async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] Targets);
+
+ async UpdateZoomConstraints(ScrollableLayerGuid aGuid, ZoomConstraints? aConstraints);
+
+ async SetKeyboardMap(KeyboardMap aKeyboardMap);
+
+ async SetDPI(float aDpiValue);
+
+ async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aValues);
+
+ async StartScrollbarDrag(ScrollableLayerGuid aGuid, AsyncDragMetrics aDragMetrics);
+
+ async StartAutoscroll(ScrollableLayerGuid aGuid, ScreenPoint aAnchorLocation);
+
+ async StopAutoscroll(ScrollableLayerGuid aGuid);
+
+ async SetLongTapEnabled(bool aTapGestureEnabled);
+
+ async SetBrowserGestureResponse(uint64_t aInputBlockId,
+ BrowserGestureResponse aResponse);
+
+ async __delete__();
+
+child:
+
+ async HandleTap(GeckoContentController_TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
+ ScrollableLayerGuid aGuid, uint64_t aInputBlockId,
+ DoubleTapToZoomMetrics? aDoubleTapToZoomMetrics);
+
+ async NotifyPinchGesture(PinchGestureType aType, ScrollableLayerGuid aGuid,
+ LayoutDevicePoint aFocusPoint, LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers);
+
+ async CancelAutoscroll(ViewID aScrollId);
+
+ async NotifyScaleGestureComplete(ViewID aScrollId, float aScale);
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/layers/ipc/PAPZInputBridge.ipdl b/gfx/layers/ipc/PAPZInputBridge.ipdl
new file mode 100644
index 0000000000..7564757b28
--- /dev/null
+++ b/gfx/layers/ipc/PAPZInputBridge.ipdl
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "ipc/nsGUIEventIPC.h";
+
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::APZEventResult from "mozilla/layers/APZInputBridge.h";
+using struct mozilla::layers::APZHandledResult from "mozilla/layers/APZInputBridge.h";
+
+using mozilla::EventMessage from "mozilla/EventForwards.h";
+using class mozilla::MultiTouchInput from "InputData.h";
+using class mozilla::MouseInput from "InputData.h";
+using class mozilla::PanGestureInput from "InputData.h";
+using class mozilla::PinchGestureInput from "InputData.h";
+using class mozilla::TapGestureInput from "InputData.h";
+using class mozilla::ScrollWheelInput from "InputData.h";
+using class mozilla::KeyboardInput from "InputData.h";
+
+using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This protocol is used to send input events from the UI process to the
+ * GPU process for handling by APZ. There is one instance per top-level
+ * compositor, or in other words, one instance per concrete APZCTreeManager
+ * instance. The child side lives on the controller thread in the UI process,
+ * ie the main thread on most platforms, but the Android UI thread on Android.
+ * The parent side lives on the main thread in the GPU process. If there is no
+ * GPU process, then this protocol is not instantiated.
+ */
+[ParentProc=GPU, ChildProc=Parent]
+sync protocol PAPZInputBridge
+{
+parent:
+ // The following messages are used to
+ // implement the ReceiveInputEvent methods
+
+ sync ReceiveMultiTouchInputEvent(MultiTouchInput aEvent, bool aWantsCallback)
+ returns(APZEventResult aOutResult, MultiTouchInput aOutEvent);
+
+ sync ReceiveMouseInputEvent(MouseInput aEvent, bool aWantsCallback)
+ returns (APZEventResult aOutResult,
+ MouseInput aOutEvent);
+
+ sync ReceivePanGestureInputEvent(PanGestureInput aEvent, bool aWantsCallback)
+ returns (APZEventResult aOutResult,
+ PanGestureInput aOutEvent);
+
+ sync ReceivePinchGestureInputEvent(PinchGestureInput aEvent,
+ bool aWantsCallback)
+ returns (APZEventResult aOutResult,
+ PinchGestureInput aOutEvent);
+
+ sync ReceiveTapGestureInputEvent(TapGestureInput aEvent, bool aWantsCallback)
+ returns (APZEventResult aOutResult,
+ TapGestureInput aOutEvent);
+
+ sync ReceiveScrollWheelInputEvent(ScrollWheelInput aEvent,
+ bool aWantsCallback)
+ returns (APZEventResult aOutResult,
+ ScrollWheelInput aOutEvent);
+
+ sync ReceiveKeyboardInputEvent(KeyboardInput aEvent, bool aWantsCallback)
+ returns (APZEventResult aOutResult,
+ KeyboardInput aOutEvent);
+
+ async UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
+ EventMessage aEventMessage,
+ ScrollableLayerGuid? aTargetGuid);
+
+ sync ProcessUnhandledEvent(LayoutDeviceIntPoint aRefPoint)
+ returns (LayoutDeviceIntPoint aOutRefPoint,
+ ScrollableLayerGuid aOutTargetGuid,
+ uint64_t aOutFocusSequenceNumber,
+ LayersId aOutLayersId);
+
+child:
+ async CallInputBlockCallback(uint64_t aInputBlockId,
+ APZHandledResult aHandledResult);
+};
+
+} // namespace gfx
+} // namespace mozilla
diff --git a/gfx/layers/ipc/PCanvas.ipdl b/gfx/layers/ipc/PCanvas.ipdl
new file mode 100644
index 0000000000..5187e61d23
--- /dev/null
+++ b/gfx/layers/ipc/PCanvas.ipdl
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PCanvasManager;
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/layers/CanvasTranslator.h";
+
+[MoveOnly] using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
+using mozilla::layers::TextureType from "mozilla/layers/LayersTypes.h";
+[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::gfx::BackendType from "mozilla/gfx/Types.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PCanvas is the IPDL for recorded Canvas drawing.
+ */
+[ParentImpl="CanvasTranslator"]
+async protocol PCanvas {
+ manager PCanvasManager;
+
+parent:
+ /**
+ * Initialize a CanvasTranslator for a particular TextureType, which
+ * translates events from shared memory buffers. aHeaderHandle is a shared
+ * memory handle for the control header. aBufferHandles are shared memory
+ * handles for the initial buffers for translation. aBufferSize is the size of
+ * each aBufferHandles' memory and the default size. aReaderSem and aWriterSem
+ * are handles for the semaphores to handle waiting on either side.
+ */
+ async InitTranslator(TextureType aTextureType, BackendType aBackendType,
+ Handle aHeaderHandle, Handle[] aBufferHandles,
+ uint64_t aBufferSize,
+ CrossProcessSemaphoreHandle aReaderSem,
+ CrossProcessSemaphoreHandle aWriterSem);
+
+ /**
+ * Restart the translation from a Stopped state.
+ */
+ async RestartTranslation();
+
+ /**
+ * Adds a new buffer to be translated. The current buffer will be recycled if
+ * it is of the default size. The translation will then be restarted.
+ */
+ async AddBuffer(Handle aBufferHandle, uint64_t aBufferSize);
+
+ /**
+ * Sets the shared memory to be used for readback.
+ */
+ async SetDataSurfaceBuffer(Handle aBufferHandle, uint64_t aBufferSize);
+
+ /**
+ * Notify CanvasTranslator it is about to be minimized.
+ */
+ async ClearCachedResources();
+
+ async __delete__();
+
+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();
+
+ /**
+ * Block further accelerated canvases from being created, but allow existing
+ * canvases to continue processing.
+ */
+ async BlockCanvas();
+
+ /**
+ * Notify that a remote accelerated canvas requires a fallback refresh.
+ */
+ async NotifyRequiresRefresh(int64_t aTextureId);
+
+ /**
+ * Cache the shmem of the framebuffer for snapshotting.
+ */
+ async SnapshotShmem(int64_t aTextureId, Handle aShmemHandle, uint32_t aShmemSize) returns (bool aSuccess);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl
new file mode 100644
index 0000000000..abe89fb87d
--- /dev/null
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -0,0 +1,209 @@
+/* -*- 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 PCompositorManager;
+include protocol PCompositorWidget;
+include protocol PTexture;
+include protocol PWebRenderBridge;
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+include "mozilla/layers/CompositorBridgeParent.h";
+
+using struct mozilla::null_t from "mozilla/ipc/IPCCore.h";
+using struct mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using mozilla::layers::WindowKind from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::CSSIntRegion from "Units.h";
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using mozilla::LayoutDeviceIntRegion from "Units.h";
+using mozilla::LayoutDeviceIntSize from "Units.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
+using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::RenderReasons from "mozilla/webrender/webrender_ffi.h";
+using base::ProcessId from "base/process.h";
+using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+struct FrameStats {
+ TransactionId id;
+ TimeStamp compositeStart;
+ TimeStamp renderStart;
+ TimeStamp compositeEnd;
+ int32_t contentFrameTime;
+ double resourceUploadTime;
+ double gpuCacheUploadTime;
+ TimeStamp transactionStart;
+ TimeStamp refreshStart;
+ TimeStamp fwdTime;
+ TimeStamp sceneBuiltTime;
+ uint32_t skippedComposites;
+ nsCString url;
+};
+
+/**
+ * The PCompositorBridge protocol is a top-level protocol for the compositor.
+ * There is an instance of the protocol for each compositor, plus one for each
+ * content process. In other words:
+ * - There is a CompositorBridgeParent/CompositorBridgeChild pair created
+ * for each "top level browser window", which has its own compositor. The
+ * CompositorBridgeChild instance lives in the UI process, and the
+ * CompositorBridgeParent lives in the GPU process (if there is one) or the
+ * UI process otherwise.
+ * - There is also a ContentCompositorBridgeParent/CompositorBridgeChild
+ * pair created for each content process. The ContentCompositorBridgeParent
+ * lives in the GPU process (if there is one) or the UI process otherwise. The
+ * CompositorBridgeChild is a singleton in the content process. Note that
+ * a given content process may have multiple browser instances (represented
+ * by BrowserChild instances), that are attached to different windows, and therefore
+ * rendered by different compositors. This means that when a browser instance
+ * sends messages via its CompositorBridgeChild, the corresponding
+ * ContentCompositorBridgeParent has to use the layers id in the message
+ * to find the correct compositor or CompositorBridgeParent to pass the message
+ * on to.
+ *
+ * One of the main goals of this protocol is to manage the PLayerTransaction sub-
+ * protocol, which is per-browser. A lot of the functions in the protocol are
+ * basically multiplexing/demultiplexing stuff in PLayerTransaction.
+ */
+[ParentImpl="CompositorBridgeParentBase"]
+sync protocol PCompositorBridge
+{
+ manager PCompositorManager;
+
+ manages PAPZ;
+ manages PAPZCTreeManager;
+ // A Compositor manages a single Layer Manager (PLayerTransaction)
+ manages PTexture;
+ manages PCompositorWidget;
+ manages PWebRenderBridge;
+
+child:
+ // The compositor completed a layers transaction. id is the layers id
+ // of the child layer tree that was composited (or 0 when notifying
+ // the root layer tree).
+ // transactionId is the id of the transaction before this composite, or 0
+ // if there was no transaction since the last composite.
+ [Priority=control] async DidComposite(LayersId id,
+ TransactionId[] transactionId,
+ TimeStamp compositeStart,
+ TimeStamp compositeEnd);
+
+ async NotifyFrameStats(FrameStats[] aFrameStats);
+
+ async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
+
+ async ObserveLayersUpdate(LayersId aLayersId, bool aActive);
+
+ async CompositorOptionsChanged(LayersId id, CompositorOptions newOptions);
+
+ async NotifyJankedAnimations(LayersId id, uint64_t[] aJankedAnimations);
+
+parent:
+ async __delete__();
+
+ // Must be called before Initialize().
+ async PCompositorWidget(CompositorWidgetInitData aInitData);
+
+ // When out-of-process, this must be called to finish initialization.
+ sync Initialize(LayersId rootLayerTreeId);
+
+ // Must be called after Initialize(), and only succeeds if AsyncPanZoomEnabled() is true.
+ async PAPZ(LayersId layersId);
+ async PAPZCTreeManager(LayersId layersId);
+
+ // The child is about to be destroyed, so perform any necessary cleanup.
+ sync WillClose();
+
+ // Pause/resume the compositor. These are intended to be used on mobile, when
+ // the compositor needs to pause/resume in lockstep with the application.
+ sync Pause();
+ sync Resume();
+ async ResumeAsync();
+
+ // See bug 1316632 comment #33 for why this has to be sync. Otherwise,
+ // there are ordering issues with SendPLayerTransactionConstructor.
+ sync NotifyChildCreated(LayersId id)
+ returns (CompositorOptions compositorOptions);
+
+ // This version of NotifyChildCreated also performs a layer tree mapping.
+ //
+ // See bug 1316632 comment #33 for why this has to be sync. Otherwise,
+ // there are ordering issues with SendPLayerTransactionConstructor.
+ sync MapAndNotifyChildCreated(LayersId id, ProcessId owner)
+ returns (CompositorOptions compositorOptions);
+
+ async AdoptChild(LayersId id);
+
+ // Same as NotifyChildCreated, but used when child processes need to
+ // reassociate layers. This must be synchronous to ensure that the
+ // association happens before PLayerTransactions are sent over the
+ // cross-process bridge.
+ sync NotifyChildRecreated(LayersId id)
+ returns (CompositorOptions compositorOptions);
+
+ async NotifyMemoryPressure();
+
+ // Make sure any pending composites are started immediately and
+ // block until they are completed.
+ sync FlushRendering(RenderReasons aReasons);
+
+ // Same as FlushRendering, but asynchronous, since not all platforms require
+ // synchronous repaints on resize.
+ async FlushRenderingAsync(RenderReasons aReasons);
+
+ // Make sure any pending composites have been received.
+ sync WaitOnTransactionProcessed();
+
+ // Force an additional frame presentation to be executed. This is used to
+ // work around a windows presentation bug (See Bug 1232042)
+ async ForcePresent(RenderReasons aReasons);
+
+ sync StartFrameTimeRecording(int32_t bufferSize)
+ returns (uint32_t startIndex);
+
+ sync StopFrameTimeRecording(uint32_t startIndex)
+ returns (float[] intervals);
+
+ async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend, TextureFlags aTextureFlags, LayersId id, uint64_t aSerial, MaybeExternalImageId aExternalImageId);
+
+ sync SyncWithCompositor();
+
+ // The pipelineId is the same as the layersId
+ async PWebRenderBridge(PipelineId pipelineId, LayoutDeviceIntSize aSize, WindowKind aKind);
+
+ sync CheckContentOnlyTDR(uint32_t sequenceNum)
+ returns (bool isContentOnlyTDR);
+
+ async BeginRecording(TimeStamp aRecordingStart)
+ returns (bool success);
+
+ async EndRecording()
+ returns (FrameRecording? recording);
+
+ // To set up sharing the composited output to Firefox Reality on Desktop
+ async RequestFxrOutput();
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh b/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh
new file mode 100644
index 0000000000..f07a7a3fb0
--- /dev/null
+++ b/gfx/layers/ipc/PCompositorBridgeTypes.ipdlh
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using mozilla::TimeStamp from "mozilla/TimeStamp.h";
+[MoveOnly] using class mozilla::ipc::BigBuffer from "mozilla/ipc/BigBuffer.h";
+
+namespace mozilla {
+namespace layers {
+
+struct RecordedFrameData {
+ TimeStamp timeOffset;
+ uint32_t length;
+};
+
+struct FrameRecording {
+ TimeStamp startTime;
+ RecordedFrameData[] frames;
+ BigBuffer bytes;
+};
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/PCompositorManager.ipdl b/gfx/layers/ipc/PCompositorManager.ipdl
new file mode 100644
index 0000000000..02045511a7
--- /dev/null
+++ b/gfx/layers/ipc/PCompositorManager.ipdl
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PCanvasManager;
+include protocol PCompositorBridge;
+include LayersSurfaces;
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+using mozilla::TimeDuration from "mozilla/TimeStamp.h";
+using mozilla::CSSToLayoutDeviceScale from "Units.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
+[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::layers::CompositorOptions from "mozilla/layers/LayersMessageUtils.h";
+using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MemoryReport from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::WebRenderError from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::layers::SharedSurfacesMemoryReport from "mozilla/layers/SharedSurfacesMemoryReport.h";
+
+namespace mozilla {
+namespace layers {
+
+struct WidgetCompositorOptions {
+ CSSToLayoutDeviceScale scale;
+ TimeDuration vsyncRate;
+ CompositorOptions options;
+ bool useExternalSurfaceSize;
+ IntSize surfaceSize;
+ uint64_t innerWindowId;
+};
+
+struct ContentCompositorOptions {
+};
+
+struct SameProcessWidgetCompositorOptions {
+};
+
+union CompositorBridgeOptions {
+ ContentCompositorOptions;
+ WidgetCompositorOptions;
+ SameProcessWidgetCompositorOptions;
+};
+
+/**
+ * The PCompositorManager protocol is the top-level protocol between the
+ * compositor thread and the creators of compositors. It exists in the
+ * following conditions:
+ * - One PCompositorManager between the GPU process and each content process.
+ * If the GPU/UI processes are combined, there is one PCompositorManager
+ * between the combined GPU/UI process and each content process.
+ * - One PCompositorManager between the GPU process and the UI process. If
+ * they are combined, there is still one PCompositorManager, but both the
+ * child and parent live in the same process.
+ * The intention for this protocol is to facilitate communication with the
+ * compositor thread for compositor data that is only shared once, rather than
+ * per PCompositorBridge instance.
+ */
+[NeedsOtherPid, ParentProc=compositor, ChildProc=anydom]
+sync protocol PCompositorManager
+{
+ manages PCompositorBridge;
+
+parent:
+ /**
+ * There are three variants of a PCompositorBridge protocol, each of which can
+ * only be created by certain processes and configurations:
+ * - A "content" PCompositorBridge is requested by each content process,
+ * representing the drawable area for Web content.
+ * - A "widget" PCompositorBridge is requested by the UI process for each
+ * "top level browser window" for chrome and such.
+ * - A "same process widget" PCompositorBridge is requested by the combined
+ * GPU/UI process for each "top level browser window" as above.
+ * See gfx/layers/ipc/PCompositorBridge.ipdl for more details.
+ */
+ async PCompositorBridge(CompositorBridgeOptions options);
+
+ async AddSharedSurface(ExternalImageId aId, SurfaceDescriptorShared aDesc);
+ async RemoveSharedSurface(ExternalImageId aId);
+ async ReportSharedSurfacesMemory() returns (SharedSurfacesMemoryReport aReport);
+
+ async NotifyMemoryPressure();
+
+ async ReportMemory() returns (MemoryReport aReport);
+
+ async InitCanvasManager(Endpoint<PCanvasManagerParent> endpoint);
+
+child:
+ async NotifyWebRenderError(WebRenderError error);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PImageBridge.ipdl b/gfx/layers/ipc/PImageBridge.ipdl
new file mode 100644
index 0000000000..8af889d8ac
--- /dev/null
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PTexture;
+include ProtocolTypes;
+include protocol PMediaSystemResourceManager;
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PImageBridge protocol is used to allow isolated threads or processes to push
+ * frames directly to the compositor thread/process without relying on the main thread
+ * which might be too busy dealing with content script.
+ */
+[NeedsOtherPid, ParentProc=compositor, ChildProc=anydom]
+sync protocol PImageBridge
+{
+ manages PTexture;
+ manages PMediaSystemResourceManager;
+
+child:
+ async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
+
+ async DidComposite(ImageCompositeNotification[] aNotifications);
+
+ // Report the number of frames dropped for the given CompositableHost.
+ async ReportFramesDropped(CompositableHandle aHandle, uint32_t aFrames);
+
+parent:
+ async Update(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId);
+
+ // First step of the destruction sequence. This puts ImageBridge
+ // in a state in which it can't send asynchronous messages
+ // so as to not race with the channel getting closed.
+ // In the child side, the Closing the channel does not happen right after WillClose,
+ // it is scheduled in the ImageBridgeChild's message queue in order to ensure
+ // that all of the messages from the parent side have been received and processed
+ // before sending closing the channel.
+ async WillClose();
+
+ async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t aSerial, MaybeExternalImageId aExternalImageId);
+ async PMediaSystemResourceManager();
+
+ sync NewCompositable(CompositableHandle aHandle, TextureInfo aInfo);
+ async ReleaseCompositable(CompositableHandle aHandle);
+};
+
+
+} // namespace
+} // namespace
+
diff --git a/gfx/layers/ipc/PTexture.ipdl b/gfx/layers/ipc/PTexture.ipdl
new file mode 100644
index 0000000000..ad6cf8fc13
--- /dev/null
+++ b/gfx/layers/ipc/PTexture.ipdl
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include protocol PCompositorBridge;
+include protocol PImageBridge;
+include protocol PVideoBridge;
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PTexture is the IPDL glue between a TextureClient and a TextureHost.
+ */
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+sync protocol PTexture {
+ manager PImageBridge or PCompositorBridge or PVideoBridge;
+
+child:
+ async __delete__();
+
+parent:
+ /**
+ * Asynchronously tell the compositor side to remove the texture.
+ */
+ async Destroy();
+
+ async RecycleTexture(TextureFlags aTextureFlags);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PUiCompositorController.ipdl b/gfx/layers/ipc/PUiCompositorController.ipdl
new file mode 100644
index 0000000000..5aa75bf7da
--- /dev/null
+++ b/gfx/layers/ipc/PUiCompositorController.ipdl
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using mozilla::CSSRect from "Units.h";
+using mozilla::CSSToScreenScale from "Units.h";
+using mozilla::ScreenIntSize from "Units.h";
+using mozilla::ScreenPoint from "Units.h";
+
+include "mozilla/GfxMessageUtils.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PUiCompositorController protocol is used to pause and resume the
+ * compositor from the UI thread. Primarily used on Android to coordinate registering and
+ * releasing the surface with the compositor.
+ */
+[NeedsOtherPid, ParentProc=compositor, ChildProc=Parent]
+sync protocol PUiCompositorController
+{
+
+parent:
+ // Pause/resume the compositor. These are intended to be used on mobile, when
+ // the compositor needs to pause/resume in lockstep with the application.
+ sync Pause();
+ sync Resume() returns (bool aOutResumed);
+ sync ResumeAndResize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight)
+ returns (bool aOutResumed);
+
+ async InvalidateAndRender();
+ async MaxToolbarHeight(int32_t aHeight);
+ async FixedBottomOffset(int32_t aOffset);
+ async DefaultClearColor(uint32_t aColor);
+ async RequestScreenPixels();
+ async EnableLayerUpdateNotifications(bool aEnable);
+child:
+ async ToolbarAnimatorMessageFromCompositor(int32_t aMessage);
+ async RootFrameMetrics(ScreenPoint aScrollOffset, CSSToScreenScale aZoom);
+ async ScreenPixels(Shmem aMem, ScreenIntSize aSize, bool aNeedsYFlip);
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/PVideoBridge.ipdl b/gfx/layers/ipc/PVideoBridge.ipdl
new file mode 100644
index 0000000000..eb9d7dafee
--- /dev/null
+++ b/gfx/layers/ipc/PVideoBridge.ipdl
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PTexture;
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+
+using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
+using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+using mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The PVideoBridge protocol is used to share textures from the video decoders
+ * to the compositor.
+ */
+[NeedsOtherPid, ParentProc=compositor, ChildProc=any]
+sync protocol PVideoBridge
+{
+ manages PTexture;
+
+child:
+ async Ping() returns (void_t ok);
+
+parent:
+ /**
+ * Since PVideoBridge creates textures on behalf of another process, we also
+ * supply ContentParentId of the owning content process.
+ */
+ async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend,
+ TextureFlags aTextureFlags, ContentParentId aContentId, uint64_t aSerial);
+};
+
+} // namespace
+} // namespace
+
diff --git a/gfx/layers/ipc/PWebRenderBridge.ipdl b/gfx/layers/ipc/PWebRenderBridge.ipdl
new file mode 100644
index 0000000000..8e5c56b7cb
--- /dev/null
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include LayersSurfaces;
+include LayersMessages;
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+include WebRenderMessages;
+include protocol PCompositorBridge;
+include protocol PTexture;
+
+using mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
+using mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
+using mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositionPayload from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::RenderReasons from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MaybeIdNamespace from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::ExternalImageKeyPair from "mozilla/webrender/WebRenderTypes.h";
+[MoveOnly] using mozilla::layers::DisplayListData from "mozilla/layers/RenderRootTypes.h";
+[MoveOnly] using mozilla::layers::MaybeTransactionData from "mozilla/layers/RenderRootTypes.h";
+using mozilla::layers::FocusTarget from "mozilla/layers/FocusTarget.h";
+using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
+using mozilla::VsyncId from "mozilla/VsyncDispatcher.h";
+
+namespace mozilla {
+namespace layers {
+
+[ManualDealloc, ParentImpl=virtual]
+sync protocol PWebRenderBridge
+{
+ manager PCompositorBridge;
+
+parent:
+ sync EnsureConnected()
+ returns (TextureFactoryIdentifier textureFactoryIdentifier, MaybeIdNamespace maybeIdNamespace, nsCString error);
+
+ async NewCompositable(CompositableHandle handle, TextureInfo info);
+ async ReleaseCompositable(CompositableHandle compositable);
+
+ async DeleteCompositorAnimations(uint64_t[] aIds);
+ async SetDisplayList(DisplayListData displayList,
+ OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
+ bool containsSVGGroup,
+ VsyncId vsyncId, TimeStamp vsyncStartTime,
+ TimeStamp refreshStartTime, TimeStamp txnStartTime, nsCString txnURL, TimeStamp fwdTime,
+ CompositionPayload[] payloads);
+ async EmptyTransaction(FocusTarget focusTarget,
+ MaybeTransactionData transationData,
+ OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
+ VsyncId vsyncId, TimeStamp vsyncStartTime,
+ TimeStamp refreshStartTime, TimeStamp txnStartTime,
+ nsCString txnURL, TimeStamp fwdTime,
+ CompositionPayload[] payloads);
+ async SetFocusTarget(FocusTarget focusTarget);
+ async UpdateResources(IdNamespace aIdNamespace, OpUpdateResource[] aResourceUpdates,
+ RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems);
+ async ParentCommands(IdNamespace aIdNamespace, WebRenderParentCommand[] commands);
+ sync GetSnapshot(PTexture texture) returns (bool aNeedsYFlip);
+ async ClearCachedResources();
+ async ClearAnimationResources();
+ async SetDefaultClearColor(uint32_t aColor);
+ // Invalidate rendered frame
+ async InvalidateRenderedFrame();
+ // Schedule a composite if one isn't already scheduled.
+ async ScheduleComposite(RenderReasons aReasons);
+ // Save the frame capture to disk
+ async Capture();
+
+ // Start capturing each frame to disk. See
+ // nsIDOMWindowUtils::wrStartCaptureSequence for documentation.
+ async StartCaptureSequence(nsCString aPath, uint32_t aFlags);
+
+ // Stop the captures started by StartCaptureSequence. See
+ // nsIDOMWindowUtils::wrStopCaptureSequence for documentation.
+ async StopCaptureSequence();
+
+ // Replacement for PCompositorBridge::SyncWithCompositor, but for WR. We need
+ // it on PWebRenderBridge because it associated with a particular top-level
+ // window, and PCompositorBridge doesn't allow doing that in a secure manner.
+ sync SyncWithCompositor();
+
+ // Tell the compositor to notify APZ that a target scroll frame has been
+ // confirmed for an input event.
+ async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
+
+ // Testing APIs
+
+ // Enter test mode, set the sample time to sampleTime, and resample
+ // animations. sampleTime must not be null.
+ sync SetTestSampleTime(TimeStamp sampleTime);
+ // Leave test mode and resume normal compositing
+ sync LeaveTestMode();
+
+ // Returns the |OMTAValue| for the compositor animation with the given id.
+ sync GetAnimationValue(uint64_t aCompositorAnimationsId) returns (OMTAValue value);
+
+ // The next time the display list tree is composited, add this async scroll offset
+ // in CSS pixels for the given ViewID.
+ // Useful for testing rendering of async scrolling.
+ sync SetAsyncScrollOffset(ViewID scrollId, float x, float y);
+
+ // The next time the display list is composited, include this async zoom in
+ // for the given ViewID.
+ // Useful for testing rendering of async zooming.
+ sync SetAsyncZoom(ViewID scrollId, float zoom);
+
+ // Flush any pending APZ repaints to the main thread.
+ async FlushApzRepaints();
+
+ // Get a copy of the compositor-side APZ test data instance for this
+ // layers id.
+ sync GetAPZTestData() returns (APZTestData data);
+
+ // Child requests frame uniformity measurements
+ sync GetFrameUniformity() returns (FrameUniformityData data);
+
+
+ async Shutdown();
+ sync ShutdownSync();
+child:
+ async WrUpdated(IdNamespace aNewIdNamespace, TextureFactoryIdentifier textureFactoryIdentifier);
+ async WrReleasedImages(ExternalImageKeyPair[] pairs);
+ async __delete__();
+};
+
+} // layers
+} // mozilla
diff --git a/gfx/layers/ipc/RefCountedShmem.cpp b/gfx/layers/ipc/RefCountedShmem.cpp
new file mode 100644
index 0000000000..8eec82dc9a
--- /dev/null
+++ b/gfx/layers/ipc/RefCountedShmem.cpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RefCountedShmem.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/WebRenderMessages.h"
+
+#define SHM_REFCOUNT_HEADER_SIZE 16
+
+namespace mozilla {
+namespace layers {
+
+uint8_t* RefCountedShm::GetBytes(const RefCountedShmem& aShm) {
+ uint8_t* data = aShm.buffer().get<uint8_t>();
+ if (!data) {
+ return nullptr;
+ }
+ return data + SHM_REFCOUNT_HEADER_SIZE;
+}
+
+size_t RefCountedShm::GetSize(const RefCountedShmem& aShm) {
+ if (!IsValid(aShm)) {
+ return 0;
+ }
+ size_t totalSize = aShm.buffer().Size<uint8_t>();
+ if (totalSize < SHM_REFCOUNT_HEADER_SIZE) {
+ return 0;
+ }
+
+ return totalSize - SHM_REFCOUNT_HEADER_SIZE;
+}
+
+bool RefCountedShm::IsValid(const RefCountedShmem& aShm) {
+ return aShm.buffer().IsWritable() &&
+ aShm.buffer().Size<uint8_t>() > SHM_REFCOUNT_HEADER_SIZE;
+}
+
+int32_t RefCountedShm::GetReferenceCount(const RefCountedShmem& aShm) {
+ if (!IsValid(aShm)) {
+ return 0;
+ }
+
+ return *aShm.buffer().get<Atomic<int32_t>>();
+}
+
+int32_t RefCountedShm::AddRef(const RefCountedShmem& aShm) {
+ if (!IsValid(aShm)) {
+ return 0;
+ }
+
+ auto* counter = aShm.buffer().get<Atomic<int32_t>>();
+ if (counter) {
+ return (*counter)++;
+ }
+ return 0;
+}
+
+int32_t RefCountedShm::Release(const RefCountedShmem& aShm) {
+ if (!IsValid(aShm)) {
+ return 0;
+ }
+
+ auto* counter = aShm.buffer().get<Atomic<int32_t>>();
+ if (counter) {
+ return --(*counter);
+ }
+
+ return 0;
+}
+
+bool RefCountedShm::Alloc(mozilla::ipc::IProtocol* aAllocator, size_t aSize,
+ RefCountedShmem& aShm) {
+ MOZ_ASSERT(!IsValid(aShm));
+ auto size = aSize + SHM_REFCOUNT_HEADER_SIZE;
+ if (!aAllocator->AllocUnsafeShmem(size, &aShm.buffer())) {
+ return false;
+ }
+ return true;
+}
+
+void RefCountedShm::Dealloc(mozilla::ipc::IProtocol* aAllocator,
+ RefCountedShmem& aShm) {
+ aAllocator->DeallocShmem(aShm.buffer());
+ aShm.buffer() = ipc::Shmem();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/RefCountedShmem.h b/gfx/layers/ipc/RefCountedShmem.h
new file mode 100644
index 0000000000..390d6ea25f
--- /dev/null
+++ b/gfx/layers/ipc/RefCountedShmem.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_REFCOUNTED_SHMEM_H
+#define MOZILLA_LAYERS_REFCOUNTED_SHMEM_H
+
+#include "mozilla/ipc/Shmem.h"
+#include "chrome/common/ipc_message_utils.h"
+
+namespace mozilla {
+namespace ipc {
+class IProtocol;
+}
+
+namespace layers {
+
+// This class is IPDL-defined
+class RefCountedShmem;
+
+// This just implement the methods externally.
+class RefCountedShm {
+ public:
+ static uint8_t* GetBytes(const RefCountedShmem& aShm);
+
+ static size_t GetSize(const RefCountedShmem& aShm);
+
+ static bool IsValid(const RefCountedShmem& aShm);
+
+ static bool Alloc(mozilla::ipc::IProtocol* aAllocator, size_t aSize,
+ RefCountedShmem& aShm);
+
+ static void Dealloc(mozilla::ipc::IProtocol* aAllocator,
+ RefCountedShmem& aShm);
+
+ static int32_t GetReferenceCount(const RefCountedShmem& aShm);
+
+ static int32_t AddRef(const RefCountedShmem& aShm);
+
+ static int32_t Release(const RefCountedShmem& aShm);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/RemoteContentController.cpp b/gfx/layers/ipc/RemoteContentController.cpp
new file mode 100644
index 0000000000..682331362b
--- /dev/null
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -0,0 +1,526 @@
+/* -*- 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/DoubleTapToZoom.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,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
+ 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,
+ aDoubleTapToZoomMetrics);
+ }
+}
+
+void RemoteContentController::HandleTapOnCompositorThread(
+ TapType aTapType, LayoutDevicePoint aPoint, Modifiers aModifiers,
+ ScrollableLayerGuid aGuid, uint64_t aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
+ 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, aDoubleTapToZoomMetrics);
+ }
+}
+
+void RemoteContentController::HandleTap(
+ TapType aTapType, const LayoutDevicePoint& aPoint, Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
+ 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, aDoubleTapToZoomMetrics);
+ } else {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers,
+ ScrollableLayerGuid, uint64_t,
+ Maybe<DoubleTapToZoomMetrics>>(
+ "layers::RemoteContentController::HandleTapOnCompositorThread",
+ this, &RemoteContentController::HandleTapOnCompositorThread,
+ aTapType, aPoint, aModifiers, aGuid, aInputBlockId,
+ aDoubleTapToZoomMetrics));
+ }
+ return;
+ }
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (NS_IsMainThread()) {
+ HandleTapOnMainThread(aTapType, aPoint, aModifiers, aGuid, aInputBlockId,
+ aDoubleTapToZoomMetrics);
+ } 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,
+ Maybe<DoubleTapToZoomMetrics>>(
+ "layers::RemoteContentController::HandleTapOnMainThread", this,
+ &RemoteContentController::HandleTapOnMainThread, aTapType, aPoint,
+ aModifiers, aGuid, aInputBlockId, aDoubleTapToZoomMetrics));
+#endif
+ }
+}
+
+void RemoteContentController::NotifyPinchGestureOnCompositorThread(
+ PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) {
+ MOZ_ASSERT(mCompositorThread->IsOnCurrentThread());
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on
+ // the compositor thread.
+ APZCTreeManagerParent* apzctmp =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId);
+ if (apzctmp) {
+ Unused << apzctmp->SendNotifyPinchGesture(aType, aGuid, aFocusPoint,
+ aSpanChange, aModifiers);
+ }
+}
+
+void RemoteContentController::NotifyPinchGesture(
+ PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint, LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) {
+ APZThreadUtils::AssertOnControllerThread();
+
+ // For now we only ever want to handle this NotifyPinchGesture message in
+ // the parent process, even if the APZ is sending it to a content process.
+
+ // If we're in the GPU process, try to find a handle to the parent process
+ // and send it there.
+ if (XRE_IsGPUProcess()) {
+ if (mCompositorThread->IsOnCurrentThread()) {
+ NotifyPinchGestureOnCompositorThread(aType, aGuid, aFocusPoint,
+ aSpanChange, aModifiers);
+ } else {
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<PinchGestureInput::PinchGestureType,
+ ScrollableLayerGuid, LayoutDevicePoint,
+ LayoutDeviceCoord, Modifiers>(
+ "layers::RemoteContentController::"
+ "NotifyPinchGestureOnCompositorThread",
+ this,
+ &RemoteContentController::NotifyPinchGestureOnCompositorThread,
+ aType, aGuid, aFocusPoint, aSpanChange, aModifiers));
+ }
+ return;
+ }
+
+ // If we're in the parent process, handle it directly. We don't have a
+ // handle to the widget though, so we fish out the ChromeProcessController
+ // and delegate to that instead.
+ if (XRE_IsParentProcess()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<GeckoContentController> rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ aGuid.mLayersId);
+ if (rootController) {
+ rootController->NotifyPinchGesture(aType, aGuid, aFocusPoint, aSpanChange,
+ aModifiers);
+ }
+ }
+}
+
+bool RemoteContentController::IsRepaintThread() {
+ return mCompositorThread->IsOnCurrentThread();
+}
+
+void RemoteContentController::DispatchToRepaintThread(
+ already_AddRefed<Runnable> aTask) {
+ mCompositorThread->Dispatch(std::move(aTask));
+}
+
+void RemoteContentController::NotifyAPZStateChange(
+ const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg,
+ Maybe<uint64_t> aInputBlockId) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<ScrollableLayerGuid, APZStateChange, int,
+ Maybe<uint64_t>>(
+ "layers::RemoteContentController::NotifyAPZStateChange", this,
+ &RemoteContentController::NotifyAPZStateChange, aGuid, aChange,
+ aArg, aInputBlockId));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAPZStateChange(aGuid, aChange, aArg, aInputBlockId);
+ }
+}
+
+void RemoteContentController::UpdateOverscrollVelocity(
+ const ScrollableLayerGuid& aGuid, float aX, float aY, bool aIsRootContent) {
+ if (XRE_IsParentProcess()) {
+#ifdef MOZ_WIDGET_ANDROID
+ // We always want these to go to the parent process on Android
+ if (!NS_IsMainThread()) {
+ mozilla::jni::DispatchToGeckoPriorityQueue(
+ NewRunnableMethod<ScrollableLayerGuid, float, float, bool>(
+ "layers::RemoteContentController::UpdateOverscrollVelocity", this,
+ &RemoteContentController::UpdateOverscrollVelocity, aGuid, aX, aY,
+ aIsRootContent));
+ return;
+ }
+#endif
+
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ RefPtr<GeckoContentController> rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ aGuid.mLayersId);
+ if (rootController) {
+ rootController->UpdateOverscrollVelocity(aGuid, aX, aY, aIsRootContent);
+ }
+ } else if (XRE_IsGPUProcess()) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<ScrollableLayerGuid, float, float, bool>(
+ "layers::RemoteContentController::UpdateOverscrollVelocity", this,
+ &RemoteContentController::UpdateOverscrollVelocity, aGuid, aX, aY,
+ aIsRootContent));
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(mCompositorThread->IsOnCurrentThread());
+ GeckoContentController* rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ aGuid.mLayersId);
+ if (rootController) {
+ MOZ_RELEASE_ASSERT(rootController->IsRemote());
+ Unused << static_cast<RemoteContentController*>(rootController)
+ ->SendUpdateOverscrollVelocity(aGuid, aX, aY,
+ aIsRootContent);
+ }
+ }
+}
+
+void RemoteContentController::UpdateOverscrollOffset(
+ const ScrollableLayerGuid& aGuid, float aX, float aY, bool aIsRootContent) {
+ if (XRE_IsParentProcess()) {
+#ifdef MOZ_WIDGET_ANDROID
+ // We always want these to go to the parent process on Android
+ if (!NS_IsMainThread()) {
+ mozilla::jni::DispatchToGeckoPriorityQueue(
+ NewRunnableMethod<ScrollableLayerGuid, float, float, bool>(
+ "layers::RemoteContentController::UpdateOverscrollOffset", this,
+ &RemoteContentController::UpdateOverscrollOffset, aGuid, aX, aY,
+ aIsRootContent));
+ return;
+ }
+#endif
+
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ RefPtr<GeckoContentController> rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ aGuid.mLayersId);
+ if (rootController) {
+ rootController->UpdateOverscrollOffset(aGuid, aX, aY, aIsRootContent);
+ }
+ } else if (XRE_IsGPUProcess()) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<ScrollableLayerGuid, float, float, bool>(
+ "layers::RemoteContentController::UpdateOverscrollOffset", this,
+ &RemoteContentController::UpdateOverscrollOffset, aGuid, aX, aY,
+ aIsRootContent));
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(mCompositorThread->IsOnCurrentThread());
+ GeckoContentController* rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(
+ aGuid.mLayersId);
+ if (rootController) {
+ MOZ_RELEASE_ASSERT(rootController->IsRemote());
+ Unused << static_cast<RemoteContentController*>(rootController)
+ ->SendUpdateOverscrollOffset(aGuid, aX, aY, aIsRootContent);
+ }
+ }
+}
+
+void RemoteContentController::NotifyMozMouseScrollEvent(
+ const ScrollableLayerGuid::ViewID& aScrollId, const nsString& aEvent) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<ScrollableLayerGuid::ViewID, nsString>(
+ "layers::RemoteContentController::NotifyMozMouseScrollEvent", this,
+ &RemoteContentController::NotifyMozMouseScrollEvent, aScrollId,
+ aEvent));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyMozMouseScrollEvent(aScrollId, aEvent);
+ }
+}
+
+void RemoteContentController::NotifyFlushComplete() {
+ MOZ_ASSERT(IsRepaintThread());
+
+ if (mCanSend) {
+ Unused << SendNotifyFlushComplete();
+ }
+}
+
+void RemoteContentController::NotifyAsyncScrollbarDragInitiated(
+ uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId,
+ ScrollDirection aDirection) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(
+ NewRunnableMethod<uint64_t, ScrollableLayerGuid::ViewID,
+ ScrollDirection>(
+ "layers::RemoteContentController::"
+ "NotifyAsyncScrollbarDragInitiated",
+ this, &RemoteContentController::NotifyAsyncScrollbarDragInitiated,
+ aDragBlockId, aScrollId, aDirection));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAsyncScrollbarDragInitiated(aDragBlockId, aScrollId,
+ aDirection);
+ }
+}
+
+void RemoteContentController::NotifyAsyncScrollbarDragRejected(
+ const ScrollableLayerGuid::ViewID& aScrollId) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
+ "layers::RemoteContentController::NotifyAsyncScrollbarDragRejected",
+ this, &RemoteContentController::NotifyAsyncScrollbarDragRejected,
+ aScrollId));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAsyncScrollbarDragRejected(aScrollId);
+ }
+}
+
+void RemoteContentController::NotifyAsyncAutoscrollRejected(
+ const ScrollableLayerGuid::ViewID& aScrollId) {
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ // We have to send messages from the compositor thread
+ mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid::ViewID>(
+ "layers::RemoteContentController::NotifyAsyncAutoscrollRejected", this,
+ &RemoteContentController::NotifyAsyncAutoscrollRejected, aScrollId));
+ return;
+ }
+
+ if (mCanSend) {
+ Unused << SendNotifyAsyncAutoscrollRejected(aScrollId);
+ }
+}
+
+void RemoteContentController::CancelAutoscroll(
+ const ScrollableLayerGuid& aGuid) {
+ if (XRE_GetProcessType() == GeckoProcessType_GPU) {
+ CancelAutoscrollCrossProcess(aGuid);
+ } else {
+ CancelAutoscrollInProcess(aGuid);
+ }
+}
+
+void RemoteContentController::CancelAutoscrollInProcess(
+ const ScrollableLayerGuid& aGuid) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(NewRunnableMethod<ScrollableLayerGuid>(
+ "layers::RemoteContentController::CancelAutoscrollInProcess", this,
+ &RemoteContentController::CancelAutoscrollInProcess, aGuid));
+ return;
+ }
+
+ APZCCallbackHelper::CancelAutoscroll(aGuid.mScrollId);
+}
+
+void RemoteContentController::CancelAutoscrollCrossProcess(
+ const ScrollableLayerGuid& aGuid) {
+ MOZ_ASSERT(XRE_IsGPUProcess());
+
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid>(
+ "layers::RemoteContentController::CancelAutoscrollCrossProcess", this,
+ &RemoteContentController::CancelAutoscrollCrossProcess, aGuid));
+ return;
+ }
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on
+ // the compositor thread.
+ if (APZCTreeManagerParent* parent =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(
+ aGuid.mLayersId)) {
+ Unused << parent->SendCancelAutoscroll(aGuid.mScrollId);
+ }
+}
+
+void RemoteContentController::NotifyScaleGestureComplete(
+ const ScrollableLayerGuid& aGuid, float aScale) {
+ if (XRE_GetProcessType() == GeckoProcessType_GPU) {
+ NotifyScaleGestureCompleteCrossProcess(aGuid, aScale);
+ } else {
+ NotifyScaleGestureCompleteInProcess(aGuid, aScale);
+ }
+}
+
+void RemoteContentController::NotifyScaleGestureCompleteInProcess(
+ const ScrollableLayerGuid& aGuid, float aScale) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(NewRunnableMethod<ScrollableLayerGuid, float>(
+ "layers::RemoteContentController::"
+ "NotifyScaleGestureCompleteInProcess",
+ this, &RemoteContentController::NotifyScaleGestureCompleteInProcess,
+ aGuid, aScale));
+ return;
+ }
+
+ RefPtr<GeckoContentController> rootController =
+ CompositorBridgeParent::GetGeckoContentControllerForRoot(aGuid.mLayersId);
+ if (rootController) {
+ MOZ_ASSERT(rootController != this);
+ if (rootController != this) {
+ rootController->NotifyScaleGestureComplete(aGuid, aScale);
+ }
+ }
+}
+
+void RemoteContentController::NotifyScaleGestureCompleteCrossProcess(
+ const ScrollableLayerGuid& aGuid, float aScale) {
+ MOZ_ASSERT(XRE_IsGPUProcess());
+
+ if (!mCompositorThread->IsOnCurrentThread()) {
+ mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, float>(
+ "layers::RemoteContentController::"
+ "NotifyScaleGestureCompleteCrossProcess",
+ this, &RemoteContentController::NotifyScaleGestureCompleteCrossProcess,
+ aGuid, aScale));
+ return;
+ }
+
+ // The raw pointer to APZCTreeManagerParent is ok here because we are on
+ // the compositor thread.
+ if (APZCTreeManagerParent* parent =
+ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(
+ aGuid.mLayersId)) {
+ Unused << parent->SendNotifyScaleGestureComplete(aGuid.mScrollId, aScale);
+ }
+}
+
+void RemoteContentController::ActorDestroy(ActorDestroyReason aWhy) {
+ // This controller could possibly be kept alive longer after this
+ // by a RefPtr, but it is no longer valid to send messages.
+ mCanSend = false;
+}
+
+void RemoteContentController::Destroy() {
+ if (mCanSend) {
+ mCanSend = false;
+ Unused << SendDestroy();
+ }
+}
+
+mozilla::ipc::IPCResult RemoteContentController::RecvDestroy() {
+ // The actor on the other side is about to get destroyed, so let's not
+ // send it any more messages.
+ mCanSend = false;
+ return IPC_OK();
+}
+
+bool RemoteContentController::IsRemote() { return true; }
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/RemoteContentController.h b/gfx/layers/ipc/RemoteContentController.h
new file mode 100644
index 0000000000..7c2ef3789f
--- /dev/null
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -0,0 +1,126 @@
+/* -*- 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 {
+
+struct DoubleTapToZoomMetrics;
+/**
+ * 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,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) override;
+
+ void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid,
+ const LayoutDevicePoint& aFocusPoint,
+ LayoutDeviceCoord aSpanChange,
+ Modifiers aModifiers) override;
+
+ bool IsRepaintThread() override;
+
+ void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
+
+ void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+ APZStateChange aChange, int aArg,
+ Maybe<uint64_t> aInputBlockId) override;
+
+ void UpdateOverscrollVelocity(const ScrollableLayerGuid& aGuid, float aX,
+ float aY, bool aIsRootContent) override;
+
+ void UpdateOverscrollOffset(const ScrollableLayerGuid& aGuid, float aX,
+ float aY, bool aIsRootContent) override;
+
+ void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId,
+ const nsString& aEvent) override;
+
+ void NotifyFlushComplete() override;
+
+ void NotifyAsyncScrollbarDragInitiated(
+ uint64_t aDragBlockId, const ScrollableLayerGuid::ViewID& aScrollId,
+ ScrollDirection aDirection) override;
+ void NotifyAsyncScrollbarDragRejected(
+ const ScrollableLayerGuid::ViewID& aScrollId) override;
+
+ void NotifyAsyncAutoscrollRejected(
+ const ScrollableLayerGuid::ViewID& aScrollId) override;
+
+ void CancelAutoscroll(const ScrollableLayerGuid& aScrollId) override;
+
+ void NotifyScaleGestureComplete(const ScrollableLayerGuid& aGuid,
+ float aScale) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ void Destroy() override;
+ mozilla::ipc::IPCResult RecvDestroy();
+
+ bool IsRemote() override;
+
+ private:
+ nsCOMPtr<nsISerialEventTarget> mCompositorThread;
+ bool mCanSend;
+
+ void HandleTapOnMainThread(
+ TapType aType, LayoutDevicePoint aPoint, Modifiers aModifiers,
+ ScrollableLayerGuid aGuid, uint64_t aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics);
+ void HandleTapOnCompositorThread(
+ TapType aType, LayoutDevicePoint aPoint, Modifiers aModifiers,
+ ScrollableLayerGuid aGuid, uint64_t aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics);
+ void NotifyPinchGestureOnCompositorThread(
+ PinchGestureInput::PinchGestureType aType,
+ const ScrollableLayerGuid& aGuid, const LayoutDevicePoint& aFocusPoint,
+ LayoutDeviceCoord aSpanChange, Modifiers aModifiers);
+
+ void CancelAutoscrollInProcess(const ScrollableLayerGuid& aScrollId);
+ void CancelAutoscrollCrossProcess(const ScrollableLayerGuid& aScrollId);
+ void NotifyScaleGestureCompleteInProcess(const ScrollableLayerGuid& aGuid,
+ float aScale);
+ void NotifyScaleGestureCompleteCrossProcess(const ScrollableLayerGuid& aGuid,
+ float aScale);
+};
+
+} // namespace layers
+
+} // namespace mozilla
+
+#endif // mozilla_layers_RemoteContentController_h
diff --git a/gfx/layers/ipc/ShadowLayerUtils.h b/gfx/layers/ipc/ShadowLayerUtils.h
new file mode 100644
index 0000000000..de07854b04
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtils.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_ShadowLayerUtils_h
+#define IPC_ShadowLayerUtils_h
+
+#include "ipc/EnumSerializer.h"
+#include "ipc/IPCMessageUtils.h"
+#include "GLContextTypes.h"
+#include "SurfaceDescriptor.h"
+#include "SurfaceTypes.h"
+#include "mozilla/WidgetUtils.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::ScreenRotation>
+ : public ContiguousEnumSerializer<mozilla::ScreenRotation,
+ mozilla::ROTATION_0,
+ mozilla::ROTATION_COUNT> {};
+
+} // namespace IPC
+
+#endif // IPC_ShadowLayerUtils_h
diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
new file mode 100644
index 0000000000..0757d1e22b
--- /dev/null
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -0,0 +1,193 @@
+/* -*- 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();
+}
+
+nsresult SharedPlanarYCbCrImage::CopyData(const PlanarYCbCrData& aData) {
+ // If mTextureClient has not already been allocated by CreateEmptyBuffer,
+ // allocate it. This code path is slower than the one used when
+ // CreateEmptyBuffer has been called since it will trigger a full copy.
+ if (!mTextureClient) {
+ nsresult r =
+ CreateEmptyBuffer(aData, aData.YDataSize(), aData.CbCrDataSize());
+ if (NS_FAILED(r)) {
+ return r;
+ }
+ }
+
+ TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ MOZ_ASSERT(false, "Failed to lock the texture.");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!UpdateYCbCrTextureClient(mTextureClient, aData)) {
+ MOZ_ASSERT(false, "Failed to copy YCbCr data into the TextureClient");
+ return NS_ERROR_UNEXPECTED;
+ }
+ mTextureClient->MarkImmutable();
+ return NS_OK;
+}
+
+nsresult SharedPlanarYCbCrImage::AdoptData(const Data& aData) {
+ MOZ_ASSERT(false, "This shouldn't be used.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+bool SharedPlanarYCbCrImage::IsValid() const {
+ return mTextureClient && mTextureClient->IsValid();
+}
+
+nsresult SharedPlanarYCbCrImage::CreateEmptyBuffer(
+ const PlanarYCbCrData& aData, const gfx::IntSize& aYSize,
+ const gfx::IntSize& aCbCrSize) {
+ MOZ_ASSERT(!mTextureClient, "This image already has allocated data");
+
+ TextureFlags flags =
+ mCompositable ? mCompositable->GetTextureFlags() : TextureFlags::DEFAULT;
+ {
+ YCbCrTextureClientAllocationHelper helper(aData, aYSize, aCbCrSize, flags);
+ Result<already_AddRefed<TextureClient>, nsresult> result =
+ RecycleAllocator()->CreateOrRecycle(helper);
+ if (result.isErr()) {
+ return Err(result.unwrapErr());
+ }
+ mTextureClient = result.unwrap();
+ }
+
+ if (!mTextureClient) {
+ NS_WARNING("SharedPlanarYCbCrImage::Allocate failed.");
+ // TODO: TextureClientRecycleAllocator::CreateOrRecycle may return NULL on
+ // non out-of-memory failures.
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ MappedYCbCrTextureData mapped;
+ // The locking here is sort of a lie. The SharedPlanarYCbCrImage just pulls
+ // pointers out of the TextureClient and keeps them around, which works only
+ // because the underlyin BufferTextureData is always mapped in memory even
+ // outside of the lock/unlock interval. That's sad and new code should follow
+ // this example.
+ if (!mTextureClient->Lock(OpenMode::OPEN_READ) ||
+ !mTextureClient->BorrowMappedYCbCrData(mapped)) {
+ MOZ_CRASH("GFX: Cannot lock or borrow mapped YCbCr");
+ }
+
+ // copy some of aData's values in mData (most of them)
+ mData.mYChannel = mapped.y.data;
+ mData.mCbChannel = mapped.cb.data;
+ mData.mCrChannel = mapped.cr.data;
+ mData.mPictureRect = aData.mPictureRect;
+ mData.mStereoMode = aData.mStereoMode;
+ mData.mYUVColorSpace = aData.mYUVColorSpace;
+ mData.mColorDepth = aData.mColorDepth;
+ mData.mChromaSubsampling = aData.mChromaSubsampling;
+ // those members are not always equal to aData's, due to potentially different
+ // packing.
+ mData.mYSkip = 0;
+ mData.mCbSkip = 0;
+ mData.mCrSkip = 0;
+ mData.mYStride = aData.mYStride;
+ mData.mCbCrStride = aData.mCbCrStride;
+
+ // do not set mBuffer like in PlanarYCbCrImage because the later
+ // will try to manage this memory without knowing it belongs to a
+ // shmem.
+ mBufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(
+ aYSize, mData.mYStride, aCbCrSize, mData.mCbCrStride);
+ mSize = mData.mPictureRect.Size();
+ mOrigin = mData.mPictureRect.TopLeft();
+
+ mTextureClient->Unlock();
+
+ // ImageDataSerializer::ComputeYCbCrBufferSize may return zero when the size
+ // requested is out of the limit.
+ return mBufferSize > 0 ? NS_OK : NS_ERROR_INVALID_ARG;
+}
+
+void SharedPlanarYCbCrImage::SetIsDRM(bool aIsDRM) {
+ Image::SetIsDRM(aIsDRM);
+ if (mTextureClient) {
+ mTextureClient->AddFlags(TextureFlags::DRM_SOURCE);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.h b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
new file mode 100644
index 0000000000..9f6c06df55
--- /dev/null
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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;
+ nsresult CopyData(const PlanarYCbCrData& aData) override;
+ nsresult AdoptData(const Data& aData) override;
+ nsresult CreateEmptyBuffer(const Data& aData, const gfx::IntSize& aYSize,
+ const gfx::IntSize& aCbCrSize) override;
+
+ void SetIsDRM(bool aIsDRM) override;
+ bool IsValid() const override;
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
+
+ TextureClientRecycleAllocator* RecycleAllocator();
+
+ private:
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<ImageClient> mCompositable;
+ RefPtr<TextureClientRecycleAllocator> mRecycleAllocator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/SharedRGBImage.cpp b/gfx/layers/ipc/SharedRGBImage.cpp
new file mode 100644
index 0000000000..beb69ec4eb
--- /dev/null
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedRGBImage.h"
+#include "ImageTypes.h" // for ImageFormat::SHARED_RGB, etc
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "mozilla/gfx/Point.h" // for IntSIze
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator, etc
+#include "mozilla/layers/ImageClient.h" // for ImageClient
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureClient.h" // for BufferTextureClient, etc
+#include "mozilla/layers/TextureClientRecycleAllocator.h" // for ITextureClientAllocationHelper
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::AddRef, etc
+#include "nsProxyRelease.h"
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+// Just big enough for a 1080p RGBA32 frame
+#define MAX_FRAME_SIZE (16 * 1024 * 1024)
+
+namespace mozilla {
+namespace layers {
+
+class TextureClientForRawBufferAccessAllocationHelper
+ : public ITextureClientAllocationHelper {
+ public:
+ TextureClientForRawBufferAccessAllocationHelper(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aTextureFlags)
+ : ITextureClientAllocationHelper(aFormat, aSize, BackendSelector::Content,
+ aTextureFlags, ALLOC_DEFAULT) {}
+
+ bool IsCompatible(TextureClient* aTextureClient) override {
+ bool ret = aTextureClient->GetFormat() == mFormat &&
+ aTextureClient->GetSize() == mSize;
+ return ret;
+ }
+
+ already_AddRefed<TextureClient> Allocate(
+ KnowsCompositor* aAllocator) override {
+ return TextureClient::CreateForRawBufferAccess(
+ aAllocator, mFormat, mSize, gfx::BackendType::NONE, mTextureFlags);
+ }
+};
+
+SharedRGBImage::SharedRGBImage(ImageClient* aCompositable)
+ : Image(nullptr, ImageFormat::SHARED_RGB), mCompositable(aCompositable) {
+ MOZ_COUNT_CTOR(SharedRGBImage);
+}
+
+SharedRGBImage::SharedRGBImage(TextureClientRecycleAllocator* aRecycleAllocator)
+ : Image(nullptr, ImageFormat::SHARED_RGB),
+ mRecycleAllocator(aRecycleAllocator) {
+ MOZ_COUNT_CTOR(SharedRGBImage);
+}
+
+SharedRGBImage::~SharedRGBImage() {
+ MOZ_COUNT_DTOR(SharedRGBImage);
+ NS_ReleaseOnMainThread("SharedRGBImage::mSourceSurface",
+ mSourceSurface.forget());
+}
+
+TextureClientRecycleAllocator* SharedRGBImage::RecycleAllocator() {
+ static const uint32_t MAX_POOLED_VIDEO_COUNT = 5;
+
+ if (!mRecycleAllocator && mCompositable) {
+ if (!mCompositable->HasTextureClientRecycler()) {
+ // Initialize TextureClientRecycler
+ mCompositable->GetTextureClientRecycler()->SetMaxPoolSize(
+ MAX_POOLED_VIDEO_COUNT);
+ }
+ mRecycleAllocator = mCompositable->GetTextureClientRecycler();
+ }
+ return mRecycleAllocator;
+}
+
+bool SharedRGBImage::Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
+ mSize = aSize;
+
+ TextureFlags flags =
+ mCompositable ? mCompositable->GetTextureFlags() : TextureFlags::DEFAULT;
+ {
+ TextureClientForRawBufferAccessAllocationHelper helper(aFormat, aSize,
+ flags);
+ mTextureClient =
+ RecycleAllocator()->CreateOrRecycle(helper).unwrapOr(nullptr);
+ }
+
+ 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..0f8b2a9743
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -0,0 +1,656 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurfacesChild.h"
+#include "CompositorManagerChild.h"
+#include "mozilla/layers/IpcResourceUpdateQueue.h"
+#include "mozilla/layers/SourceSurfaceSharedData.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/layers/RenderRootStateManager.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/StaticPrefs_image.h"
+#include "mozilla/PresShell.h"
+#include "nsRefreshDriver.h"
+#include "nsView.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()
+ : Runnable("SharedSurfacesChild::SharedUserData"),
+ mId({}),
+ 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(data.forget());
+ }
+}
+
+NS_IMETHODIMP SharedSurfacesChild::SharedUserData::Run() {
+ SharedSurfacesChild::Unshare(mId, mShared, mKeys);
+ mShared = false;
+ mKeys.Clear();
+ return NS_OK;
+}
+
+wr::ImageKey SharedSurfacesChild::SharedUserData::UpdateKey(
+ RenderRootStateManager* aManager, wr::IpcResourceUpdateQueue& aResources,
+ const Maybe<IntRect>& aDirtyRect) {
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(!aManager->IsDestroyed());
+
+ // We iterate through all of the items to ensure we clean up the old
+ // RenderRootStateManager references. Most of the time there will be few
+ // entries and this should not be particularly expensive compared to the
+ // cost of duplicating image keys. In an ideal world, we would generate a
+ // single key for the surface, and it would be usable on all of the
+ // renderer instances. For now, we must allocate a key for each WR bridge.
+ wr::ImageKey key;
+ bool found = false;
+ auto i = mKeys.Length();
+ while (i > 0) {
+ --i;
+ ImageKeyData& entry = mKeys[i];
+ if (entry.mManager->IsDestroyed()) {
+ mKeys.RemoveElementAt(i);
+ } else if (entry.mManager == aManager) {
+ WebRenderBridgeChild* wrBridge = aManager->WrBridge();
+ MOZ_ASSERT(wrBridge);
+
+ // Even if the manager is the same, its underlying WebRenderBridgeChild
+ // can change state. If our namespace differs, then our old key has
+ // already been discarded.
+ bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace;
+ if (!ownsKey) {
+ entry.mImageKey = wrBridge->GetNextImageKey();
+ entry.TakeDirtyRect();
+ aResources.AddSharedExternalImage(mId, entry.mImageKey);
+ } else {
+ entry.MergeDirtyRect(aDirtyRect);
+ Maybe<IntRect> dirtyRect = entry.TakeDirtyRect();
+ if (dirtyRect) {
+ MOZ_ASSERT(mShared);
+ aResources.UpdateSharedExternalImage(
+ mId, entry.mImageKey, ViewAs<ImagePixel>(dirtyRect.ref()));
+ }
+ }
+
+ key = entry.mImageKey;
+ found = true;
+ } else {
+ // We don't have the resource update queue for this manager, so just
+ // accumulate the dirty rects until it is requested.
+ entry.MergeDirtyRect(aDirtyRect);
+ }
+ }
+
+ if (!found) {
+ key = aManager->WrBridge()->GetNextImageKey();
+ ImageKeyData data(aManager, key);
+ mKeys.AppendElement(std::move(data));
+ aResources.AddSharedExternalImage(mId, key);
+ }
+
+ return key;
+}
+
+/* static */
+SourceSurfaceSharedData* SharedSurfacesChild::AsSourceSurfaceSharedData(
+ SourceSurface* aSurface) {
+ MOZ_ASSERT(aSurface);
+ switch (aSurface->GetType()) {
+ case SurfaceType::DATA_SHARED:
+ case SurfaceType::DATA_RECYCLING_SHARED:
+ return static_cast<SourceSurfaceSharedData*>(aSurface);
+ default:
+ return nullptr;
+ }
+}
+
+/* static */
+nsresult SharedSurfacesChild::ShareInternal(SourceSurfaceSharedData* aSurface,
+ SharedUserData** aUserData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+ MOZ_ASSERT(aUserData);
+
+ CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
+ if (NS_WARN_IF(!manager || !manager->CanSend())) {
+ // We cannot try to share the surface, most likely because the GPU process
+ // crashed. Ideally, we would retry when it is ready, but the handles may be
+ // a scarce resource, which can cause much more serious problems if we run
+ // out. Better to copy into a fresh buffer later.
+ aSurface->FinishedSharing();
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ SharedUserData* data =
+ static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey));
+ if (!data) {
+ data = MakeAndAddRef<SharedUserData>().take();
+ aSurface->AddUserData(&sSharedKey, data, SharedUserData::Destroy);
+ } else if (data->IsShared()) {
+ if (manager->OwnsExternalImageId(data->Id())) {
+ // It has already been shared with the GPU process.
+ *aUserData = data;
+ return NS_OK;
+ }
+
+ // 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->ClearShared();
+ }
+
+ // Ensure that the handle doesn't get released until after we have finished
+ // sending the buffer to the GPU process and/or reallocating it.
+ // FinishedSharing is not a sufficient condition because another thread may
+ // decide we are done while we are in the processing of sharing our newly
+ // reallocated handle. Once it goes out of scope, it may release the handle.
+ SourceSurfaceSharedData::HandleLock lock(aSurface);
+
+ // If we live in the same process, then it is a simple matter of directly
+ // asking the parent instance to store a pointer to the same data, no need
+ // to map the data into our memory space twice.
+ if (manager->SameProcess()) {
+ data->MarkShared(manager->GetNextExternalImageId());
+ CompositorManagerParent::AddSharedSurface(data->Id(), aSurface);
+ *aUserData = data;
+ return NS_OK;
+ }
+
+ // Attempt to share a handle with the GPU process. The handle may or may not
+ // be available -- it will only be available if it is either not yet finalized
+ // and/or if it has been finalized but never used for drawing in process.
+ ipc::SharedMemoryBasic::Handle handle = ipc::SharedMemoryBasic::NULLHandle();
+ nsresult rv = aSurface->CloneHandle(handle);
+ if (rv == NS_ERROR_NOT_AVAILABLE) {
+ // It is at least as expensive to copy the image to the GPU process if we
+ // have already closed the handle necessary to share, but if we reallocate
+ // the shared buffer to get a new handle, we can save some memory.
+ if (NS_WARN_IF(!aSurface->ReallocHandle())) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Reattempt the sharing of the handle to the GPU process.
+ rv = aSurface->CloneHandle(handle);
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ MOZ_ASSERT(rv != NS_ERROR_NOT_AVAILABLE);
+ return rv;
+ }
+
+ SurfaceFormat format = aSurface->GetFormat();
+ MOZ_RELEASE_ASSERT(
+ format == SurfaceFormat::B8G8R8X8 || format == SurfaceFormat::B8G8R8A8,
+ "bad format");
+
+ data->MarkShared(manager->GetNextExternalImageId());
+ manager->SendAddSharedSurface(
+ data->Id(),
+ SurfaceDescriptorShared(aSurface->GetSize(), aSurface->Stride(), format,
+ std::move(handle)));
+ *aUserData = data;
+ return NS_OK;
+}
+
+/* static */
+void SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface) {
+ MOZ_ASSERT(aSurface);
+
+ // The IPDL actor to do sharing can only be accessed on the main thread so we
+ // need to dispatch if off the main thread. However there is no real danger if
+ // we end up racing because if it is already shared, this method will do
+ // nothing.
+ if (!NS_IsMainThread()) {
+ class ShareRunnable final : public Runnable {
+ public:
+ explicit ShareRunnable(SourceSurfaceSharedData* aSurface)
+ : Runnable("SharedSurfacesChild::Share"), mSurface(aSurface) {}
+
+ NS_IMETHOD Run() override {
+ SharedUserData* unused = nullptr;
+ SharedSurfacesChild::ShareInternal(mSurface, &unused);
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<SourceSurfaceSharedData> mSurface;
+ };
+
+ SchedulerGroup::Dispatch(MakeAndAddRef<ShareRunnable>(aSurface));
+ return;
+ }
+
+ SharedUserData* unused = nullptr;
+ SharedSurfacesChild::ShareInternal(aSurface, &unused);
+}
+
+/* static */
+nsresult SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+ MOZ_ASSERT(aManager);
+
+ // Each time the surface changes, the producers of SourceSurfaceSharedData
+ // surfaces promise to increment the invalidation counter each time the
+ // surface has changed. We can use this counter to determine whether or not
+ // we should update our paired ImageKey.
+ Maybe<IntRect> dirtyRect = aSurface->TakeDirtyRect();
+ SharedUserData* data = nullptr;
+ nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data);
+ if (NS_SUCCEEDED(rv)) {
+ MOZ_ASSERT(data);
+ aKey = data->UpdateKey(aManager, aResources, dirtyRect);
+ }
+
+ return rv;
+}
+
+/* static */
+nsresult SharedSurfacesChild::Share(SourceSurface* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+ MOZ_ASSERT(aManager);
+
+ auto sharedSurface = AsSourceSurfaceSharedData(aSurface);
+ if (!sharedSurface) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ return Share(sharedSurface, aManager, aResources, aKey);
+}
+
+/* static */
+nsresult SharedSurfacesChild::Share(SourceSurface* aSurface,
+ wr::ExternalImageId& aId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+
+ auto sharedSurface = AsSourceSurfaceSharedData(aSurface);
+ if (!sharedSurface) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ // The external image ID does not change with the invalidation counter. The
+ // caller of this should be aware of the invalidations of the surface through
+ // another mechanism (e.g. imgRequestProxy listener notifications).
+ SharedUserData* data = nullptr;
+ nsresult rv = ShareInternal(sharedSurface, &data);
+ if (NS_SUCCEEDED(rv)) {
+ MOZ_ASSERT(data);
+ aId = data->Id();
+ }
+
+ return rv;
+}
+
+/* static */ nsresult SharedSurfacesChild::Share(
+ gfx::SourceSurface* aSurface, Maybe<SurfaceDescriptor>& aDesc) {
+ if (!aSurface) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // TODO(aosmond): With a refactor of how we store the external image ID, we
+ // could probably make it safe to access off the main thread. This would be
+ // useful for OffscreenCanvas on DOM workers.
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ wr::ExternalImageId extId{};
+ nsresult rv = Share(aSurface, extId);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ aDesc = Some(SurfaceDescriptorExternalImage(
+ wr::ExternalImageSource::SharedSurfaces, extId));
+ return NS_OK;
+}
+
+/* static */
+void SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId,
+ bool aReleaseId,
+ nsTArray<ImageKeyData>& aKeys) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ for (const auto& entry : aKeys) {
+ if (!entry.mManager->IsDestroyed()) {
+ entry.mManager->AddImageKeyForDiscard(entry.mImageKey);
+ }
+ }
+
+ if (!aReleaseId) {
+ // We don't own the external image ID itself.
+ return;
+ }
+
+ CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
+ if (MOZ_UNLIKELY(!manager || !manager->CanSend())) {
+ return;
+ }
+
+ if (manager->OwnsExternalImageId(aId)) {
+ // Only attempt to release current mappings in the compositor process. It is
+ // possible we had a surface that was previously shared, the compositor
+ // process crashed / was restarted, and then we freed the surface. In that
+ // case we know the mapping has already been freed.
+ manager->SendRemoveSharedSurface(aId);
+ }
+}
+
+/* static */ Maybe<wr::ExternalImageId> SharedSurfacesChild::GetExternalId(
+ const SourceSurfaceSharedData* aSurface) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aSurface);
+
+ SharedUserData* data =
+ static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey));
+ if (!data || !data->IsShared()) {
+ return Nothing();
+ }
+
+ return Some(data->Id());
+}
+
+AnimationImageKeyData::AnimationImageKeyData(RenderRootStateManager* aManager,
+ const wr::ImageKey& aImageKey)
+ : SharedSurfacesChild::ImageKeyData(aManager, aImageKey) {}
+
+AnimationImageKeyData::AnimationImageKeyData(AnimationImageKeyData&& aOther)
+ : SharedSurfacesChild::ImageKeyData(std::move(aOther)),
+ mPendingRelease(std::move(aOther.mPendingRelease)) {}
+
+AnimationImageKeyData& AnimationImageKeyData::operator=(
+ AnimationImageKeyData&& aOther) {
+ mPendingRelease = std::move(aOther.mPendingRelease);
+ SharedSurfacesChild::ImageKeyData::operator=(std::move(aOther));
+ return *this;
+}
+
+AnimationImageKeyData::~AnimationImageKeyData() = default;
+
+SharedSurfacesAnimation::~SharedSurfacesAnimation() {
+ MOZ_ASSERT(mKeys.IsEmpty());
+}
+
+void SharedSurfacesAnimation::Destroy() {
+ if (!NS_IsMainThread()) {
+ nsCOMPtr<nsIRunnable> task =
+ NewRunnableMethod("SharedSurfacesAnimation::Destroy", this,
+ &SharedSurfacesAnimation::Destroy);
+ NS_DispatchToMainThread(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);
+}
+
+// This will get the widget listener that handles painting. Generally, this is
+// the attached widget listener (or previously attached if the attached is paint
+// suppressed). Otherwise it is the widget listener. There should be a function
+// in nsIWidget that does this for us but there isn't yet.
+static nsIWidgetListener* GetPaintWidgetListener(nsIWidget* aWidget) {
+ if (auto* attached = aWidget->GetAttachedWidgetListener()) {
+ if (attached->GetView() &&
+ attached->GetView()->IsPrimaryFramePaintSuppressed()) {
+ if (auto* previouslyAttached =
+ aWidget->GetPreviouslyAttachedWidgetListener()) {
+ return previouslyAttached;
+ }
+ }
+ return attached;
+ }
+
+ return aWidget->GetWidgetListener();
+}
+
+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());
+
+ if (auto* cbc =
+ entry.mManager->LayerManager()->GetCompositorBridgeChild()) {
+ if (cbc->IsPaused()) {
+ continue;
+ }
+ }
+
+ // Only root compositor bridge childs record if they are paused, so check
+ // the refresh driver.
+ if (auto* widget = entry.mManager->LayerManager()->GetWidget()) {
+ nsIWidgetListener* wl = GetPaintWidgetListener(widget);
+ // Note call to wl->GetView() to make sure this is view type widget
+ // listener even though we don't use the view in this code.
+ if (wl && wl->GetView() && wl->GetPresShell()) {
+ if (auto* rd = wl->GetPresShell()->GetRefreshDriver()) {
+ if (rd->IsThrottled()) {
+ continue;
+ }
+ }
+ }
+ }
+
+ 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..02e2d6aafe
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesChild.h
@@ -0,0 +1,259 @@
+/* -*- 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/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/webrender/WebRenderTypes.h" // for wr::ImageKey
+#include "nsTArray.h" // for AutoTArray
+#include "nsThreadUtils.h" // for Runnable
+#include "ImageTypes.h" // for ContainerProducerID
+
+namespace mozilla {
+namespace layers {
+class AnimationImageKeyData;
+} // namespace layers
+} // namespace mozilla
+
+template <>
+struct nsTArray_RelocationStrategy<mozilla::layers::AnimationImageKeyData> {
+ typedef nsTArray_RelocateUsingMoveConstructor<
+ mozilla::layers::AnimationImageKeyData>
+ Type;
+};
+
+namespace mozilla {
+namespace gfx {
+class SourceSurface;
+class SourceSurfaceSharedData;
+} // namespace gfx
+
+namespace wr {
+class IpcResourceUpdateQueue;
+} // namespace wr
+
+namespace layers {
+
+class CompositorManagerChild;
+class RenderRootStateManager;
+
+class SharedSurfacesChild {
+ public:
+ /**
+ * Request that the surface be mapped into the compositor thread's memory
+ * space. This is useful for when the caller itself has no present need for
+ * the surface to be mapped, but knows there will be such a need in the
+ * future. This may be called from any thread, but it may cause a dispatch to
+ * the main thread.
+ */
+ static void Share(gfx::SourceSurfaceSharedData* aSurface);
+
+ /**
+ * Request that the surface be mapped into the compositor thread's memory
+ * space, and a valid ExternalImageId be generated for it for use with
+ * WebRender. This must be called from the main thread.
+ */
+ static nsresult Share(gfx::SourceSurface* aSurface, wr::ExternalImageId& aId);
+
+ /**
+ * Request that the surface be mapped into the compositor thread's memory
+ * space, and a valid ExternalImageId be generated for it for use with
+ * WebRender. This must be called from the main thread.
+ */
+ static nsresult Share(gfx::SourceSurface* aSurface,
+ Maybe<SurfaceDescriptor>& aDesc);
+
+ /**
+ * Request that the surface be mapped into the compositor thread's memory
+ * space, and a valid ImageKey be generated for it for use with WebRender.
+ * This must be called from the main thread.
+ */
+ static nsresult Share(gfx::SourceSurfaceSharedData* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey);
+
+ /**
+ * Request that the surface be mapped into the compositor thread's memory
+ * space, and a valid ImageKey be generated for it for use with WebRender.
+ * This must be called from the main thread.
+ */
+ static nsresult Share(gfx::SourceSurface* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey);
+
+ /**
+ * Get the external ID, if any, bound to the shared surface. Used for memory
+ * reporting purposes.
+ */
+ static Maybe<wr::ExternalImageId> GetExternalId(
+ const gfx::SourceSurfaceSharedData* aSurface);
+
+ /**
+ * Get the surface (or its underlying surface) as a SourceSurfaceSharedData
+ * pointer, if valid.
+ */
+ static gfx::SourceSurfaceSharedData* AsSourceSurfaceSharedData(
+ gfx::SourceSurface* aSurface);
+
+ class ImageKeyData {
+ public:
+ ImageKeyData(RenderRootStateManager* aManager,
+ const wr::ImageKey& aImageKey);
+ virtual ~ImageKeyData();
+
+ ImageKeyData(ImageKeyData&& aOther);
+ ImageKeyData& operator=(ImageKeyData&& aOther);
+ ImageKeyData(const ImageKeyData&) = delete;
+ ImageKeyData& operator=(const ImageKeyData&) = delete;
+
+ void MergeDirtyRect(const Maybe<gfx::IntRect>& aDirtyRect);
+
+ Maybe<gfx::IntRect> TakeDirtyRect() { return std::move(mDirtyRect); }
+
+ RefPtr<RenderRootStateManager> mManager;
+ Maybe<gfx::IntRect> mDirtyRect;
+ wr::ImageKey mImageKey;
+ };
+
+ private:
+ SharedSurfacesChild() = delete;
+ ~SharedSurfacesChild() = delete;
+
+ friend class SharedSurfacesAnimation;
+
+ class SharedUserData final : public Runnable {
+ public:
+ SharedUserData();
+ 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 ClearShared() {
+ mKeys.Clear();
+ mShared = false;
+ }
+
+ bool IsShared() const { return mShared; }
+
+ void MarkShared(const wr::ExternalImageId& aId) {
+ MOZ_ASSERT(!mShared);
+ mId = aId;
+ mShared = true;
+ }
+
+ wr::ImageKey UpdateKey(RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ const Maybe<gfx::IntRect>& aDirtyRect);
+
+ protected:
+ AutoTArray<ImageKeyData, 1> mKeys;
+ wr::ExternalImageId mId;
+ bool mShared : 1;
+ };
+
+ static nsresult ShareInternal(gfx::SourceSurfaceSharedData* aSurface,
+ SharedUserData** aUserData);
+
+ static void Unshare(const wr::ExternalImageId& aId, bool aReleaseId,
+ nsTArray<ImageKeyData>& aKeys);
+
+ static void DestroySharedUserData(void* aClosure);
+
+ static gfx::UserDataKey sSharedKey;
+};
+
+class AnimationImageKeyData final : public SharedSurfacesChild::ImageKeyData {
+ public:
+ AnimationImageKeyData(RenderRootStateManager* aManager,
+ const wr::ImageKey& aImageKey);
+
+ virtual ~AnimationImageKeyData();
+
+ AnimationImageKeyData(AnimationImageKeyData&& aOther);
+ AnimationImageKeyData& operator=(AnimationImageKeyData&& aOther);
+
+ AutoTArray<RefPtr<gfx::SourceSurfaceSharedData>, 2> mPendingRelease;
+};
+
+/**
+ * This helper class owns a single ImageKey which will map to different external
+ * image IDs representing different frames in an animation.
+ */
+class SharedSurfacesAnimation final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedSurfacesAnimation)
+
+ SharedSurfacesAnimation() = default;
+
+ void Destroy();
+
+ /**
+ * Set the animation to display the given frame.
+ * @param aSurface The current frame.
+ * @param aDirtyRect Dirty rect representing the change between the new frame
+ * and the previous frame. We will request only the delta
+ * be reuploaded by WebRender.
+ */
+ nsresult SetCurrentFrame(gfx::SourceSurfaceSharedData* aSurface,
+ const gfx::IntRect& aDirtyRect);
+
+ /**
+ * Generate an ImageKey for the given frame.
+ * @param aSurface The current frame. This should match what was cached via
+ * SetCurrentFrame, but if it does not, it will need to
+ * regenerate the cached ImageKey.
+ */
+ nsresult UpdateKey(gfx::SourceSurfaceSharedData* aSurface,
+ RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources,
+ wr::ImageKey& aKey);
+
+ /**
+ * Release our reference to all frames up to and including the frame which
+ * has an external image ID which matches aId.
+ */
+ void ReleasePreviousFrame(RenderRootStateManager* aManager,
+ const wr::ExternalImageId& aId);
+
+ /**
+ * Destroy any state information bound for the given layer manager. Any
+ * image keys are already invalid.
+ */
+ void Invalidate(RenderRootStateManager* aManager);
+
+ private:
+ ~SharedSurfacesAnimation();
+
+ void HoldSurfaceForRecycling(AnimationImageKeyData& aEntry,
+ gfx::SourceSurfaceSharedData* aSurface);
+
+ AutoTArray<AnimationImageKeyData, 1> mKeys;
+ wr::ExternalImageId mId;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/SharedSurfacesMemoryReport.h b/gfx/layers/ipc/SharedSurfacesMemoryReport.h
new file mode 100644
index 0000000000..81baf1349f
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesMemoryReport.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_SHAREDSURFACESMEMORYREPORT_H
+#define MOZILLA_GFX_SHAREDSURFACESMEMORYREPORT_H
+
+#include <cstdint> // for uint32_t
+#include <unordered_map>
+#include "base/process.h"
+#include "ipc/IPCMessageUtils.h"
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "mozilla/gfx/Point.h" // for IntSize
+
+namespace mozilla {
+namespace layers {
+
+class SharedSurfacesMemoryReport final {
+ public:
+ class SurfaceEntry final {
+ public:
+ base::ProcessId mCreatorPid;
+ gfx::IntSize mSize;
+ int32_t mStride;
+ uint32_t mConsumers;
+ bool mCreatorRef;
+ };
+
+ std::unordered_map<uint64_t, SurfaceEntry> mSurfaces;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::SharedSurfacesMemoryReport> {
+ typedef mozilla::layers::SharedSurfacesMemoryReport paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mSurfaces);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mSurfaces);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::layers::SharedSurfacesMemoryReport::SurfaceEntry>
+ : public PlainOldDataSerializer<
+ mozilla::layers::SharedSurfacesMemoryReport::SurfaceEntry> {};
+
+} // namespace IPC
+
+#endif
diff --git a/gfx/layers/ipc/SharedSurfacesParent.cpp b/gfx/layers/ipc/SharedSurfacesParent.cpp
new file mode 100644
index 0000000000..932a7558ad
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesParent.cpp
@@ -0,0 +1,405 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedSurfacesParent.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/StaticPrefs_image.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/layers/SharedSurfacesMemoryReport.h"
+#include "mozilla/layers/SourceSurfaceSharedData.h"
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/webrender/RenderSharedSurfaceTextureHost.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "nsThreadUtils.h" // for GetCurrentSerialEventTarget
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+StaticMutex SharedSurfacesParent::sMutex;
+StaticAutoPtr<SharedSurfacesParent> SharedSurfacesParent::sInstance;
+
+void SharedSurfacesParent::MappingTracker::NotifyExpiredLocked(
+ SourceSurfaceSharedDataWrapper* aSurface,
+ const StaticMutexAutoLock& aAutoLock) {
+ RemoveObjectLocked(aSurface, aAutoLock);
+ mExpired.AppendElement(aSurface);
+}
+
+void SharedSurfacesParent::MappingTracker::TakeExpired(
+ nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired,
+ const StaticMutexAutoLock& aAutoLock) {
+ aExpired = std::move(mExpired);
+}
+
+void SharedSurfacesParent::MappingTracker::NotifyHandlerEnd() {
+ nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>> expired;
+ {
+ StaticMutexAutoLock lock(sMutex);
+ TakeExpired(expired, lock);
+ }
+
+ SharedSurfacesParent::ExpireMap(expired);
+}
+
+SharedSurfacesParent::SharedSurfacesParent()
+ : mTracker(
+ StaticPrefs::image_mem_shared_unmap_min_expiration_ms_AtStartup(),
+ mozilla::GetCurrentSerialEventTarget()) {}
+
+/* static */
+void SharedSurfacesParent::Initialize() {
+ MOZ_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ sInstance = new SharedSurfacesParent();
+ }
+}
+
+/* static */
+void SharedSurfacesParent::ShutdownRenderThread() {
+ // The main thread should blocked on waiting for the render thread to
+ // complete so this should be safe to release off the main thread.
+ MOZ_ASSERT(wr::RenderThread::IsInRenderThread());
+ StaticMutexAutoLock lock(sMutex);
+ MOZ_ASSERT(sInstance);
+
+ for (const auto& key : sInstance->mSurfaces.Keys()) {
+ // There may be lingering consumers of the surfaces that didn't get shutdown
+ // yet but since we are here, we know the render thread is finished and we
+ // can unregister everything.
+ wr::RenderThread::Get()->UnregisterExternalImageDuringShutdown(
+ wr::ToExternalImageId(key));
+ }
+}
+
+/* static */
+void SharedSurfacesParent::Shutdown() {
+ // The compositor thread and render threads are shutdown, so this is the last
+ // thread that could use it. The expiration tracker needs to be freed on the
+ // main thread.
+ MOZ_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sMutex);
+ sInstance = nullptr;
+}
+
+/* static */
+already_AddRefed<DataSourceSurface> SharedSurfacesParent::Get(
+ const wr::ExternalImageId& aId) {
+ RefPtr<SourceSurfaceSharedDataWrapper> surface;
+
+ {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd";
+ return nullptr;
+ }
+
+ if (sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface))) {
+ return surface.forget();
+ }
+ }
+
+ // We cannot block the compositor thread since that's the thread the necessary
+ // IPDL events would come in on.
+ if (NS_WARN_IF(CompositorThreadHolder::IsInCompositorThread())) {
+ return nullptr;
+ }
+
+ // Block until we see the relevant resource come in or the actor is destroyed.
+ CompositorManagerParent::WaitForSharedSurface(aId);
+
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd";
+ return nullptr;
+ }
+
+ 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)) {
+ RemoveTrackingLocked(surface, lock);
+ wr::RenderThread::Get()->UnregisterExternalImage(wr::ToExternalImageId(id));
+ sInstance->mSurfaces.Remove(id);
+ }
+
+ return true;
+}
+
+/* static */
+void SharedSurfacesParent::AddSameProcess(const wr::ExternalImageId& aId,
+ SourceSurfaceSharedData* aSurface) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ 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));
+
+ auto texture = MakeRefPtr<wr::RenderSharedSurfaceTextureHost>(surface);
+ wr::RenderThread::Get()->RegisterExternalImage(aId, texture.forget());
+
+ surface->AddConsumer();
+ sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface));
+}
+
+/* static */
+void SharedSurfacesParent::RemoveAll(uint32_t aNamespace) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return;
+ }
+
+ auto* renderThread = wr::RenderThread::Get();
+
+ // 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()) {
+ if (static_cast<uint32_t>(i.Key() >> 32) != aNamespace) {
+ continue;
+ }
+
+ SourceSurfaceSharedDataWrapper* surface = i.Data();
+ if (surface->HasCreatorRef() &&
+ surface->RemoveConsumer(/* aForCreator */ true)) {
+ RemoveTrackingLocked(surface, lock);
+ if (renderThread) {
+ renderThread->UnregisterExternalImage(wr::ToExternalImageId(i.Key()));
+ }
+ i.Remove();
+ }
+ }
+}
+
+/* static */
+void SharedSurfacesParent::Add(const wr::ExternalImageId& aId,
+ SurfaceDescriptorShared&& aDesc,
+ base::ProcessId aPid) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(aPid != base::GetCurrentProcId());
+
+ RefPtr<SourceSurfaceSharedDataWrapper> surface =
+ new SourceSurfaceSharedDataWrapper();
+
+ // We preferentially map in new surfaces when they are initially received
+ // because we are likely to reference them in a display list soon. The unmap
+ // will ensure we add the surface to the expiration tracker. We do it outside
+ // the mutex to ensure we always lock the surface mutex first, and our mutex
+ // second, to avoid deadlock.
+ //
+ // Note that the surface wrapper maps in the given handle as read only.
+ surface->Init(aDesc.size(), aDesc.stride(), aDesc.format(),
+ std::move(aDesc.handle()), aPid);
+
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " shtd";
+ return;
+ }
+
+ uint64_t id = wr::AsUint64(aId);
+ MOZ_ASSERT(!sInstance->mSurfaces.Contains(id));
+
+ auto texture = MakeRefPtr<wr::RenderSharedSurfaceTextureHost>(surface);
+ wr::RenderThread::Get()->RegisterExternalImage(aId, texture.forget());
+
+ surface->AddConsumer();
+ sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface));
+}
+
+/* static */
+void SharedSurfacesParent::Remove(const wr::ExternalImageId& aId) {
+ DebugOnly<bool> rv = Release(aId, /* aForCreator */ true);
+ MOZ_ASSERT(rv);
+}
+
+/* static */
+void SharedSurfacesParent::AddTrackingLocked(
+ SourceSurfaceSharedDataWrapper* aSurface,
+ const StaticMutexAutoLock& aAutoLock) {
+ MOZ_ASSERT(!aSurface->GetExpirationState()->IsTracked());
+ sInstance->mTracker.AddObjectLocked(aSurface, aAutoLock);
+}
+
+/* static */
+void SharedSurfacesParent::AddTracking(
+ SourceSurfaceSharedDataWrapper* aSurface) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return;
+ }
+
+ AddTrackingLocked(aSurface, lock);
+}
+
+/* static */
+void SharedSurfacesParent::RemoveTrackingLocked(
+ SourceSurfaceSharedDataWrapper* aSurface,
+ const StaticMutexAutoLock& aAutoLock) {
+ if (!aSurface->GetExpirationState()->IsTracked()) {
+ return;
+ }
+
+ sInstance->mTracker.RemoveObjectLocked(aSurface, aAutoLock);
+}
+
+/* static */
+void SharedSurfacesParent::RemoveTracking(
+ SourceSurfaceSharedDataWrapper* aSurface) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return;
+ }
+
+ RemoveTrackingLocked(aSurface, lock);
+}
+
+/* static */
+bool SharedSurfacesParent::AgeOneGenerationLocked(
+ nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired,
+ const StaticMutexAutoLock& aAutoLock) {
+ if (sInstance->mTracker.IsEmptyLocked(aAutoLock)) {
+ return false;
+ }
+
+ sInstance->mTracker.AgeOneGenerationLocked(aAutoLock);
+ sInstance->mTracker.TakeExpired(aExpired, aAutoLock);
+ return true;
+}
+
+/* static */
+bool SharedSurfacesParent::AgeOneGeneration(
+ nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return false;
+ }
+
+ return AgeOneGenerationLocked(aExpired, lock);
+}
+
+/* static */
+bool SharedSurfacesParent::AgeAndExpireOneGeneration() {
+ nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>> expired;
+ bool aged = AgeOneGeneration(expired);
+ ExpireMap(expired);
+ return aged;
+}
+
+/* static */
+void SharedSurfacesParent::ExpireMap(
+ nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>>& aExpired) {
+ for (auto& surface : aExpired) {
+ surface->ExpireMap();
+ }
+}
+
+/* static */
+void SharedSurfacesParent::AccumulateMemoryReport(
+ uint32_t aNamespace, SharedSurfacesMemoryReport& aReport) {
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return;
+ }
+
+ for (const auto& entry : sInstance->mSurfaces) {
+ if (static_cast<uint32_t>(entry.GetKey() >> 32) != aNamespace) {
+ continue;
+ }
+
+ SourceSurfaceSharedDataWrapper* surface = entry.GetData();
+ aReport.mSurfaces.insert(std::make_pair(
+ entry.GetKey(),
+ SharedSurfacesMemoryReport::SurfaceEntry{
+ surface->GetCreatorPid(), surface->GetSize(), surface->Stride(),
+ surface->GetConsumers(), surface->HasCreatorRef()}));
+ }
+}
+
+/* static */
+bool SharedSurfacesParent::AccumulateMemoryReport(
+ SharedSurfacesMemoryReport& aReport) {
+ if (XRE_IsParentProcess()) {
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+ if (!gpm || gpm->GPUProcessPid() != base::kInvalidProcessId) {
+ return false;
+ }
+ } else if (!XRE_IsGPUProcess()) {
+ return false;
+ }
+
+ StaticMutexAutoLock lock(sMutex);
+ if (!sInstance) {
+ return true;
+ }
+
+ for (const auto& entry : sInstance->mSurfaces) {
+ SourceSurfaceSharedDataWrapper* surface = entry.GetData();
+ aReport.mSurfaces.insert(std::make_pair(
+ entry.GetKey(),
+ SharedSurfacesMemoryReport::SurfaceEntry{
+ surface->GetCreatorPid(), surface->GetSize(), surface->Stride(),
+ surface->GetConsumers(), surface->HasCreatorRef()}));
+ }
+
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/SharedSurfacesParent.h b/gfx/layers/ipc/SharedSurfacesParent.h
new file mode 100644
index 0000000000..b62c4a5d81
--- /dev/null
+++ b/gfx/layers/ipc/SharedSurfacesParent.h
@@ -0,0 +1,162 @@
+/* -*- 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/layers/SourceSurfaceSharedData.h"
+#include "mozilla/webrender/WebRenderTypes.h" // for wr::ExternalImageId
+#include "nsExpirationTracker.h"
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class CompositorManagerParent;
+class SharedSurfacesMemoryReport;
+
+class SharedSurfacesParent final {
+ public:
+ static void Initialize();
+ static void ShutdownRenderThread();
+ static void Shutdown();
+
+ // Get without increasing the consumer count.
+ static already_AddRefed<gfx::DataSourceSurface> Get(
+ const wr::ExternalImageId& aId);
+
+ // Get but also increase the consumer count. Must call Release after finished.
+ static already_AddRefed<gfx::DataSourceSurface> Acquire(
+ const wr::ExternalImageId& aId);
+
+ static bool Release(const wr::ExternalImageId& aId, bool aForCreator = false);
+
+ static void Add(const wr::ExternalImageId& aId,
+ SurfaceDescriptorShared&& aDesc, base::ProcessId aPid);
+
+ static void Remove(const wr::ExternalImageId& aId);
+
+ static void RemoveAll(uint32_t aNamespace);
+
+ static void AccumulateMemoryReport(uint32_t aNamespace,
+ SharedSurfacesMemoryReport& aReport);
+
+ static bool AccumulateMemoryReport(SharedSurfacesMemoryReport& aReport);
+
+ static void AddTracking(gfx::SourceSurfaceSharedDataWrapper* aSurface);
+
+ static void RemoveTracking(gfx::SourceSurfaceSharedDataWrapper* aSurface);
+
+ static bool AgeOneGeneration(
+ nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired);
+
+ static bool AgeAndExpireOneGeneration();
+
+ private:
+ friend class CompositorManagerParent;
+ friend class gfx::SourceSurfaceSharedDataWrapper;
+
+ SharedSurfacesParent();
+
+ static void AddSameProcess(const wr::ExternalImageId& aId,
+ gfx::SourceSurfaceSharedData* aSurface);
+
+ static void AddTrackingLocked(gfx::SourceSurfaceSharedDataWrapper* aSurface,
+ const StaticMutexAutoLock& aAutoLock);
+
+ static void RemoveTrackingLocked(
+ gfx::SourceSurfaceSharedDataWrapper* aSurface,
+ const StaticMutexAutoLock& aAutoLock);
+
+ static bool AgeOneGenerationLocked(
+ nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired,
+ const StaticMutexAutoLock& aAutoLock);
+
+ static void ExpireMap(
+ nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired);
+
+ static StaticMutex sMutex MOZ_UNANNOTATED;
+
+ static StaticAutoPtr<SharedSurfacesParent> sInstance;
+
+ nsRefPtrHashtable<nsUint64HashKey, gfx::SourceSurfaceSharedDataWrapper>
+ mSurfaces;
+
+ class MappingTracker final
+ : public ExpirationTrackerImpl<gfx::SourceSurfaceSharedDataWrapper, 4,
+ StaticMutex, StaticMutexAutoLock> {
+ public:
+ explicit MappingTracker(uint32_t aExpirationTimeoutMS,
+ nsIEventTarget* aEventTarget)
+ : ExpirationTrackerImpl<gfx::SourceSurfaceSharedDataWrapper, 4,
+ StaticMutex, StaticMutexAutoLock>(
+ aExpirationTimeoutMS, "SharedMappingTracker", aEventTarget) {}
+
+ void TakeExpired(
+ nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>>& aExpired,
+ const StaticMutexAutoLock& aAutoLock);
+
+ protected:
+ void NotifyExpiredLocked(gfx::SourceSurfaceSharedDataWrapper* aSurface,
+ const StaticMutexAutoLock& aAutoLock) override;
+
+ void NotifyHandlerEndLocked(const StaticMutexAutoLock& aAutoLock) override {
+ }
+
+ void NotifyHandlerEnd() override;
+
+ StaticMutex& GetMutex() override { return sMutex; }
+
+ nsTArray<RefPtr<gfx::SourceSurfaceSharedDataWrapper>> mExpired;
+ };
+
+ MappingTracker mTracker;
+};
+
+/**
+ * Helper class that is used to keep SourceSurfaceSharedDataWrapper objects
+ * around as long as one of the dependent IPDL actors is still alive and may
+ * reference them for a given PCompositorManager namespace.
+ */
+class SharedSurfacesHolder final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedSurfacesHolder)
+
+ public:
+ explicit SharedSurfacesHolder(uint32_t aNamespace) : mNamespace(aNamespace) {}
+
+ already_AddRefed<gfx::DataSourceSurface> Get(const wr::ExternalImageId& aId) {
+ uint32_t extNamespace = static_cast<uint32_t>(wr::AsUint64(aId) >> 32);
+ if (NS_WARN_IF(extNamespace != mNamespace)) {
+ MOZ_ASSERT_UNREACHABLE("Wrong namespace?");
+ return nullptr;
+ }
+
+ return SharedSurfacesParent::Get(aId);
+ }
+
+ private:
+ ~SharedSurfacesHolder() { SharedSurfacesParent::RemoveAll(mNamespace); }
+
+ uint32_t mNamespace;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/SurfaceDescriptor.h b/gfx/layers/ipc/SurfaceDescriptor.h
new file mode 100644
index 0000000000..6d4c2d3271
--- /dev/null
+++ b/gfx/layers/ipc/SurfaceDescriptor.h
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_SurfaceDescriptor_h
+#define IPC_SurfaceDescriptor_h
+
+#if defined(XP_MACOSX)
+# define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+#endif
+
+#endif // IPC_SurfaceDescriptor_h
diff --git a/gfx/layers/ipc/SynchronousTask.h b/gfx/layers/ipc/SynchronousTask.h
new file mode 100644
index 0000000000..a7091eb8cb
--- /dev/null
+++ b/gfx/layers/ipc/SynchronousTask.h
@@ -0,0 +1,72 @@
+/* -*- 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), mDone(false) {}
+
+ nsresult Wait(PRIntervalTime aInterval = PR_INTERVAL_NO_TIMEOUT) {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+
+ // For indefinite timeouts, wait in a while loop to handle spurious
+ // wakeups.
+ while (aInterval == PR_INTERVAL_NO_TIMEOUT && !mDone) {
+ mMonitor.Wait();
+ }
+
+ // For finite timeouts, we only check once for completion, and otherwise
+ // rely on the ReentrantMonitor to manage the interval. If the monitor
+ // returns too early, we'll never know, but we can check if the mDone
+ // flag was set to true, indicating that the task finished successfully.
+ if (!mDone) {
+ // We ignore the return value from ReentrantMonitor::Wait, because it's
+ // always NS_OK, even in the case of timeout.
+ mMonitor.Wait(aInterval);
+
+ if (!mDone) {
+ return NS_ERROR_ABORT;
+ }
+ }
+
+ return NS_OK;
+ }
+
+ private:
+ void Complete() {
+ ReentrantMonitorAutoEnter lock(mMonitor);
+ mDone = true;
+ mMonitor.NotifyAll();
+ }
+
+ private:
+ ReentrantMonitor mMonitor MOZ_UNANNOTATED;
+ bool mDone;
+};
+
+class MOZ_STACK_CLASS AutoCompleteTask final {
+ public:
+ explicit AutoCompleteTask(SynchronousTask* aTask) : mTask(aTask) {}
+ ~AutoCompleteTask() { mTask->Complete(); }
+
+ private:
+ SynchronousTask* mTask;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/TextureForwarder.h b/gfx/layers/ipc/TextureForwarder.h
new file mode 100644
index 0000000000..af84390143
--- /dev/null
+++ b/gfx/layers/ipc/TextureForwarder.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_TEXTUREFORWARDER
+#define MOZILLA_LAYERS_TEXTUREFORWARDER
+
+#include <stdint.h> // for int32_t, uint64_t
+#include "gfxTypes.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/LayersMessages.h" // for Edit, etc
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/layers/KnowsCompositor.h"
+#include "nsISerialEventTarget.h"
+
+namespace mozilla {
+namespace layers {
+class CanvasChild;
+
+/**
+ * An abstract interface for classes that implement the autogenerated
+ * IPDL actor class. Lets us check if they are still valid for IPC.
+ */
+class LayersIPCActor {
+ public:
+ virtual bool IPCOpen() const { return true; }
+};
+
+/**
+ * An abstract interface for LayersIPCActors that implement a top-level
+ * IPDL protocol so also have their own channel.
+ * Has their own MessageLoop for message dispatch, and can allocate
+ * shmem.
+ */
+class LayersIPCChannel : public LayersIPCActor,
+ public mozilla::ipc::IShmemAllocator {
+ public:
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ virtual bool IsSameProcess() const = 0;
+
+ virtual bool UsesImageBridge() const { return false; }
+
+ virtual base::ProcessId GetParentPid() const = 0;
+
+ virtual nsISerialEventTarget* GetThread() const = 0;
+
+ virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() {
+ return nullptr;
+ }
+
+ virtual void CancelWaitForNotifyNotUsed(uint64_t aTextureId) = 0;
+
+ virtual wr::MaybeExternalImageId GetNextExternalImageId() {
+ return Nothing();
+ }
+
+ protected:
+ virtual ~LayersIPCChannel() = default;
+};
+
+/**
+ * An abstract interface for classes that can allocate PTexture objects
+ * across IPDL. Currently a sub-class of LayersIPCChannel for simplicity
+ * since all our implementations use both, but could be independant if needed.
+ */
+class TextureForwarder : public LayersIPCChannel {
+ public:
+ /**
+ * Create a TextureChild/Parent pair as as well as the TextureHost on the
+ * parent side.
+ */
+ virtual PTextureChild* CreateTexture(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ const dom::ContentParentId& aContentId, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId) = 0;
+
+ /**
+ * Returns the CanvasChild for this TextureForwarder.
+ */
+ virtual already_AddRefed<CanvasChild> GetCanvasChild() { return nullptr; };
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/UiCompositorControllerChild.cpp b/gfx/layers/ipc/UiCompositorControllerChild.cpp
new file mode 100644
index 0000000000..c1bf49bc27
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/UiCompositorControllerChild.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/SynchronousTask.h"
+#include "mozilla/layers/UiCompositorControllerMessageTypes.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/StaticPtr.h"
+#include "nsBaseWidget.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+#if defined(MOZ_WIDGET_ANDROID)
+# include "mozilla/widget/AndroidUiThread.h"
+
+static RefPtr<nsThread> GetUiThread() { return mozilla::GetAndroidUiThread(); }
+#else
+static RefPtr<nsThread> GetUiThread() {
+ MOZ_CRASH("Platform does not support UiCompositorController");
+ return nullptr;
+}
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+namespace mozilla {
+namespace layers {
+
+// public:
+/* static */
+RefPtr<UiCompositorControllerChild>
+UiCompositorControllerChild::CreateForSameProcess(
+ const LayersId& aRootLayerTreeId, nsBaseWidget* aWidget) {
+ RefPtr<UiCompositorControllerChild> child =
+ new UiCompositorControllerChild(0, aWidget);
+ child->mParent = new UiCompositorControllerParent(aRootLayerTreeId);
+ GetUiThread()->Dispatch(
+ NewRunnableMethod(
+ "layers::UiCompositorControllerChild::OpenForSameProcess", child,
+ &UiCompositorControllerChild::OpenForSameProcess),
+ nsIThread::DISPATCH_NORMAL);
+ return child;
+}
+
+/* static */
+RefPtr<UiCompositorControllerChild>
+UiCompositorControllerChild::CreateForGPUProcess(
+ const uint64_t& aProcessToken,
+ Endpoint<PUiCompositorControllerChild>&& aEndpoint, nsBaseWidget* aWidget) {
+ RefPtr<UiCompositorControllerChild> child =
+ new UiCompositorControllerChild(aProcessToken, aWidget);
+
+ RefPtr<nsIRunnable> task =
+ NewRunnableMethod<Endpoint<PUiCompositorControllerChild>&&>(
+ "layers::UiCompositorControllerChild::OpenForGPUProcess", child,
+ &UiCompositorControllerChild::OpenForGPUProcess,
+ std::move(aEndpoint));
+
+ GetUiThread()->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL);
+ return child;
+}
+
+bool UiCompositorControllerChild::Pause() {
+ if (!mIsOpen) {
+ return false;
+ }
+ return SendPause();
+}
+
+bool UiCompositorControllerChild::Resume() {
+ if (!mIsOpen) {
+ return false;
+ }
+ bool resumed = false;
+ return SendResume(&resumed) && resumed;
+}
+
+bool UiCompositorControllerChild::ResumeAndResize(const int32_t& aX,
+ const int32_t& aY,
+ const int32_t& aWidth,
+ const int32_t& aHeight) {
+ if (!mIsOpen) {
+ mResize = Some(gfx::IntRect(aX, aY, aWidth, aHeight));
+ // Since we are caching these values, pretend the call succeeded.
+ return true;
+ }
+ bool resumed = false;
+ return SendResumeAndResize(aX, aY, aWidth, aHeight, &resumed) && resumed;
+}
+
+bool UiCompositorControllerChild::InvalidateAndRender() {
+ if (!mIsOpen) {
+ return false;
+ }
+ return SendInvalidateAndRender();
+}
+
+bool UiCompositorControllerChild::SetMaxToolbarHeight(const int32_t& aHeight) {
+ if (!mIsOpen) {
+ mMaxToolbarHeight = Some(aHeight);
+ // Since we are caching this value, pretend the call succeeded.
+ return true;
+ }
+ return SendMaxToolbarHeight(aHeight);
+}
+
+bool UiCompositorControllerChild::SetFixedBottomOffset(int32_t aOffset) {
+ return SendFixedBottomOffset(aOffset);
+}
+
+bool UiCompositorControllerChild::ToolbarAnimatorMessageFromUI(
+ const int32_t& aMessage) {
+ if (!mIsOpen) {
+ return false;
+ }
+
+ if (aMessage == IS_COMPOSITOR_CONTROLLER_OPEN) {
+ RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN);
+ }
+
+ return true;
+}
+
+bool UiCompositorControllerChild::SetDefaultClearColor(const uint32_t& aColor) {
+ if (!mIsOpen) {
+ mDefaultClearColor = Some(aColor);
+ // Since we are caching this value, pretend the call succeeded.
+ return true;
+ }
+
+ return SendDefaultClearColor(aColor);
+}
+
+bool UiCompositorControllerChild::RequestScreenPixels() {
+ if (!mIsOpen) {
+ return false;
+ }
+
+ return SendRequestScreenPixels();
+}
+
+bool UiCompositorControllerChild::EnableLayerUpdateNotifications(
+ const bool& aEnable) {
+ if (!mIsOpen) {
+ mLayerUpdateEnabled = Some(aEnable);
+ // Since we are caching this value, pretend the call succeeded.
+ return true;
+ }
+
+ return SendEnableLayerUpdateNotifications(aEnable);
+}
+
+void UiCompositorControllerChild::Destroy() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ layers::SynchronousTask task("UiCompositorControllerChild::Destroy");
+ GetUiThread()->Dispatch(NS_NewRunnableFunction(
+ "layers::UiCompositorControllerChild::Destroy", [&]() {
+ MOZ_ASSERT(GetUiThread()->IsOnCurrentThread());
+ AutoCompleteTask complete(&task);
+
+ // Clear the process token so that we don't notify the GPUProcessManager
+ // about an abnormal shutdown, thereby tearing down the GPU process.
+ mProcessToken = 0;
+
+ if (mWidget) {
+ // Dispatch mWidget to main thread to prevent it from being destructed
+ // by the ui thread.
+ RefPtr<nsIWidget> widget = std::move(mWidget);
+ NS_ReleaseOnMainThread("UiCompositorControllerChild::mWidget",
+ widget.forget());
+ }
+
+ if (mIsOpen) {
+ // Close the underlying IPC channel.
+ PUiCompositorControllerChild::Close();
+ mIsOpen = false;
+ }
+ }));
+
+ task.Wait();
+}
+
+bool UiCompositorControllerChild::DeallocPixelBuffer(Shmem& aMem) {
+ return DeallocShmem(aMem);
+}
+
+// protected:
+void UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mIsOpen = false;
+ mParent = nullptr;
+
+ if (mProcessToken) {
+ gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+ mProcessToken = 0;
+ }
+}
+
+void UiCompositorControllerChild::ProcessingError(Result aCode,
+ const char* aReason) {
+ if (aCode != MsgDropped) {
+ gfxDevCrash(gfx::LogReason::ProcessingError)
+ << "Processing error in UiCompositorControllerChild: " << int(aCode);
+ }
+}
+
+void UiCompositorControllerChild::HandleFatalError(const char* aMsg) {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerChild::RecvToolbarAnimatorMessageFromCompositor(
+ const int32_t& aMessage) {
+#if defined(MOZ_WIDGET_ANDROID)
+ if (mWidget) {
+ mWidget->RecvToolbarAnimatorMessageFromCompositor(aMessage);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerChild::RecvRootFrameMetrics(
+ const ScreenPoint& aScrollOffset, const CSSToScreenScale& aZoom) {
+#if defined(MOZ_WIDGET_ANDROID)
+ if (mWidget) {
+ mWidget->UpdateRootFrameMetrics(aScrollOffset, aZoom);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerChild::RecvScreenPixels(
+ ipc::Shmem&& aMem, const ScreenIntSize& aSize, bool aNeedsYFlip) {
+#if defined(MOZ_WIDGET_ANDROID)
+ if (mWidget) {
+ mWidget->RecvScreenPixels(std::move(aMem), aSize, aNeedsYFlip);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+// private:
+UiCompositorControllerChild::UiCompositorControllerChild(
+ const uint64_t& aProcessToken, nsBaseWidget* aWidget)
+ : mIsOpen(false), mProcessToken(aProcessToken), mWidget(aWidget) {}
+
+UiCompositorControllerChild::~UiCompositorControllerChild() = default;
+
+void UiCompositorControllerChild::OpenForSameProcess() {
+ MOZ_ASSERT(GetUiThread()->IsOnCurrentThread());
+
+ mIsOpen = Open(mParent, mozilla::layers::CompositorThread(),
+ mozilla::ipc::ChildSide);
+
+ if (!mIsOpen) {
+ mParent = nullptr;
+ return;
+ }
+
+ mParent->InitializeForSameProcess();
+ SendCachedValues();
+ // Let Ui thread know the connection is open;
+ RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN);
+}
+
+void UiCompositorControllerChild::OpenForGPUProcess(
+ Endpoint<PUiCompositorControllerChild>&& aEndpoint) {
+ MOZ_ASSERT(GetUiThread()->IsOnCurrentThread());
+
+ mIsOpen = aEndpoint.Bind(this);
+
+ if (!mIsOpen) {
+ // The GPU Process Manager might be gone if we receive ActorDestroy very
+ // late in shutdown.
+ if (gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get()) {
+ gpm->NotifyRemoteActorDestroyed(mProcessToken);
+ }
+ return;
+ }
+
+ SendCachedValues();
+ // Let Ui thread know the connection is open;
+ RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN);
+}
+
+void UiCompositorControllerChild::SendCachedValues() {
+ MOZ_ASSERT(mIsOpen);
+ if (mResize) {
+ bool resumed;
+ SendResumeAndResize(mResize.ref().x, mResize.ref().y, mResize.ref().width,
+ mResize.ref().height, &resumed);
+ mResize.reset();
+ }
+ if (mMaxToolbarHeight) {
+ SendMaxToolbarHeight(mMaxToolbarHeight.ref());
+ mMaxToolbarHeight.reset();
+ }
+ if (mDefaultClearColor) {
+ SendDefaultClearColor(mDefaultClearColor.ref());
+ mDefaultClearColor.reset();
+ }
+ if (mLayerUpdateEnabled) {
+ SendEnableLayerUpdateNotifications(mLayerUpdateEnabled.ref());
+ mLayerUpdateEnabled.reset();
+ }
+}
+
+#ifdef MOZ_WIDGET_ANDROID
+void UiCompositorControllerChild::SetCompositorSurfaceManager(
+ java::CompositorSurfaceManager::Param aCompositorSurfaceManager) {
+ MOZ_ASSERT(!mCompositorSurfaceManager,
+ "SetCompositorSurfaceManager must only be called once.");
+ MOZ_ASSERT(mProcessToken != 0,
+ "SetCompositorSurfaceManager must only be called for GPU process "
+ "controllers.");
+ mCompositorSurfaceManager = aCompositorSurfaceManager;
+};
+
+void UiCompositorControllerChild::OnCompositorSurfaceChanged(
+ int32_t aWidgetId, java::sdk::Surface::Param aSurface) {
+ // If mCompositorSurfaceManager is not set then there is no GPU process and
+ // we do not need to do anything.
+ if (mCompositorSurfaceManager == nullptr) {
+ return;
+ }
+
+ nsresult result =
+ mCompositorSurfaceManager->OnSurfaceChanged(aWidgetId, aSurface);
+
+ // If our remote binder has died then notify the GPU process manager.
+ if (NS_FAILED(result)) {
+ if (mProcessToken) {
+ gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+ mProcessToken = 0;
+ }
+ }
+}
+#endif
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/UiCompositorControllerChild.h b/gfx/layers/ipc/UiCompositorControllerChild.h
new file mode 100644
index 0000000000..bf543ee6a3
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerChild.h
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef include_gfx_ipc_UiCompositorControllerChild_h
+#define include_gfx_ipc_UiCompositorControllerChild_h
+
+#include "mozilla/layers/PUiCompositorControllerChild.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/RefPtr.h"
+#include "nsThread.h"
+#ifdef MOZ_WIDGET_ANDROID
+# include "SurfaceTexture.h"
+# include "mozilla/java/CompositorSurfaceManagerWrappers.h"
+#endif
+
+class nsBaseWidget;
+
+namespace mozilla {
+namespace layers {
+
+class UiCompositorControllerChild final
+ : protected PUiCompositorControllerChild {
+ friend class PUiCompositorControllerChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerChild, final)
+
+ static RefPtr<UiCompositorControllerChild> CreateForSameProcess(
+ const LayersId& aRootLayerTreeId, nsBaseWidget* aWidget);
+ static RefPtr<UiCompositorControllerChild> CreateForGPUProcess(
+ const uint64_t& aProcessToken,
+ Endpoint<PUiCompositorControllerChild>&& aEndpoint,
+ nsBaseWidget* aWidget);
+
+ bool Pause();
+ bool Resume();
+ bool ResumeAndResize(const int32_t& aX, const int32_t& aY,
+ const int32_t& aHeight, const int32_t& aWidth);
+ bool InvalidateAndRender();
+ bool SetMaxToolbarHeight(const int32_t& aHeight);
+ bool SetFixedBottomOffset(int32_t aOffset);
+ bool ToolbarAnimatorMessageFromUI(const int32_t& aMessage);
+ bool SetDefaultClearColor(const uint32_t& aColor);
+ bool RequestScreenPixels();
+ bool EnableLayerUpdateNotifications(const bool& aEnable);
+
+ void Destroy();
+
+ bool DeallocPixelBuffer(Shmem& aMem);
+
+#ifdef MOZ_WIDGET_ANDROID
+ // Set mCompositorSurfaceManager. Must be called straight after initialization
+ // for GPU process controllers. Do not call for in-process controllers. This
+ // is separate from CreateForGPUProcess to avoid cluttering its declaration
+ // with JNI types.
+ void SetCompositorSurfaceManager(
+ java::CompositorSurfaceManager::Param aCompositorSurfaceManager);
+
+ // Send a Surface to the GPU process that a given widget ID should be
+ // composited in to. If not using a GPU process this function does nothing, as
+ // the InProcessCompositorWidget can read the Surface directly from the
+ // widget.
+ //
+ // Note that this function does not actually use the PUiCompositorController
+ // IPDL protocol, and instead uses Android's binder IPC mechanism via
+ // mCompositorSurfaceManager. It can be called from any thread.
+ void OnCompositorSurfaceChanged(int32_t aWidgetId,
+ java::sdk::Surface::Param aSurface);
+#endif
+
+ protected:
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ void ProcessingError(Result aCode, const char* aReason) override;
+ void HandleFatalError(const char* aMsg) override;
+ mozilla::ipc::IPCResult RecvToolbarAnimatorMessageFromCompositor(
+ const int32_t& aMessage);
+ mozilla::ipc::IPCResult RecvRootFrameMetrics(const ScreenPoint& aScrollOffset,
+ const CSSToScreenScale& aZoom);
+ mozilla::ipc::IPCResult RecvScreenPixels(Shmem&& aMem,
+ const ScreenIntSize& aSize,
+ bool aNeedsYFlip);
+
+ private:
+ explicit UiCompositorControllerChild(const uint64_t& aProcessToken,
+ nsBaseWidget* aWidget);
+ virtual ~UiCompositorControllerChild();
+ void OpenForSameProcess();
+ void OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint);
+ void SendCachedValues();
+
+ bool mIsOpen;
+ uint64_t mProcessToken;
+ Maybe<gfx::IntRect> mResize;
+ Maybe<int32_t> mMaxToolbarHeight;
+ Maybe<uint32_t> mDefaultClearColor;
+ Maybe<bool> mLayerUpdateEnabled;
+ RefPtr<nsBaseWidget> mWidget;
+ // Should only be set when compositor is in process.
+ RefPtr<UiCompositorControllerParent> mParent;
+
+#ifdef MOZ_WIDGET_ANDROID
+ // Android interface to send Surfaces to the GPU process. This uses Android
+ // binder rather than IPDL because Surfaces cannot be sent via IPDL. It lives
+ // here regardless because it is a conceptually logical location, even if the
+ // underlying IPC mechanism is different.
+ // This will be null if there is no GPU process.
+ mozilla::java::CompositorSurfaceManager::GlobalRef mCompositorSurfaceManager;
+#endif
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_gfx_ipc_UiCompositorControllerChild_h
diff --git a/gfx/layers/ipc/UiCompositorControllerMessageTypes.h b/gfx/layers/ipc/UiCompositorControllerMessageTypes.h
new file mode 100644
index 0000000000..3e68e7be31
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerMessageTypes.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef include_gfx_ipc_UiCompositorControllerMessageTypes_h
+#define include_gfx_ipc_UiCompositorControllerMessageTypes_h
+
+namespace mozilla {
+namespace layers {
+
+//
+// NOTE: These values are also defined in
+// mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+// and must be kept in sync. Any new message added here must also be added
+// there.
+//
+
+// clang-format off
+enum UiCompositorControllerMessageTypes {
+ FIRST_PAINT = 0, // Sent from compositor after first paint
+ LAYERS_UPDATED = 1, // Sent from the compositor when any layer has been updated
+ COMPOSITOR_CONTROLLER_OPEN = 2, // Compositor controller IPC is open
+ IS_COMPOSITOR_CONTROLLER_OPEN = 3 // Special message sent from controller to query if the compositor controller is open
+};
+// clang-format on
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_gfx_ipc_UiCompositorControllerMessageTypes_h
diff --git a/gfx/layers/ipc/UiCompositorControllerParent.cpp b/gfx/layers/ipc/UiCompositorControllerParent.cpp
new file mode 100644
index 0000000000..1e34969e84
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "UiCompositorControllerParent.h"
+
+#if defined(MOZ_WIDGET_ANDROID)
+# include "apz/src/APZCTreeManager.h"
+# include "mozilla/widget/AndroidCompositorWidget.h"
+#endif
+#include <utility>
+
+#include "FrameMetrics.h"
+#include "SynchronousTask.h"
+#include "mozilla/Unused.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/UiCompositorControllerMessageTypes.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+
+namespace mozilla {
+namespace layers {
+
+typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
+
+/* static */
+RefPtr<UiCompositorControllerParent>
+UiCompositorControllerParent::GetFromRootLayerTreeId(
+ const LayersId& aRootLayerTreeId) {
+ RefPtr<UiCompositorControllerParent> controller;
+ CompositorBridgeParent::CallWithIndirectShadowTree(
+ aRootLayerTreeId, [&](LayerTreeState& aState) -> void {
+ controller = aState.mUiControllerParent;
+ });
+ return controller;
+}
+
+/* static */
+RefPtr<UiCompositorControllerParent> UiCompositorControllerParent::Start(
+ const LayersId& aRootLayerTreeId,
+ Endpoint<PUiCompositorControllerParent>&& aEndpoint) {
+ RefPtr<UiCompositorControllerParent> parent =
+ new UiCompositorControllerParent(aRootLayerTreeId);
+
+ RefPtr<Runnable> task =
+ NewRunnableMethod<Endpoint<PUiCompositorControllerParent>&&>(
+ "layers::UiCompositorControllerParent::Open", parent,
+ &UiCompositorControllerParent::Open, std::move(aEndpoint));
+ CompositorThread()->Dispatch(task.forget());
+
+ return parent;
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvPause() {
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ parent->PauseComposition();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvResume(
+ bool* aOutResumed) {
+ *aOutResumed = false;
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ *aOutResumed = parent->ResumeComposition();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvResumeAndResize(
+ const int32_t& aX, const int32_t& aY, const int32_t& aWidth,
+ const int32_t& aHeight, bool* aOutResumed) {
+ *aOutResumed = false;
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ // Front-end expects a first paint callback upon resume/resize.
+ parent->ForceIsFirstPaint();
+#if defined(MOZ_WIDGET_ANDROID)
+ parent->GetWidget()->AsAndroid()->NotifyClientSizeChanged(
+ LayoutDeviceIntSize(aWidth, aHeight));
+#endif
+ *aOutResumed = parent->ResumeCompositionAndResize(aX, aY, aWidth, aHeight);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvInvalidateAndRender() {
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ parent->ScheduleComposition(wr::RenderReasons::OTHER);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvMaxToolbarHeight(
+ const int32_t& aHeight) {
+ mMaxToolbarHeight = aHeight;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvFixedBottomOffset(
+ const int32_t& aOffset) {
+#if defined(MOZ_WIDGET_ANDROID)
+ CompositorBridgeParent* parent =
+ CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
+ mRootLayerTreeId);
+ if (parent) {
+ parent->SetFixedLayerMargins(0, aOffset);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UiCompositorControllerParent::RecvDefaultClearColor(
+ const uint32_t& aColor) {
+ LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+
+ if (state && state->mWrBridge) {
+ state->mWrBridge->SetClearColor(gfx::DeviceColor::UnusualFromARGB(aColor));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvRequestScreenPixels() {
+#if defined(MOZ_WIDGET_ANDROID)
+ LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+
+ if (state && state->mWrBridge) {
+ state->mWrBridge->RequestScreenPixels(this);
+ state->mWrBridge->ScheduleForcedGenerateFrame(wr::RenderReasons::OTHER);
+ }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvEnableLayerUpdateNotifications(
+ const bool& aEnable) {
+#if defined(MOZ_WIDGET_ANDROID)
+ // Layers updates are need by Robocop test which enables them
+ mCompositorLayersUpdateEnabled = aEnable;
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ return IPC_OK();
+}
+
+void UiCompositorControllerParent::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ Shutdown();
+}
+
+void UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor(
+ int32_t aMessage) {
+ // This function can be call from ether compositor or controller thread.
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ CompositorThread()->Dispatch(NewRunnableMethod<int32_t>(
+ "layers::UiCompositorControllerParent::"
+ "ToolbarAnimatorMessageFromCompositor",
+ this,
+ &UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor,
+ aMessage));
+ return;
+ }
+
+ Unused << SendToolbarAnimatorMessageFromCompositor(aMessage);
+}
+
+bool UiCompositorControllerParent::AllocPixelBuffer(const int32_t aSize,
+ ipc::Shmem* aMem) {
+ MOZ_ASSERT(aSize > 0);
+ return AllocShmem(aSize, aMem);
+}
+
+void UiCompositorControllerParent::NotifyLayersUpdated() {
+#ifdef MOZ_WIDGET_ANDROID
+ if (mCompositorLayersUpdateEnabled) {
+ ToolbarAnimatorMessageFromCompositor(LAYERS_UPDATED);
+ }
+#endif
+}
+
+void UiCompositorControllerParent::NotifyFirstPaint() {
+ ToolbarAnimatorMessageFromCompositor(FIRST_PAINT);
+}
+
+void UiCompositorControllerParent::NotifyUpdateScreenMetrics(
+ const GeckoViewMetrics& aMetrics) {
+#if defined(MOZ_WIDGET_ANDROID)
+ // TODO: Need to handle different x-and y-scales.
+ CSSToScreenScale scale = ViewTargetAs<ScreenPixel>(
+ aMetrics.mZoom, PixelCastJustification::ScreenIsParentLayerForRoot);
+ ScreenPoint scrollOffset = aMetrics.mVisualScrollOffset * scale;
+ CompositorThread()->Dispatch(NewRunnableMethod<ScreenPoint, CSSToScreenScale>(
+ "UiCompositorControllerParent::SendRootFrameMetrics", this,
+ &UiCompositorControllerParent::SendRootFrameMetrics, scrollOffset,
+ scale));
+#endif
+}
+
+UiCompositorControllerParent::UiCompositorControllerParent(
+ const LayersId& aRootLayerTreeId)
+ : mRootLayerTreeId(aRootLayerTreeId)
+#ifdef MOZ_WIDGET_ANDROID
+ ,
+ mCompositorLayersUpdateEnabled(false)
+#endif
+ ,
+ mMaxToolbarHeight(0) {
+ MOZ_COUNT_CTOR(UiCompositorControllerParent);
+}
+
+UiCompositorControllerParent::~UiCompositorControllerParent() {
+ MOZ_COUNT_DTOR(UiCompositorControllerParent);
+}
+
+void UiCompositorControllerParent::InitializeForSameProcess() {
+ // This function is called by UiCompositorControllerChild in the main thread.
+ // So dispatch to the compositor thread to Initialize.
+ if (!CompositorThreadHolder::IsInCompositorThread()) {
+ SetOtherProcessId(base::GetCurrentProcId());
+ SynchronousTask task(
+ "UiCompositorControllerParent::InitializeForSameProcess");
+
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "UiCompositorControllerParent::InitializeForSameProcess", [&]() {
+ AutoCompleteTask complete(&task);
+ InitializeForSameProcess();
+ }));
+
+ task.Wait();
+ return;
+ }
+
+ Initialize();
+}
+
+void UiCompositorControllerParent::InitializeForOutOfProcess() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ Initialize();
+}
+
+void UiCompositorControllerParent::Initialize() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+ MOZ_ASSERT(state);
+ MOZ_ASSERT(state->mParent);
+ if (!state || !state->mParent) {
+ return;
+ }
+ state->mUiControllerParent = this;
+}
+
+void UiCompositorControllerParent::Open(
+ Endpoint<PUiCompositorControllerParent>&& aEndpoint) {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ if (!aEndpoint.Bind(this)) {
+ // We can't recover from this.
+ MOZ_CRASH("Failed to bind UiCompositorControllerParent to endpoint");
+ }
+ InitializeForOutOfProcess();
+}
+
+void UiCompositorControllerParent::Shutdown() {
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ LayerTreeState* state =
+ CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+ if (state) {
+ state->mUiControllerParent = nullptr;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/UiCompositorControllerParent.h b/gfx/layers/ipc/UiCompositorControllerParent.h
new file mode 100644
index 0000000000..e6284e7dfb
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerParent.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef include_gfx_ipc_UiCompositorControllerParent_h
+#define include_gfx_ipc_UiCompositorControllerParent_h
+
+#include "mozilla/layers/PUiCompositorControllerParent.h"
+#include "mozilla/layers/APZUtils.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace layers {
+
+struct FrameMetrics;
+
+class UiCompositorControllerParent final
+ : public PUiCompositorControllerParent {
+ // UiCompositorControllerChild needs to call the private constructor when
+ // running in process.
+ friend class UiCompositorControllerChild;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerParent, final)
+
+ static RefPtr<UiCompositorControllerParent> GetFromRootLayerTreeId(
+ const LayersId& aRootLayerTreeId);
+ static RefPtr<UiCompositorControllerParent> Start(
+ const LayersId& aRootLayerTreeId,
+ Endpoint<PUiCompositorControllerParent>&& aEndpoint);
+
+ // PUiCompositorControllerParent functions
+ mozilla::ipc::IPCResult RecvPause();
+ mozilla::ipc::IPCResult RecvResume(bool* aOutResumed);
+ mozilla::ipc::IPCResult RecvResumeAndResize(const int32_t& aX,
+ const int32_t& aY,
+ const int32_t& aHeight,
+ const int32_t& aWidth,
+ bool* aOutResumed);
+ mozilla::ipc::IPCResult RecvInvalidateAndRender();
+ mozilla::ipc::IPCResult RecvMaxToolbarHeight(const int32_t& aHeight);
+ mozilla::ipc::IPCResult RecvFixedBottomOffset(const int32_t& aOffset);
+ mozilla::ipc::IPCResult RecvDefaultClearColor(const uint32_t& aColor);
+ mozilla::ipc::IPCResult RecvRequestScreenPixels();
+ mozilla::ipc::IPCResult RecvEnableLayerUpdateNotifications(
+ const bool& aEnable);
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // Class specific functions
+ void ToolbarAnimatorMessageFromCompositor(int32_t aMessage);
+ bool AllocPixelBuffer(const int32_t aSize, Shmem* aMem);
+
+ // Called when a layer has been updated so the UI thread may be notified if
+ // necessary.
+ void NotifyLayersUpdated();
+ void NotifyFirstPaint();
+ void NotifyUpdateScreenMetrics(const GeckoViewMetrics& aMetrics);
+
+ private:
+ explicit UiCompositorControllerParent(const LayersId& aRootLayerTreeId);
+ virtual ~UiCompositorControllerParent();
+ void InitializeForSameProcess();
+ void InitializeForOutOfProcess();
+ void Initialize();
+ void Open(Endpoint<PUiCompositorControllerParent>&& aEndpoint);
+ void Shutdown();
+
+ LayersId mRootLayerTreeId;
+
+#if defined(MOZ_WIDGET_ANDROID)
+ bool mCompositorLayersUpdateEnabled; // Flag set to true when the UI thread
+ // is expecting to be notified when a
+ // layer has been updated
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ int32_t mMaxToolbarHeight;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_gfx_ipc_UiCompositorControllerParent_h
diff --git a/gfx/layers/ipc/VideoBridgeChild.cpp b/gfx/layers/ipc/VideoBridgeChild.cpp
new file mode 100644
index 0000000000..c2d5f2ec25
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeChild.cpp
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VideoBridgeChild.h"
+#include "VideoBridgeParent.h"
+#include "CompositorThread.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/StaticMutex.h"
+#include "transport/runnable_utils.h"
+#include "SynchronousTask.h"
+
+namespace mozilla {
+namespace layers {
+
+// Singleton
+static StaticMutex sVideoBridgeLock MOZ_UNANNOTATED;
+StaticRefPtr<VideoBridgeChild> sVideoBridge;
+
+/* static */
+void VideoBridgeChild::StartupForGPUProcess() {
+ ipc::Endpoint<PVideoBridgeParent> parentPipe;
+ ipc::Endpoint<PVideoBridgeChild> childPipe;
+
+ PVideoBridge::CreateEndpoints(base::GetCurrentProcId(),
+ base::GetCurrentProcId(), &parentPipe,
+ &childPipe);
+
+ VideoBridgeChild::Open(std::move(childPipe));
+ VideoBridgeParent::Open(std::move(parentPipe), VideoBridgeSource::GpuProcess);
+}
+
+/* static */
+void VideoBridgeChild::Open(Endpoint<PVideoBridgeChild>&& aEndpoint) {
+ StaticMutexAutoLock lock(sVideoBridgeLock);
+ MOZ_ASSERT(!sVideoBridge || !sVideoBridge->CanSend());
+ sVideoBridge = new VideoBridgeChild();
+
+ if (!aEndpoint.Bind(sVideoBridge)) {
+ // We can't recover from this.
+ MOZ_CRASH("Failed to bind VideoBridgeChild to endpoint");
+ }
+}
+
+/* static */
+void VideoBridgeChild::Shutdown() {
+ StaticMutexAutoLock lock(sVideoBridgeLock);
+ if (sVideoBridge) {
+ sVideoBridge->Close();
+ sVideoBridge = nullptr;
+ }
+}
+
+VideoBridgeChild::VideoBridgeChild()
+ : mThread(GetCurrentSerialEventTarget()), mCanSend(true) {}
+
+VideoBridgeChild::~VideoBridgeChild() = default;
+
+VideoBridgeChild* VideoBridgeChild::GetSingleton() {
+ StaticMutexAutoLock lock(sVideoBridgeLock);
+ return sVideoBridge;
+}
+
+bool VideoBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
+ if (!mThread->IsOnCurrentThread()) {
+ return DispatchAllocShmemInternal(aSize, aShmem, true); // true: unsafe
+ }
+
+ if (!CanSend()) {
+ return false;
+ }
+
+ return PVideoBridgeChild::AllocUnsafeShmem(aSize, aShmem);
+}
+
+bool VideoBridgeChild::AllocShmem(size_t aSize, ipc::Shmem* aShmem) {
+ MOZ_ASSERT(CanSend());
+ return PVideoBridgeChild::AllocShmem(aSize, aShmem);
+}
+
+void VideoBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
+ ipc::Shmem* aShmem, bool aUnsafe,
+ bool* aSuccess) {
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+
+ bool ok = false;
+ if (aUnsafe) {
+ ok = AllocUnsafeShmem(aSize, aShmem);
+ } else {
+ ok = AllocShmem(aSize, aShmem);
+ }
+ *aSuccess = ok;
+}
+
+bool VideoBridgeChild::DispatchAllocShmemInternal(size_t aSize,
+ ipc::Shmem* aShmem,
+ bool aUnsafe) {
+ SynchronousTask task("AllocatorProxy alloc");
+
+ bool success = false;
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<VideoBridgeChild>(this), &VideoBridgeChild::ProxyAllocShmemNow,
+ &task, aSize, aShmem, aUnsafe, &success);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+
+ return success;
+}
+
+void VideoBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
+ ipc::Shmem* aShmem, bool* aResult) {
+ AutoCompleteTask complete(aTask);
+
+ if (!CanSend()) {
+ return;
+ }
+ *aResult = DeallocShmem(*aShmem);
+}
+
+bool VideoBridgeChild::DeallocShmem(ipc::Shmem& aShmem) {
+ if (GetThread()->IsOnCurrentThread()) {
+ if (!CanSend()) {
+ return false;
+ }
+ return PVideoBridgeChild::DeallocShmem(aShmem);
+ }
+
+ SynchronousTask task("AllocatorProxy Dealloc");
+ bool result = false;
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<VideoBridgeChild>(this), &VideoBridgeChild::ProxyDeallocShmemNow,
+ &task, &aShmem, &result);
+ GetThread()->Dispatch(runnable.forget());
+
+ task.Wait();
+ return result;
+}
+
+PTextureChild* VideoBridgeChild::AllocPTextureChild(
+ const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&,
+ const TextureFlags&, const dom::ContentParentId& aContentId,
+ const uint64_t& aSerial) {
+ MOZ_ASSERT(CanSend());
+ return TextureClient::CreateIPDLActor();
+}
+
+bool VideoBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
+ return TextureClient::DestroyIPDLActor(actor);
+}
+
+void VideoBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mCanSend = false;
+}
+
+PTextureChild* VideoBridgeChild::CreateTexture(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ const dom::ContentParentId& aContentId, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(CanSend());
+ return SendPTextureConstructor(aSharedData, std::move(aReadLock),
+ aLayersBackend, aFlags, aContentId, aSerial);
+}
+
+bool VideoBridgeChild::IsSameProcess() const {
+ return OtherPid() == base::GetCurrentProcId();
+}
+
+void VideoBridgeChild::HandleFatalError(const char* aMsg) {
+ dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
+}
+
+mozilla::ipc::IPCResult VideoBridgeChild::RecvPing(PingResolver&& aResolver) {
+ aResolver(void_t{});
+ return IPC_OK();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/ipc/VideoBridgeChild.h b/gfx/layers/ipc/VideoBridgeChild.h
new file mode 100644
index 0000000000..b72289e4b6
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeChild.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_VIDEOBRIDGECHILD_H
+#define MOZILLA_GFX_VIDEOBRIDGECHILD_H
+
+#include "mozilla/layers/PVideoBridgeChild.h"
+#include "mozilla/layers/VideoBridgeUtils.h"
+#include "ISurfaceAllocator.h"
+#include "TextureForwarder.h"
+
+namespace mozilla {
+namespace layers {
+
+class SynchronousTask;
+
+class VideoBridgeChild final : public PVideoBridgeChild,
+ public TextureForwarder {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoBridgeChild, override);
+
+ static void StartupForGPUProcess();
+ static void Shutdown();
+
+ static VideoBridgeChild* GetSingleton();
+
+ // PVideoBridgeChild
+ PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
+ ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const dom::ContentParentId& aContentId,
+ const uint64_t& aSerial);
+ bool DeallocPTextureChild(PTextureChild* actor);
+
+ mozilla::ipc::IPCResult RecvPing(PingResolver&& aResolver);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // ISurfaceAllocator
+ bool AllocUnsafeShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+ bool AllocShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ // TextureForwarder
+ PTextureChild* CreateTexture(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+ LayersBackend aLayersBackend, TextureFlags aFlags,
+ const dom::ContentParentId& aContentId, uint64_t aSerial,
+ wr::MaybeExternalImageId& aExternalImageId) override;
+
+ // ClientIPCAllocator
+ base::ProcessId GetParentPid() const override { return OtherPid(); }
+ nsISerialEventTarget* GetThread() const override { return mThread; }
+ void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override {
+ MOZ_ASSERT(false, "NO RECYCLING HERE");
+ }
+
+ // ISurfaceAllocator
+ bool IsSameProcess() const override;
+
+ bool CanSend() { return mCanSend; }
+
+ static void Open(Endpoint<PVideoBridgeChild>&& aEndpoint);
+
+ protected:
+ void HandleFatalError(const char* aMsg) override;
+ bool DispatchAllocShmemInternal(size_t aSize, mozilla::ipc::Shmem* aShmem,
+ bool aUnsafe);
+ void ProxyAllocShmemNow(SynchronousTask* aTask, size_t aSize,
+ mozilla::ipc::Shmem* aShmem, bool aUnsafe,
+ bool* aSuccess);
+ void ProxyDeallocShmemNow(SynchronousTask* aTask, mozilla::ipc::Shmem* aShmem,
+ bool* aResult);
+
+ private:
+ VideoBridgeChild();
+ virtual ~VideoBridgeChild();
+
+ nsCOMPtr<nsISerialEventTarget> mThread;
+ bool mCanSend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/ipc/VideoBridgeParent.cpp b/gfx/layers/ipc/VideoBridgeParent.cpp
new file mode 100644
index 0000000000..6413c27bc1
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeParent.cpp
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VideoBridgeParent.h"
+#include "CompositorThread.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/VideoBridgeUtils.h"
+#include "mozilla/webrender/RenderThread.h"
+
+namespace mozilla::layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gfx;
+
+using VideoBridgeTable =
+ EnumeratedArray<VideoBridgeSource, VideoBridgeSource::_Count,
+ VideoBridgeParent*>;
+
+static StaticDataMutex<VideoBridgeTable> sVideoBridgeFromProcess(
+ "VideoBridges");
+static Atomic<bool> sVideoBridgeParentShutDown(false);
+
+VideoBridgeParent::VideoBridgeParent(VideoBridgeSource aSource)
+ : mMonitor("VideoBridgeParent::mMonitor"),
+ mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()),
+ mClosed(false) {
+ auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock();
+ switch (aSource) {
+ case VideoBridgeSource::RddProcess:
+ case VideoBridgeSource::GpuProcess:
+ case VideoBridgeSource::MFMediaEngineCDMProcess:
+ (*videoBridgeFromProcess)[aSource] = this;
+ break;
+ default:
+ MOZ_CRASH("Unhandled case");
+ }
+}
+
+VideoBridgeParent::~VideoBridgeParent() {
+ auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock();
+ for (auto& bridgeParent : *videoBridgeFromProcess) {
+ if (bridgeParent == this) {
+ bridgeParent = nullptr;
+ }
+ }
+}
+
+/* static */
+void VideoBridgeParent::Open(Endpoint<PVideoBridgeParent>&& aEndpoint,
+ VideoBridgeSource aSource) {
+ RefPtr<VideoBridgeParent> parent = new VideoBridgeParent(aSource);
+
+ CompositorThread()->Dispatch(
+ NewRunnableMethod<Endpoint<PVideoBridgeParent>&&>(
+ "gfx::layers::VideoBridgeParent::Bind", parent,
+ &VideoBridgeParent::Bind, std::move(aEndpoint)));
+}
+
+void VideoBridgeParent::Bind(Endpoint<PVideoBridgeParent>&& aEndpoint) {
+ if (!aEndpoint.Bind(this)) {
+ // We can't recover from this.
+ MOZ_CRASH("Failed to bind VideoBridgeParent to endpoint");
+ }
+}
+
+/* static */
+RefPtr<VideoBridgeParent> VideoBridgeParent::GetSingleton(
+ const Maybe<VideoBridgeSource>& aSource) {
+ MOZ_ASSERT(aSource.isSome());
+ auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock();
+ switch (aSource.value()) {
+ case VideoBridgeSource::RddProcess:
+ case VideoBridgeSource::GpuProcess:
+ case VideoBridgeSource::MFMediaEngineCDMProcess:
+ MOZ_ASSERT((*videoBridgeFromProcess)[aSource.value()]);
+ return RefPtr{(*videoBridgeFromProcess)[aSource.value()]};
+ default:
+ MOZ_CRASH("Unhandled case");
+ }
+}
+
+already_AddRefed<TextureHost> VideoBridgeParent::LookupTextureAsync(
+ const dom::ContentParentId& aContentId, uint64_t aSerial) {
+ MonitorAutoLock lock(mMonitor);
+
+ // We raced shutting down the actor.
+ if (NS_WARN_IF(!mCompositorThreadHolder)) {
+ MOZ_ASSERT_UNREACHABLE("Called on destroyed VideoBridgeParent actor!");
+ return nullptr;
+ }
+
+ MOZ_ASSERT(mCompositorThreadHolder->IsInThread());
+
+ auto* actor = mTextureMap[aSerial];
+ if (NS_WARN_IF(!actor)) {
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(aContentId != TextureHost::GetTextureContentId(actor))) {
+ return nullptr;
+ }
+
+ return do_AddRef(TextureHost::AsTextureHost(actor));
+}
+
+already_AddRefed<TextureHost> VideoBridgeParent::LookupTexture(
+ const dom::ContentParentId& aContentId, uint64_t aSerial) {
+ MonitorAutoLock lock(mMonitor);
+
+ // We raced shutting down the actor.
+ if (NS_WARN_IF(!mCompositorThreadHolder)) {
+ return nullptr;
+ }
+
+ auto* actor = mTextureMap[aSerial];
+ if (actor) {
+ if (NS_WARN_IF(aContentId != TextureHost::GetTextureContentId(actor))) {
+ return nullptr;
+ }
+ return do_AddRef(TextureHost::AsTextureHost(actor));
+ }
+
+ // We cannot block on the Compositor thread because that is the thread we get
+ // the IPC calls for the update on.
+ if (NS_WARN_IF(mCompositorThreadHolder->IsInThread())) {
+ MOZ_ASSERT_UNREACHABLE("Should never call on Compositor thread!");
+ return nullptr;
+ }
+
+ // Canvas may have raced ahead of VideoBridgeParent setting up the
+ // PTextureParent IPDL object. This should happen only rarely/briefly. Since
+ // we know that the PTexture constructor must be in the send queue, we can
+ // block until the IPDL ping comes back.
+ bool complete = false;
+
+ auto resolve = [&](void_t&&) {
+ MonitorAutoLock lock(mMonitor);
+ complete = true;
+ lock.NotifyAll();
+ };
+
+ auto reject = [&](ipc::ResponseRejectReason) {
+ MonitorAutoLock lock(mMonitor);
+ complete = true;
+ lock.NotifyAll();
+ };
+
+ mCompositorThreadHolder->Dispatch(
+ NS_NewRunnableFunction("VideoBridgeParent::LookupTexture", [&]() {
+ if (CanSend()) {
+ SendPing(std::move(resolve), std::move(reject));
+ } else {
+ reject(ipc::ResponseRejectReason::ChannelClosed);
+ }
+ }));
+
+ while (!complete) {
+ lock.Wait();
+ }
+
+ actor = mTextureMap[aSerial];
+ if (!actor) {
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(aContentId != TextureHost::GetTextureContentId(actor))) {
+ return nullptr;
+ }
+
+ return do_AddRef(TextureHost::AsTextureHost(actor));
+}
+
+void VideoBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+ bool shutdown = sVideoBridgeParentShutDown;
+
+ if (!shutdown && aWhy == AbnormalShutdown) {
+ gfxCriticalNote
+ << "VideoBridgeParent receives IPC close with reason=AbnormalShutdown";
+ }
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ // Can't alloc/dealloc shmems from now on.
+ mClosed = true;
+ mCompositorThreadHolder = nullptr;
+ }
+}
+
+/* static */
+void VideoBridgeParent::Shutdown() {
+ CompositorThread()->Dispatch(NS_NewRunnableFunction(
+ "VideoBridgeParent::Shutdown",
+ []() -> void { VideoBridgeParent::ShutdownInternal(); }));
+}
+
+/* static */
+void VideoBridgeParent::ShutdownInternal() {
+ sVideoBridgeParentShutDown = true;
+
+ nsTArray<RefPtr<VideoBridgeParent>> bridges;
+
+ // We don't want to hold the sVideoBridgeFromProcess lock when the
+ // VideoBridgeParent objects are closed without holding a reference to them.
+ {
+ auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock();
+ for (auto& bridgeParent : *videoBridgeFromProcess) {
+ if (bridgeParent) {
+ bridges.AppendElement(bridgeParent);
+ }
+ }
+ }
+
+ for (auto& bridge : bridges) {
+ bridge->Close();
+ }
+}
+
+/* static */
+void VideoBridgeParent::UnregisterExternalImages() {
+ MOZ_ASSERT(sVideoBridgeParentShutDown);
+
+ auto videoBridgeFromProcess = sVideoBridgeFromProcess.Lock();
+ for (auto& bridgeParent : *videoBridgeFromProcess) {
+ if (bridgeParent) {
+ bridgeParent->DoUnregisterExternalImages();
+ }
+ }
+}
+
+void VideoBridgeParent::DoUnregisterExternalImages() {
+ const ManagedContainer<PTextureParent>& textures = ManagedPTextureParent();
+ for (const auto& key : textures) {
+ RefPtr<TextureHost> texture = TextureHost::AsTextureHost(key);
+
+ if (texture) {
+ texture->MaybeDestroyRenderTexture();
+ }
+ }
+}
+
+PTextureParent* VideoBridgeParent::AllocPTextureParent(
+ const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
+ const dom::ContentParentId& aContentId, const uint64_t& aSerial) {
+ PTextureParent* parent = TextureHost::CreateIPDLActor(
+ this, aSharedData, std::move(aReadLock), aLayersBackend, aFlags,
+ aContentId, aSerial, Nothing());
+
+ if (!parent) {
+ return nullptr;
+ }
+
+ MonitorAutoLock lock(mMonitor);
+ mTextureMap[aSerial] = parent;
+ return parent;
+}
+
+bool VideoBridgeParent::DeallocPTextureParent(PTextureParent* actor) {
+ MonitorAutoLock lock(mMonitor);
+ mTextureMap.erase(TextureHost::GetTextureSerial(actor));
+ return TextureHost::DestroyIPDLActor(actor);
+}
+
+void VideoBridgeParent::SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) {
+ MOZ_ASSERT(false, "AsyncMessages not supported");
+}
+
+bool VideoBridgeParent::AllocShmem(size_t aSize, ipc::Shmem* aShmem) {
+ {
+ MonitorAutoLock lock(mMonitor);
+ if (mClosed) {
+ return false;
+ }
+ }
+ return PVideoBridgeParent::AllocShmem(aSize, aShmem);
+}
+
+bool VideoBridgeParent::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
+ {
+ MonitorAutoLock lock(mMonitor);
+ if (mClosed) {
+ return false;
+ }
+ }
+ return PVideoBridgeParent::AllocUnsafeShmem(aSize, aShmem);
+}
+
+bool VideoBridgeParent::DeallocShmem(ipc::Shmem& aShmem) {
+ {
+ MonitorAutoLock lock(mMonitor);
+ if (mCompositorThreadHolder && !mCompositorThreadHolder->IsInThread()) {
+ mCompositorThreadHolder->Dispatch(NS_NewRunnableFunction(
+ "gfx::layers::VideoBridgeParent::DeallocShmem",
+ [self = RefPtr{this}, shmem = std::move(aShmem)]() mutable {
+ self->DeallocShmem(shmem);
+ }));
+ return true;
+ }
+
+ 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 mozilla::layers
diff --git a/gfx/layers/ipc/VideoBridgeParent.h b/gfx/layers/ipc/VideoBridgeParent.h
new file mode 100644
index 0000000000..7a062a77d3
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeParent.h
@@ -0,0 +1,85 @@
+/* -*- 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/Monitor.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/PVideoBridgeParent.h"
+
+namespace mozilla::layers {
+
+enum class VideoBridgeSource : uint8_t;
+class CompositorThreadHolder;
+
+class VideoBridgeParent final : public PVideoBridgeParent,
+ public HostIPCAllocator,
+ public mozilla::ipc::IShmemAllocator {
+ public:
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(VideoBridgeParent, HostIPCAllocator)
+
+ static RefPtr<VideoBridgeParent> GetSingleton(
+ const Maybe<VideoBridgeSource>& aSource);
+
+ static void Open(Endpoint<PVideoBridgeParent>&& aEndpoint,
+ VideoBridgeSource aSource);
+ static void Shutdown();
+ static void UnregisterExternalImages();
+
+ already_AddRefed<TextureHost> LookupTextureAsync(
+ const dom::ContentParentId& aContentId, uint64_t aSerial);
+ already_AddRefed<TextureHost> LookupTexture(
+ const dom::ContentParentId& aContentId, uint64_t aSerial);
+
+ // PVideoBridgeParent
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
+ ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags,
+ const dom::ContentParentId& aContentId,
+ const uint64_t& aSerial);
+ bool DeallocPTextureParent(PTextureParent* actor);
+
+ // HostIPCAllocator
+ base::ProcessId GetChildProcessId() override { return OtherPid(); }
+ void NotifyNotUsed(PTextureParent* aTexture,
+ uint64_t aTransactionId) override;
+ void SendAsyncMessage(
+ const nsTArray<AsyncParentMessageData>& aMessage) override;
+
+ // ISurfaceAllocator
+ IShmemAllocator* AsShmemAllocator() override { return this; }
+ bool IsSameProcess() const override;
+ bool IPCOpen() const override { return !mClosed; }
+
+ // IShmemAllocator
+ bool AllocShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+
+ bool AllocUnsafeShmem(size_t aSize, mozilla::ipc::Shmem* aShmem) override;
+
+ bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
+
+ private:
+ ~VideoBridgeParent();
+
+ explicit VideoBridgeParent(VideoBridgeSource aSource);
+ void Bind(Endpoint<PVideoBridgeParent>&& aEndpoint);
+ static void ShutdownInternal();
+
+ void DoUnregisterExternalImages();
+
+ Monitor mMonitor;
+ RefPtr<CompositorThreadHolder> mCompositorThreadHolder
+ MOZ_GUARDED_BY(mMonitor);
+ std::map<uint64_t, PTextureParent*> mTextureMap MOZ_GUARDED_BY(mMonitor);
+ bool mClosed;
+};
+
+} // namespace mozilla::layers
+
+#endif // gfx_layers_ipc_VideoBridgeParent_h_
diff --git a/gfx/layers/ipc/VideoBridgeUtils.h b/gfx/layers/ipc/VideoBridgeUtils.h
new file mode 100644
index 0000000000..4163b5e08f
--- /dev/null
+++ b/gfx/layers/ipc/VideoBridgeUtils.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_VideoBridgeUtils_h
+#define IPC_VideoBridgeUtils_h
+
+#include "ipc/EnumSerializer.h"
+
+namespace mozilla {
+namespace layers {
+
+enum class VideoBridgeSource : uint8_t {
+ RddProcess,
+ GpuProcess,
+ MFMediaEngineCDMProcess,
+ _Count,
+};
+
+typedef Maybe<VideoBridgeSource> MaybeVideoBridgeSource;
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::VideoBridgeSource>
+ : public ContiguousEnumSerializer<
+ mozilla::layers::VideoBridgeSource,
+ mozilla::layers::VideoBridgeSource::RddProcess,
+ mozilla::layers::VideoBridgeSource::_Count> {};
+
+} // namespace IPC
+
+#endif // IPC_VideoBridgeUtils_h
diff --git a/gfx/layers/ipc/WebRenderMessages.ipdlh b/gfx/layers/ipc/WebRenderMessages.ipdlh
new file mode 100644
index 0000000000..e257346387
--- /dev/null
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+include LayersSurfaces;
+include LayersMessages;
+include protocol PTexture;
+
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositableHandleOwner from "mozilla/layers/LayersTypes.h";
+using mozilla::wr::LayoutSize from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::ImageDescriptor from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::ImageRendering from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::MixBlendMode from "mozilla/webrender/webrender_ffi.h";
+using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MaybeFontInstanceOptions from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MaybeFontInstancePlatformOptions from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::FontInstanceKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::BlobImageKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::WrRotation 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::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+namespace mozilla {
+namespace layers {
+
+struct RefCountedShmem {
+ Shmem buffer;
+};
+
+struct OpAddSharedExternalImage {
+ ExternalImageId externalImageId;
+ ImageKey key;
+};
+
+struct OpPushExternalImageForTexture {
+ ExternalImageId externalImageId;
+ ImageKey key;
+ PTexture texture;
+ bool isUpdate;
+};
+
+struct OpAddCompositorAnimations {
+ CompositorAnimations data;
+};
+
+struct OpAddPipelineIdForCompositable {
+ PipelineId pipelineId;
+ CompositableHandle handle;
+ CompositableHandleOwner owner;
+};
+
+struct OpRemovePipelineIdForCompositable {
+ PipelineId pipelineId;
+};
+
+struct OpReleaseTextureOfImage {
+ ImageKey key;
+};
+
+struct OpUpdateAsyncImagePipeline {
+ PipelineId pipelineId;
+ LayoutDeviceRect scBounds;
+ WrRotation rotation;
+ ImageRendering filter;
+ MixBlendMode mixBlendMode;
+};
+
+struct OpUpdatedAsyncImagePipeline {
+ PipelineId pipelineId;
+};
+
+union WebRenderParentCommand {
+ OpAddPipelineIdForCompositable;
+ OpRemovePipelineIdForCompositable;
+ OpReleaseTextureOfImage;
+ OpUpdateAsyncImagePipeline;
+ OpUpdatedAsyncImagePipeline;
+ CompositableOperation;
+ OpAddCompositorAnimations;
+};
+
+struct OffsetRange {
+ uint32_t source;
+ uint32_t start;
+ uint32_t length;
+};
+
+struct OpAddImage {
+ ImageDescriptor descriptor;
+ OffsetRange bytes;
+ uint16_t tiling;
+ ImageKey key;
+};
+
+struct OpAddBlobImage {
+ ImageDescriptor descriptor;
+ OffsetRange bytes;
+ ImageIntRect visibleRect;
+ uint16_t tiling;
+ BlobImageKey key;
+};
+
+struct OpUpdateImage {
+ ImageDescriptor descriptor;
+ OffsetRange bytes;
+ ImageKey key;
+};
+
+struct OpUpdateBlobImage {
+ ImageDescriptor descriptor;
+ OffsetRange bytes;
+ BlobImageKey key;
+ ImageIntRect visibleRect;
+ ImageIntRect dirtyRect;
+};
+
+struct OpSetBlobImageVisibleArea {
+ ImageIntRect area;
+ BlobImageKey key;
+};
+
+struct OpUpdateSharedExternalImage {
+ ExternalImageId externalImageId;
+ ImageKey key;
+ ImageIntRect dirtyRect;
+};
+
+struct OpDeleteImage {
+ ImageKey key;
+};
+
+struct OpDeleteBlobImage {
+ BlobImageKey key;
+};
+
+struct OpAddRawFont {
+ OffsetRange bytes;
+ uint32_t fontIndex;
+ FontKey key;
+};
+
+struct OpAddFontDescriptor {
+ OffsetRange bytes;
+ uint32_t fontIndex;
+ FontKey key;
+};
+
+struct OpDeleteFont {
+ FontKey key;
+};
+
+struct OpAddFontInstance {
+ MaybeFontInstanceOptions options;
+ MaybeFontInstancePlatformOptions platformOptions;
+ OffsetRange variations;
+ FontInstanceKey instanceKey;
+ FontKey fontKey;
+ float glyphSize;
+};
+
+struct OpDeleteFontInstance {
+ FontInstanceKey key;
+};
+
+union OpUpdateResource {
+ OpAddImage;
+ OpAddBlobImage;
+ OpUpdateImage;
+ OpUpdateBlobImage;
+ OpSetBlobImageVisibleArea;
+ OpDeleteImage;
+ OpDeleteBlobImage;
+ OpAddRawFont;
+ OpAddFontDescriptor;
+ OpDeleteFont;
+ OpAddFontInstance;
+ OpDeleteFontInstance;
+ OpAddSharedExternalImage;
+ OpPushExternalImageForTexture;
+ OpUpdateSharedExternalImage;
+};
+
+} // namespace
+} // namespace