diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/ipc/LayerTransactionParent.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/ipc/LayerTransactionParent.cpp')
-rw-r--r-- | gfx/layers/ipc/LayerTransactionParent.cpp | 1020 |
1 files changed, 1020 insertions, 0 deletions
diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp new file mode 100644 index 0000000000..e7dc07f1d9 --- /dev/null +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -0,0 +1,1020 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "LayerTransactionParent.h" +#include <vector> // for vector +#include "CompositableHost.h" // for CompositableParent, Get, etc +#include "ImageLayers.h" // for ImageLayer +#include "Layers.h" // for Layer, ContainerLayer, etc +#include "CompositableTransactionParent.h" // for EditReplyVector +#include "CompositorBridgeParent.h" +#include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D +#include "mozilla/layers/AnimationHelper.h" // for GetAnimatedPropValue +#include "mozilla/layers/CanvasLayerComposite.h" +#include "mozilla/layers/ColorLayerComposite.h" +#include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/CompositorAnimationStorage.h" // for CompositorAnimationStorage +#include "mozilla/layers/ContainerLayerComposite.h" +#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent +#include "mozilla/layers/ImageLayerComposite.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayersMessages.h" // for EditReply, etc +#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG +#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL +#include "mozilla/layers/PaintedLayerComposite.h" +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "mozilla/PerfStats.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/Telemetry.h" +#include "mozilla/Unused.h" +#include "nsCoord.h" // for NSAppUnitsToFloatPixels +#include "nsISupportsImpl.h" // for Layer::Release, etc +#include "nsLayoutUtils.h" // for nsLayoutUtils +#include "nsMathUtils.h" // for NS_round +#include "nsPoint.h" // for nsPoint +#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc +#include "TreeTraversal.h" // for ForEachNode +#include "GeckoProfiler.h" +#include "mozilla/layers/TextureHost.h" +#include "mozilla/layers/AsyncCompositionManager.h" + +using mozilla::Telemetry::LABELS_CONTENT_FRAME_TIME_REASON; + +namespace mozilla { +namespace layers { + +//-------------------------------------------------- +// LayerTransactionParent +LayerTransactionParent::LayerTransactionParent( + HostLayerManager* aManager, CompositorBridgeParentBase* aBridge, + CompositorAnimationStorage* aAnimStorage, LayersId aId, + TimeDuration aVsyncRate) + : mLayerManager(aManager), + mCompositorBridge(aBridge), + mAnimStorage(aAnimStorage), + mId(aId), + mChildEpoch{0}, + mParentEpoch{0}, + mVsyncRate(aVsyncRate), + mDestroyed(false), + mIPCOpen(false), + mUpdateHitTestingTree(false) { + MOZ_ASSERT(mId.IsValid()); +} + +LayerTransactionParent::~LayerTransactionParent() = default; + +void LayerTransactionParent::SetLayerManager( + HostLayerManager* aLayerManager, CompositorAnimationStorage* aAnimStorage) { + if (mDestroyed) { + return; + } + mLayerManager = aLayerManager; + for (auto iter = mLayerMap.Iter(); !iter.Done(); iter.Next()) { + auto layer = iter.Data(); + if (mAnimStorage && layer->GetCompositorAnimationsId()) { + mAnimStorage->ClearById(layer->GetCompositorAnimationsId()); + } + layer->AsHostLayer()->SetLayerManager(aLayerManager); + } + mAnimStorage = aAnimStorage; +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvShutdown() { + Destroy(); + IProtocol* mgr = Manager(); + if (!Send__delete__(this)) { + return IPC_FAIL_NO_REASON(mgr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvShutdownSync() { + return RecvShutdown(); +} + +void LayerTransactionParent::Destroy() { + if (mDestroyed) { + return; + } + mDestroyed = true; + if (mAnimStorage) { + for (auto iter = mLayerMap.Iter(); !iter.Done(); iter.Next()) { + auto layer = iter.Data(); + if (layer->GetCompositorAnimationsId()) { + mAnimStorage->ClearById(layer->GetCompositorAnimationsId()); + } + layer->Disconnect(); + } + } + mCompositables.clear(); + mAnimStorage = nullptr; +} + +class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender final { + public: + explicit AutoLayerTransactionParentAsyncMessageSender( + LayerTransactionParent* aLayerTransaction, + const nsTArray<OpDestroy>* aDestroyActors = nullptr) + : mLayerTransaction(aLayerTransaction), mActorsToDestroy(aDestroyActors) { + mLayerTransaction->SetAboutToSendAsyncMessages(); + } + + ~AutoLayerTransactionParentAsyncMessageSender() { + mLayerTransaction->SendPendingAsyncMessages(); + if (mActorsToDestroy) { + // Destroy the actors after sending the async messages because the latter + // may contain references to some actors. + for (const auto& op : *mActorsToDestroy) { + mLayerTransaction->DestroyActor(op); + } + } + } + + private: + LayerTransactionParent* mLayerTransaction; + const nsTArray<OpDestroy>* mActorsToDestroy; +}; + +mozilla::ipc::IPCResult LayerTransactionParent::RecvPaintTime( + const TransactionId& aTransactionId, const TimeDuration& aPaintTime) { + mCompositorBridge->UpdatePaintTime(this, aPaintTime); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvUpdate( + const TransactionInfo& aInfo) { + AUTO_PROFILER_TRACING_MARKER("Paint", "LayerTransaction", GRAPHICS); + AUTO_PROFILER_LABEL("LayerTransactionParent::RecvUpdate", GRAPHICS); + PerfStats::AutoMetricRecording<PerfStats::Metric::LayerTransactions> + autoRecording; + + TimeStamp updateStart = TimeStamp::Now(); + + MOZ_LAYERS_LOG( + ("[ParentSide] received txn with %zu edits", aInfo.cset().Length())); + + UpdateFwdTransactionId(aInfo.fwdTransactionId()); + + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + for (const auto& op : aInfo.toDestroy()) { + DestroyActor(op); + } + return IPC_OK(); + } + + // This ensures that destroy operations are always processed. It is not safe + // to early-return from RecvUpdate without doing so. + AutoLayerTransactionParentAsyncMessageSender autoAsyncMessageSender( + this, &aInfo.toDestroy()); + + { + AutoResolveRefLayers resolve( + mCompositorBridge->GetCompositionManager(this)); + nsCString none; + mLayerManager->BeginTransaction(none); + } + + // Not all edits require an update to the hit testing tree. + mUpdateHitTestingTree = false; + + for (EditArray::index_type i = 0; i < aInfo.cset().Length(); ++i) { + const Edit& edit = const_cast<Edit&>(aInfo.cset()[i]); + + switch (edit.type()) { + // Create* ops + case Edit::TOpCreatePaintedLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreatePaintedLayer")); + + RefPtr<PaintedLayer> layer = mLayerManager->CreatePaintedLayer(); + if (!BindLayer(layer, edit.get_OpCreatePaintedLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreatePaintedLayer"); + break; + } + case Edit::TOpCreateContainerLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer")); + + RefPtr<ContainerLayer> layer = mLayerManager->CreateContainerLayer(); + if (!BindLayer(layer, edit.get_OpCreateContainerLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateContainerLayer"); + break; + } + case Edit::TOpCreateImageLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer")); + + RefPtr<ImageLayer> layer = mLayerManager->CreateImageLayer(); + if (!BindLayer(layer, edit.get_OpCreateImageLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateImageLayer"); + break; + } + case Edit::TOpCreateColorLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer")); + + RefPtr<ColorLayer> layer = mLayerManager->CreateColorLayer(); + if (!BindLayer(layer, edit.get_OpCreateColorLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateColorLayer"); + break; + } + case Edit::TOpCreateCanvasLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer")); + + RefPtr<CanvasLayer> layer = mLayerManager->CreateCanvasLayer(); + if (!BindLayer(layer, edit.get_OpCreateCanvasLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateCanvasLayer"); + break; + } + case Edit::TOpCreateRefLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer")); + + RefPtr<RefLayer> layer = mLayerManager->CreateRefLayer(); + if (!BindLayer(layer, edit.get_OpCreateRefLayer())) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "CreateRefLayer"); + break; + } + case Edit::TOpSetDiagnosticTypes: { + mLayerManager->SetDiagnosticTypes( + edit.get_OpSetDiagnosticTypes().diagnostics()); + break; + } + // Tree ops + case Edit::TOpSetRoot: { + MOZ_LAYERS_LOG(("[ParentSide] SetRoot")); + + Layer* newRoot = AsLayer(edit.get_OpSetRoot().root()); + if (!newRoot) { + return IPC_FAIL_NO_REASON(this); + } + if (newRoot->GetParent()) { + // newRoot is not a root! + return IPC_FAIL_NO_REASON(this); + } + mRoot = newRoot; + + UpdateHitTestingTree(mRoot, "SetRoot"); + break; + } + case Edit::TOpInsertAfter: { + MOZ_LAYERS_LOG(("[ParentSide] InsertAfter")); + + const OpInsertAfter& oia = edit.get_OpInsertAfter(); + Layer* child = AsLayer(oia.childLayer()); + Layer* layer = AsLayer(oia.container()); + Layer* after = AsLayer(oia.after()); + if (!child || !layer || !after) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->InsertAfter(child, after)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "InsertAfter"); + break; + } + case Edit::TOpPrependChild: { + MOZ_LAYERS_LOG(("[ParentSide] PrependChild")); + + const OpPrependChild& oac = edit.get_OpPrependChild(); + Layer* child = AsLayer(oac.childLayer()); + Layer* layer = AsLayer(oac.container()); + if (!child || !layer) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->InsertAfter(child, nullptr)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "PrependChild"); + break; + } + case Edit::TOpRemoveChild: { + MOZ_LAYERS_LOG(("[ParentSide] RemoveChild")); + + const OpRemoveChild& orc = edit.get_OpRemoveChild(); + Layer* childLayer = AsLayer(orc.childLayer()); + Layer* layer = AsLayer(orc.container()); + if (!childLayer || !layer) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->RemoveChild(childLayer)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "RemoveChild"); + break; + } + case Edit::TOpRepositionChild: { + MOZ_LAYERS_LOG(("[ParentSide] RepositionChild")); + + const OpRepositionChild& orc = edit.get_OpRepositionChild(); + Layer* child = AsLayer(orc.childLayer()); + Layer* after = AsLayer(orc.after()); + Layer* layer = AsLayer(orc.container()); + if (!child || !layer || !after) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->RepositionChild(child, after)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "RepositionChild"); + break; + } + case Edit::TOpRaiseToTopChild: { + MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild")); + + const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild(); + Layer* child = AsLayer(rtc.childLayer()); + if (!child) { + return IPC_FAIL_NO_REASON(this); + } + Layer* layer = AsLayer(rtc.container()); + if (!layer) { + return IPC_FAIL_NO_REASON(this); + } + ContainerLayer* container = layer->AsContainerLayer(); + if (!container || !container->RepositionChild(child, nullptr)) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateHitTestingTree(layer, "RaiseToTopChild"); + break; + } + case Edit::TCompositableOperation: { + if (!ReceiveCompositableUpdate(edit.get_CompositableOperation())) { + return IPC_FAIL_NO_REASON(this); + } + break; + } + case Edit::TOpAttachCompositable: { + const OpAttachCompositable& op = edit.get_OpAttachCompositable(); + RefPtr<CompositableHost> host = FindCompositable(op.compositable()); + if (!Attach(AsLayer(op.layer()), host, false)) { + return IPC_FAIL_NO_REASON(this); + } + host->SetCompositorBridgeID(mLayerManager->GetCompositorBridgeID()); + break; + } + case Edit::TOpAttachAsyncCompositable: { + const OpAttachAsyncCompositable& op = + edit.get_OpAttachAsyncCompositable(); + RefPtr<ImageBridgeParent> imageBridge = + ImageBridgeParent::GetInstance(OtherPid()); + if (!imageBridge) { + return IPC_FAIL_NO_REASON(this); + } + RefPtr<CompositableHost> host = imageBridge->FindCompositable( + op.compositable(), /* aAllowDisablingWebRender */ true); + if (!host) { + // This normally should not happen, but can after a GPU process crash. + // Media may not have had time to update the ImageContainer associated + // with a video frame, and we may try to attach a stale + // CompositableHandle. Rather than break the whole transaction, we + // just continue. + gfxCriticalNote << "CompositableHost " << op.compositable().Value() + << " not found"; + continue; + } + if (!Attach(AsLayer(op.layer()), host, true)) { + return IPC_FAIL_NO_REASON(this); + } + host->SetCompositorBridgeID(mLayerManager->GetCompositorBridgeID()); + break; + } + default: + MOZ_CRASH("not reached"); + } + } + + // Process simple attribute updates. + for (const auto& op : aInfo.setSimpleAttrs()) { + MOZ_LAYERS_LOG(("[ParentSide] SetSimpleLayerAttributes")); + Layer* layer = AsLayer(op.layer()); + if (!layer) { + return IPC_FAIL_NO_REASON(this); + } + const SimpleLayerAttributes& attrs = op.attrs(); + const SimpleLayerAttributes& orig = layer->GetSimpleAttributes(); + if (!attrs.HitTestingInfoIsEqual(orig)) { + UpdateHitTestingTree(layer, "scrolling info changed"); + } + layer->SetSimpleAttributes(op.attrs()); + } + + // Process attribute updates. + for (const auto& op : aInfo.setAttrs()) { + MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes")); + if (!SetLayerAttributes(op)) { + return IPC_FAIL_NO_REASON(this); + } + } + + // Process paints separately, after all normal edits. + for (const auto& op : aInfo.paints()) { + if (!ReceiveCompositableUpdate(op)) { + return IPC_FAIL_NO_REASON(this); + } + } + + mCompositorBridge->ShadowLayersUpdated(this, aInfo, mUpdateHitTestingTree); + + { + AutoResolveRefLayers resolve( + mCompositorBridge->GetCompositionManager(this)); + mLayerManager->EndTransaction(TimeStamp(), + LayerManager::END_NO_IMMEDIATE_REDRAW); + } + + if (!IsSameProcess()) { + // Ensure that any pending operations involving back and front + // buffers have completed, so that neither process stomps on the + // other's buffer contents. + LayerManagerComposite::PlatformSyncBeforeReplyUpdate(); + } + +#ifdef COMPOSITOR_PERFORMANCE_WARNING + int compositeTime = + (int)(mozilla::TimeStamp::Now() - updateStart).ToMilliseconds(); + if (compositeTime > 15) { + printf_stderr("Compositor: Layers update took %i ms (blocking gecko).\n", + compositeTime); + } +#endif + + // Enable visual warning for long transaction when draw FPS option is enabled + bool drawFps = StaticPrefs::layers_acceleration_draw_fps(); + if (drawFps) { + uint32_t visualWarningTrigger = + StaticPrefs::layers_transaction_warning_ms(); + // The default theshold is 200ms to trigger, hit red when it take 4 times + // longer + TimeDuration latency = TimeStamp::Now() - aInfo.transactionStart(); + if (latency > TimeDuration::FromMilliseconds(visualWarningTrigger)) { + float severity = + (latency - TimeDuration::FromMilliseconds(visualWarningTrigger)) + .ToMilliseconds() / + (4 * visualWarningTrigger); + if (severity > 1.f) { + severity = 1.f; + } + mLayerManager->VisualFrameWarning(severity); + printf_stderr( + "LayerTransactionParent::RecvUpdate transaction from process %d took " + "%f ms", + OtherPid(), latency.ToMilliseconds()); + } + + mLayerManager->RecordUpdateTime( + (TimeStamp::Now() - updateStart).ToMilliseconds()); + } + + return IPC_OK(); +} + +bool LayerTransactionParent::SetLayerAttributes( + const OpSetLayerAttributes& aOp) { + Layer* layer = AsLayer(aOp.layer()); + if (!layer) { + return false; + } + + const LayerAttributes& attrs = aOp.attrs(); + const CommonLayerAttributes& common = attrs.common(); + if (common.visibleRegion() != layer->GetVisibleRegion()) { + UpdateHitTestingTree(layer, "visible region changed"); + layer->SetVisibleRegion(common.visibleRegion()); + } + if (common.eventRegions() != layer->GetEventRegions()) { + UpdateHitTestingTree(layer, "event regions changed"); + layer->SetEventRegions(common.eventRegions()); + } + Maybe<ParentLayerIntRect> clipRect = + common.useClipRect() ? Some(common.clipRect()) : Nothing(); + if (clipRect != layer->GetClipRect()) { + UpdateHitTestingTree(layer, "clip rect changed"); + layer->SetClipRect(clipRect); + } + if (LayerHandle maskLayer = common.maskLayer()) { + layer->SetMaskLayer(AsLayer(maskLayer)); + } else { + layer->SetMaskLayer(nullptr); + } + layer->SetCompositorAnimations(mId, common.compositorAnimations()); + // Clean up the Animations by id in the CompositorAnimationStorage + // if there are no active animations on the layer + if (mAnimStorage && layer->GetCompositorAnimationsId() && + layer->GetPropertyAnimationGroups().IsEmpty()) { + mAnimStorage->ClearById(layer->GetCompositorAnimationsId()); + } + if (common.scrollMetadata() != layer->GetAllScrollMetadata()) { + UpdateHitTestingTree(layer, "scroll metadata changed"); + layer->SetScrollMetadata(common.scrollMetadata()); + } + layer->SetDisplayListLog(common.displayListLog().get()); + + // The updated invalid region is added to the existing one, since we can + // update multiple times before the next composite. + layer->AddInvalidRegion(common.invalidRegion()); + + nsTArray<RefPtr<Layer>> maskLayers; + for (size_t i = 0; i < common.ancestorMaskLayers().Length(); i++) { + Layer* maskLayer = AsLayer(common.ancestorMaskLayers().ElementAt(i)); + if (!maskLayer) { + return false; + } + maskLayers.AppendElement(maskLayer); + } + layer->SetAncestorMaskLayers(maskLayers); + + typedef SpecificLayerAttributes Specific; + const SpecificLayerAttributes& specific = attrs.specific(); + switch (specific.type()) { + case Specific::Tnull_t: + break; + + case Specific::TPaintedLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] painted layer")); + + PaintedLayer* paintedLayer = layer->AsPaintedLayer(); + if (!paintedLayer) { + return false; + } + const PaintedLayerAttributes& attrs = + specific.get_PaintedLayerAttributes(); + + paintedLayer->SetValidRegion(attrs.validRegion()); + break; + } + case Specific::TContainerLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] container layer")); + + ContainerLayer* containerLayer = layer->AsContainerLayer(); + if (!containerLayer) { + return false; + } + const ContainerLayerAttributes& attrs = + specific.get_ContainerLayerAttributes(); + containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale()); + containerLayer->SetInheritedScale(attrs.inheritedXScale(), + attrs.inheritedYScale()); + containerLayer->SetScaleToResolution(attrs.presShellResolution()); + break; + } + case Specific::TColorLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] color layer")); + + ColorLayer* colorLayer = layer->AsColorLayer(); + if (!colorLayer) { + return false; + } + colorLayer->SetColor(specific.get_ColorLayerAttributes().color().value()); + colorLayer->SetBounds(specific.get_ColorLayerAttributes().bounds()); + break; + } + case Specific::TCanvasLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] canvas layer")); + + CanvasLayer* canvasLayer = layer->AsCanvasLayer(); + if (!canvasLayer) { + return false; + } + canvasLayer->SetSamplingFilter( + specific.get_CanvasLayerAttributes().samplingFilter()); + canvasLayer->SetBounds(specific.get_CanvasLayerAttributes().bounds()); + break; + } + case Specific::TRefLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] ref layer")); + + RefLayer* refLayer = layer->AsRefLayer(); + if (!refLayer) { + return false; + } + refLayer->SetReferentId(specific.get_RefLayerAttributes().id()); + refLayer->SetEventRegionsOverride( + specific.get_RefLayerAttributes().eventRegionsOverride()); + refLayer->SetRemoteDocumentSize( + specific.get_RefLayerAttributes().remoteDocumentSize()); + UpdateHitTestingTree(layer, "ref layer attributes changed"); + break; + } + case Specific::TImageLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] image layer")); + + ImageLayer* imageLayer = layer->AsImageLayer(); + if (!imageLayer) { + return false; + } + const ImageLayerAttributes& attrs = specific.get_ImageLayerAttributes(); + imageLayer->SetSamplingFilter(attrs.samplingFilter()); + imageLayer->SetScaleToSize(attrs.scaleToSize(), attrs.scaleMode()); + break; + } + default: + MOZ_CRASH("not reached"); + } + + return true; +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetLayersObserverEpoch( + const LayersObserverEpoch& aChildEpoch) { + mChildEpoch = aChildEpoch; + return IPC_OK(); +} + +bool LayerTransactionParent::ShouldParentObserveEpoch() { + if (mParentEpoch == mChildEpoch) { + return false; + } + + mParentEpoch = mChildEpoch; + return true; +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetTestSampleTime( + const TimeStamp& aTime) { + if (!mCompositorBridge->SetTestSampleTime(GetId(), aTime)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvLeaveTestMode() { + if (mDestroyed) { + return IPC_FAIL_NO_REASON(this); + } + + mCompositorBridge->LeaveTestMode(GetId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetAnimationValue( + const uint64_t& aCompositorAnimationsId, OMTAValue* aValue) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + return IPC_FAIL_NO_REASON(this); + } + + // Make sure we apply the latest animation style or else we can end up with + // a race between when we temporarily clear the animation transform (in + // CompositorBridgeParent::SetShadowProperties) and when animation + // recalculates the value. + mCompositorBridge->ApplyAsyncProperties( + this, CompositorBridgeParentBase::TransformsToSkip::APZ); + + if (!mAnimStorage) { + return IPC_FAIL_NO_REASON(this); + } + + *aValue = mAnimStorage->GetOMTAValue(aCompositorAnimationsId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetTransform( + const LayerHandle& aLayerHandle, Maybe<Matrix4x4>* aTransform) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + return IPC_FAIL_NO_REASON(this); + } + + Layer* layer = AsLayer(aLayerHandle); + if (!layer) { + return IPC_FAIL_NO_REASON(this); + } + + mCompositorBridge->ApplyAsyncProperties( + this, CompositorBridgeParentBase::TransformsToSkip::NoneOfThem); + + Matrix4x4 transform = layer->AsHostLayer()->GetShadowBaseTransform(); + // Undo the scale transform applied by FrameTransformToTransformInDevice in + // AsyncCompositionManager.cpp. + if (ContainerLayer* c = layer->AsContainerLayer()) { + transform.PostScale(1.0f / c->GetInheritedXScale(), + 1.0f / c->GetInheritedYScale(), 1.0f); + } + float scale = 1; + Point3D scaledOrigin; + if (layer->GetTransformData()) { + const TransformData& data = *layer->GetTransformData(); + scale = data.appUnitsPerDevPixel(); + scaledOrigin = Point3D( + NS_round(NSAppUnitsToFloatPixels(data.origin().x, scale)), + NS_round(NSAppUnitsToFloatPixels(data.origin().y, scale)), 0.0f); + } + + // If our parent isn't a perspective layer, then the offset into reference + // frame coordinates will have been applied to us. Add an inverse translation + // to cancel it out. + if (!layer->GetParent() || !layer->GetParent()->GetTransformIsPerspective()) { + transform.PostTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z); + } + + *aTransform = Some(transform); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetAsyncScrollOffset( + const ScrollableLayerGuid::ViewID& aScrollID, const float& aX, + const float& aY) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + return IPC_FAIL_NO_REASON(this); + } + + mCompositorBridge->SetTestAsyncScrollOffset(GetId(), aScrollID, + CSSPoint(aX, aY)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetAsyncZoom( + const ScrollableLayerGuid::ViewID& aScrollID, const float& aValue) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + return IPC_FAIL_NO_REASON(this); + } + + mCompositorBridge->SetTestAsyncZoom(GetId(), aScrollID, + LayerToParentLayerScale(aValue)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvFlushApzRepaints() { + mCompositorBridge->FlushApzRepaints(GetId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetAPZTestData( + APZTestData* aOutData) { + mCompositorBridge->GetAPZTestData(GetId(), aOutData); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetFrameUniformity( + FrameUniformityData* aOutData) { + mCompositorBridge->GetFrameUniformity(GetId(), aOutData); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvRequestProperty( + const nsString& aProperty, float* aValue) { + *aValue = -1; + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvSetConfirmedTargetAPZC( + const uint64_t& aBlockId, nsTArray<ScrollableLayerGuid>&& aTargets) { + for (size_t i = 0; i < aTargets.Length(); i++) { + // Guard against bad data from hijacked child processes + if (aTargets[i].mLayersId != GetId()) { + NS_ERROR( + "Unexpected layers id in RecvSetConfirmedTargetAPZC; dropping " + "message..."); + return IPC_FAIL(this, "Bad layers id"); + } + } + mCompositorBridge->SetConfirmedTargetAPZC(GetId(), aBlockId, + std::move(aTargets)); + return IPC_OK(); +} + +bool LayerTransactionParent::Attach(Layer* aLayer, + CompositableHost* aCompositable, + bool aIsAsync) { + if (!aCompositable || !aLayer) { + return false; + } + + HostLayer* layer = aLayer->AsHostLayer(); + if (!layer) { + return false; + } + + TextureSourceProvider* provider = + static_cast<HostLayerManager*>(aLayer->Manager()) + ->GetTextureSourceProvider(); + + MOZ_ASSERT(!aCompositable->AsWebRenderImageHost()); + if (aCompositable->AsWebRenderImageHost()) { + gfxCriticalNote << "Use WebRenderImageHost at LayerTransactionParent."; + } + if (!layer->SetCompositableHost(aCompositable)) { + // not all layer types accept a compositable, see bug 967824 + return false; + } + aCompositable->Attach(aLayer, provider, + aIsAsync ? CompositableHost::ALLOW_REATTACH | + CompositableHost::KEEP_ATTACHED + : CompositableHost::NO_FLAGS); + return true; +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvClearCachedResources() { + if (mRoot) { + // NB: |mRoot| here is the *child* context's root. In this parent + // context, it's just a subtree root. We need to scope the clear + // of resources to exactly that subtree, so we specify it here. + mLayerManager->ClearCachedResources(mRoot); + } + mCompositorBridge->NotifyClearCachedResources(this); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvScheduleComposite() { + mCompositorBridge->ScheduleComposite(this); + return IPC_OK(); +} + +void LayerTransactionParent::ActorDestroy(ActorDestroyReason why) { Destroy(); } + +bool LayerTransactionParent::AllocShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (!mIPCOpen || mDestroyed) { + return false; + } + return PLayerTransactionParent::AllocShmem(aSize, aType, aShmem); +} + +bool LayerTransactionParent::AllocUnsafeShmem( + size_t aSize, ipc::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aShmem) { + if (!mIPCOpen || mDestroyed) { + return false; + } + + return PLayerTransactionParent::AllocUnsafeShmem(aSize, aType, aShmem); +} + +bool LayerTransactionParent::DeallocShmem(ipc::Shmem& aShmem) { + if (!mIPCOpen || mDestroyed) { + return false; + } + return PLayerTransactionParent::DeallocShmem(aShmem); +} + +bool LayerTransactionParent::IsSameProcess() const { + return OtherPid() == base::GetCurrentProcId(); +} + +void LayerTransactionParent::SetPendingTransactionId( + TransactionId aId, const VsyncId& aVsyncId, + const TimeStamp& aVsyncStartTime, const TimeStamp& aRefreshStartTime, + const TimeStamp& aTxnStartTime, const TimeStamp& aTxnEndTime, + bool aContainsSVG, const nsCString& aURL, const TimeStamp& aFwdTime) { + mPendingTransactions.AppendElement(PendingTransaction{ + aId, aVsyncId, aVsyncStartTime, aRefreshStartTime, aTxnStartTime, + aTxnEndTime, aFwdTime, aURL, aContainsSVG}); +} + +TransactionId LayerTransactionParent::FlushTransactionId( + const VsyncId& aCompositeId, TimeStamp& aCompositeEnd) { + TransactionId id; + for (auto& transaction : mPendingTransactions) { + id = transaction.mId; + if (mId.IsValid() && transaction.mId.IsValid() && !mVsyncRate.IsZero()) { + RecordContentFrameTime( + transaction.mTxnVsyncId, transaction.mVsyncStartTime, + transaction.mTxnStartTime, aCompositeId, aCompositeEnd, + transaction.mTxnEndTime - transaction.mTxnStartTime, mVsyncRate, + transaction.mContainsSVG, false); + } + +#if defined(ENABLE_FRAME_LATENCY_LOG) + if (transaction.mId.IsValid()) { + if (transaction.mRefreshStartTime) { + int32_t latencyMs = lround( + (aCompositeEnd - transaction.mRefreshStartTime).ToMilliseconds()); + printf_stderr( + "From transaction start to end of generate frame latencyMs %d this " + "%p\n", + latencyMs, this); + } + if (transaction.mFwdTime) { + int32_t latencyMs = + lround((aCompositeEnd - transaction.mFwdTime).ToMilliseconds()); + printf_stderr( + "From forwarding transaction to end of generate frame latencyMs %d " + "this %p\n", + latencyMs, this); + } + } +#endif + } + + mPendingTransactions.Clear(); + return id; +} + +void LayerTransactionParent::SendAsyncMessage( + const nsTArray<AsyncParentMessageData>& aMessage) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); +} + +void LayerTransactionParent::SendPendingAsyncMessages() { + mCompositorBridge->SendPendingAsyncMessages(); +} + +void LayerTransactionParent::SetAboutToSendAsyncMessages() { + mCompositorBridge->SetAboutToSendAsyncMessages(); +} + +void LayerTransactionParent::NotifyNotUsed(PTextureParent* aTexture, + uint64_t aTransactionId) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); +} + +bool LayerTransactionParent::BindLayerToHandle(RefPtr<Layer> aLayer, + const LayerHandle& aHandle) { + if (!aHandle || !aLayer) { + return false; + } + auto entry = mLayerMap.LookupForAdd(aHandle.Value()); + if (entry) { + return false; + } + entry.OrInsert([&aLayer]() { return aLayer; }); + return true; +} + +Layer* LayerTransactionParent::AsLayer(const LayerHandle& aHandle) { + if (!aHandle) { + return nullptr; + } + return mLayerMap.GetWeak(aHandle.Value()); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvNewCompositable( + const CompositableHandle& aHandle, const TextureInfo& aInfo) { + if (!AddCompositable(aHandle, aInfo, /* aUseWebRender */ false)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvReleaseLayer( + const LayerHandle& aHandle) { + RefPtr<Layer> layer; + if (!aHandle || !mLayerMap.Remove(aHandle.Value(), getter_AddRefs(layer))) { + return IPC_FAIL_NO_REASON(this); + } + if (mAnimStorage && layer->GetCompositorAnimationsId()) { + mAnimStorage->ClearById(layer->GetCompositorAnimationsId()); + layer->ClearCompositorAnimations(); + } + layer->Disconnect(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvReleaseCompositable( + const CompositableHandle& aHandle) { + ReleaseCompositable(aHandle); + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvRecordPaintTimes( + const PaintTiming& aTiming) { + // Currently we only add paint timings for remote layers. In the future + // we could be smarter and use paint timings from the UI process, either + // as a separate overlay or if no remote layers are attached. + if (mLayerManager && mCompositorBridge->IsRemote()) { + mLayerManager->RecordPaintTimes(aTiming); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult LayerTransactionParent::RecvGetTextureFactoryIdentifier( + TextureFactoryIdentifier* aIdentifier) { + if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) { + // Default constructor sets mParentBackend to LAYERS_NONE. + return IPC_OK(); + } + + *aIdentifier = mLayerManager->GetTextureFactoryIdentifier(); + return IPC_OK(); +} + +} // namespace layers +} // namespace mozilla |