mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-03 18:47:53 +00:00
1980 lines
68 KiB
C++
1980 lines
68 KiB
C++
/* -*- 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);
|
|
|
|
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),
|
|
mApzInputBridgeParent(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CompositorBridgeParent::FinishShutdown() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// 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),
|
|
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();
|
|
|
|
{ // 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, /* aBlocking */ true);
|
|
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, /* aBlocking */ 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();
|
|
|
|
{ // 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::SetAPZInputBridgeParent(
|
|
const LayersId& aLayersId, APZInputBridgeParent* aInputBridgeParent) {
|
|
MOZ_RELEASE_ASSERT(XRE_IsGPUProcess());
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
StaticMonitorAutoLock lock(CompositorBridgeParent::sIndirectLayerTreesLock);
|
|
CompositorBridgeParent::LayerTreeState& state =
|
|
CompositorBridgeParent::sIndirectLayerTrees[aLayersId];
|
|
MOZ_ASSERT(!state.mApzInputBridgeParent);
|
|
state.mApzInputBridgeParent = aInputBridgeParent;
|
|
}
|
|
|
|
void CompositorBridgeParent::AllocateAPZCTreeManagerParent(
|
|
const StaticMonitorAutoLock& aProofOfLayerTreeStateLock,
|
|
const LayersId& aLayersId, LayerTreeState& aState) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
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, /* aBlocking */ true);
|
|
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::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::SetWebRenderSlowCpuFrameThresholdListener(
|
|
&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(¬ifications);
|
|
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::GetIndirectShadowTreeInternal(LayersId aId) {
|
|
StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
|
|
LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aId);
|
|
if (sIndirectLayerTrees.end() == cit) {
|
|
return nullptr;
|
|
}
|
|
return &cit->second;
|
|
}
|
|
|
|
/* static */
|
|
bool CompositorBridgeParent::HasIndirectShadowTree(LayersId aId) {
|
|
return GetIndirectShadowTreeInternal(aId) != nullptr;
|
|
}
|
|
|
|
/* static */ CompositorBridgeParent::LayerTreeState*
|
|
CompositorBridgeParent::GetIndirectShadowTree(LayersId aId) {
|
|
// Only the compositor thread should use this method variant
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
|
|
return GetIndirectShadowTreeInternal(aId);
|
|
}
|
|
|
|
/* 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 */
|
|
APZInputBridgeParent* CompositorBridgeParent::GetApzInputBridgeParentForRoot(
|
|
LayersId aContentLayersId) {
|
|
StaticMonitorAutoLock lock(sIndirectLayerTreesLock);
|
|
CompositorBridgeParent::LayerTreeState* state =
|
|
GetStateForRoot(aContentLayersId, lock);
|
|
return state ? state->mApzInputBridgeParent : 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.AccumulateSingleSample(
|
|
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.AccumulateSingleSample(
|
|
static_cast<unsigned long long>(fracLatencyNorm));
|
|
|
|
if (aContainsSVGGroup) {
|
|
mozilla::glean::gfx_content_frame_time::with_svg.AccumulateSingleSample(
|
|
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
|
|
.AccumulateSingleSample(
|
|
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
|
|
.AccumulateSingleSample(
|
|
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
|