summaryrefslogtreecommitdiffstats
path: root/gfx/layers/ipc/CompositorBridgeParent.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /gfx/layers/ipc/CompositorBridgeParent.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/ipc/CompositorBridgeParent.cpp')
-rw-r--r--gfx/layers/ipc/CompositorBridgeParent.cpp2008
1 files changed, 2008 insertions, 0 deletions
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