Updating hit testing trees is very expensive for many layers, and often layer properties that change don't necessitate hit testing tree updates. While layer tree structure changes do, we can be smarter about individual layer attributes.
1058 lines
33 KiB
C++
1058 lines
33 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: sw=2 ts=8 et :
|
|
*/
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "LayerTransactionParent.h"
|
|
#include <vector> // for vector
|
|
#include "apz/src/AsyncPanZoomController.h"
|
|
#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 "gfxPrefs.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/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/SizePrintfMacros.h"
|
|
#include "mozilla/Unused.h"
|
|
#include "nsCoord.h" // for NSAppUnitsToFloatPixels
|
|
#include "nsDebug.h" // for NS_RUNTIMEABORT
|
|
#include "nsDeviceContext.h" // for AppUnitsPerCSSPixel
|
|
#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::layout::RenderFrameParent;
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
//--------------------------------------------------
|
|
// LayerTransactionParent
|
|
LayerTransactionParent::LayerTransactionParent(HostLayerManager* aManager,
|
|
CompositorBridgeParentBase* aBridge,
|
|
uint64_t aId)
|
|
: mLayerManager(aManager)
|
|
, mCompositorBridge(aBridge)
|
|
, mId(aId)
|
|
, mChildEpoch(0)
|
|
, mParentEpoch(0)
|
|
, mPendingTransaction(0)
|
|
, mDestroyed(false)
|
|
, mIPCOpen(false)
|
|
{
|
|
}
|
|
|
|
LayerTransactionParent::~LayerTransactionParent()
|
|
{
|
|
}
|
|
|
|
void
|
|
LayerTransactionParent::SetLayerManager(HostLayerManager* aLayerManager)
|
|
{
|
|
mLayerManager = aLayerManager;
|
|
for (auto iter = mLayerMap.Iter(); !iter.Done(); iter.Next()) {
|
|
auto layer = iter.Data();
|
|
layer->AsHostLayer()->SetLayerManager(aLayerManager);
|
|
}
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvShutdown()
|
|
{
|
|
Destroy();
|
|
IProtocol* mgr = Manager();
|
|
if (!Send__delete__(this)) {
|
|
return IPC_FAIL_NO_REASON(mgr);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
void
|
|
LayerTransactionParent::Destroy()
|
|
{
|
|
mDestroyed = true;
|
|
mCompositables.clear();
|
|
}
|
|
|
|
class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender
|
|
{
|
|
public:
|
|
explicit AutoLayerTransactionParentAsyncMessageSender(LayerTransactionParent* aLayerTransaction,
|
|
const InfallibleTArray<OpDestroy>* aDestroyActors = nullptr)
|
|
: mLayerTransaction(aLayerTransaction)
|
|
, mActorsToDestroy(aDestroyActors)
|
|
{
|
|
mLayerTransaction->SetAboutToSendAsyncMessages();
|
|
ImageBridgeParent::SetAboutToSendAsyncMessages(mLayerTransaction->GetChildProcessId());
|
|
}
|
|
|
|
~AutoLayerTransactionParentAsyncMessageSender()
|
|
{
|
|
mLayerTransaction->SendPendingAsyncMessages();
|
|
ImageBridgeParent::SendPendingAsyncMessages(mLayerTransaction->GetChildProcessId());
|
|
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 InfallibleTArray<OpDestroy>* mActorsToDestroy;
|
|
};
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvPaintTime(const uint64_t& aTransactionId,
|
|
const TimeDuration& aPaintTime)
|
|
{
|
|
mCompositorBridge->UpdatePaintTime(this, aPaintTime);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvInitReadLocks(ReadLockArray&& aReadLocks)
|
|
{
|
|
if (!AddReadLocks(Move(aReadLocks))) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
|
|
{
|
|
GeckoProfilerTracingRAII tracer("Paint", "LayerTransaction");
|
|
PROFILER_LABEL("LayerTransactionParent", "RecvUpdate",
|
|
js::ProfileEntry::Category::GRAPHICS);
|
|
|
|
TimeStamp updateStart = TimeStamp::Now();
|
|
|
|
MOZ_LAYERS_LOG(("[ParentSide] received txn with %" PRIuSIZE " edits", aInfo.cset().Length()));
|
|
|
|
UpdateFwdTransactionId(aInfo.fwdTransactionId());
|
|
AutoClearReadLocks clearLocks(mReadLocks);
|
|
|
|
if (mDestroyed || !layer_manager() || layer_manager()->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));
|
|
layer_manager()->BeginTransaction();
|
|
}
|
|
|
|
// 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 = layer_manager()->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 = layer_manager()->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 = layer_manager()->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 = layer_manager()->CreateColorLayer();
|
|
if (!BindLayer(layer, edit.get_OpCreateColorLayer())) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
UpdateHitTestingTree(layer, "CreateColorLayer");
|
|
break;
|
|
}
|
|
case Edit::TOpCreateTextLayer: {
|
|
MOZ_LAYERS_LOG(("[ParentSide] CreateTextLayer"));
|
|
|
|
RefPtr<TextLayer> layer = layer_manager()->CreateTextLayer();
|
|
if (!BindLayer(layer, edit.get_OpCreateTextLayer())) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
UpdateHitTestingTree(layer, "CreateTextLayer");
|
|
break;
|
|
}
|
|
case Edit::TOpCreateBorderLayer: {
|
|
MOZ_LAYERS_LOG(("[ParentSide] CreateBorderLayer"));
|
|
|
|
RefPtr<BorderLayer> layer = layer_manager()->CreateBorderLayer();
|
|
if (!BindLayer(layer, edit.get_OpCreateBorderLayer())) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
UpdateHitTestingTree(layer, "CreateBorderLayer");
|
|
break;
|
|
}
|
|
case Edit::TOpCreateCanvasLayer: {
|
|
MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer"));
|
|
|
|
RefPtr<CanvasLayer> layer = layer_manager()->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 = layer_manager()->CreateRefLayer();
|
|
if (!BindLayer(layer, edit.get_OpCreateRefLayer())) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
UpdateHitTestingTree(layer, "CreateRefLayer");
|
|
break;
|
|
}
|
|
case Edit::TOpSetDiagnosticTypes: {
|
|
mLayerManager->GetCompositor()->SetDiagnosticTypes(
|
|
edit.get_OpSetDiagnosticTypes().diagnostics());
|
|
break;
|
|
}
|
|
case Edit::TOpWindowOverlayChanged: {
|
|
mLayerManager->SetWindowOverlayChanged();
|
|
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 (mPendingCompositorUpdate) {
|
|
// Do not attach compositables from old layer trees. Return true since
|
|
// content cannot handle errors.
|
|
return IPC_OK();
|
|
}
|
|
if (!Attach(AsLayer(op.layer()), host, false)) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
if (mLayerManager->GetCompositor()) {
|
|
host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
|
|
}
|
|
break;
|
|
}
|
|
case Edit::TOpAttachAsyncCompositable: {
|
|
const OpAttachAsyncCompositable& op = edit.get_OpAttachAsyncCompositable();
|
|
if (mPendingCompositorUpdate) {
|
|
// Do not attach compositables from old layer trees. Return true since
|
|
// content cannot handle errors.
|
|
return IPC_OK();
|
|
}
|
|
ImageBridgeParent* imageBridge = ImageBridgeParent::GetInstance(OtherPid());
|
|
if (!imageBridge) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
RefPtr<CompositableHost> host = imageBridge->FindCompositable(op.compositable());
|
|
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);
|
|
}
|
|
if (mLayerManager->GetCompositor()) {
|
|
host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
|
|
}
|
|
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));
|
|
layer_manager()->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 = gfxPrefs::LayersDrawFPS();
|
|
if (drawFps) {
|
|
uint32_t visualWarningTrigger = gfxPrefs::LayerTransactionWarning();
|
|
// 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(common.compositorAnimations());
|
|
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.scaleToResolution(),
|
|
attrs.presShellResolution());
|
|
if (attrs.eventRegionsOverride() != containerLayer->GetEventRegionsOverride()) {
|
|
UpdateHitTestingTree(layer, "event regions override changed");
|
|
containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride());
|
|
}
|
|
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::TTextLayerAttributes: {
|
|
MOZ_LAYERS_LOG(("[ParentSide] text layer"));
|
|
|
|
TextLayer* textLayer = layer->AsTextLayer();
|
|
if (!textLayer) {
|
|
return false;
|
|
}
|
|
const auto& tla = specific.get_TextLayerAttributes();
|
|
textLayer->SetBounds(tla.bounds());
|
|
textLayer->SetGlyphs(Move(const_cast<nsTArray<GlyphArray>&>(tla.glyphs())));
|
|
textLayer->SetScaledFont(reinterpret_cast<gfx::ScaledFont*>(tla.scaledFont()));
|
|
break;
|
|
}
|
|
case Specific::TBorderLayerAttributes: {
|
|
MOZ_LAYERS_LOG(("[ParentSide] border layer"));
|
|
|
|
BorderLayer* borderLayer = layer->AsBorderLayer();
|
|
if (!borderLayer) {
|
|
return false;
|
|
}
|
|
borderLayer->SetRect(specific.get_BorderLayerAttributes().rect());
|
|
borderLayer->SetColors(specific.get_BorderLayerAttributes().colors());
|
|
borderLayer->SetCornerRadii(specific.get_BorderLayerAttributes().corners());
|
|
borderLayer->SetWidths(specific.get_BorderLayerAttributes().widths());
|
|
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());
|
|
UpdateHitTestingTree(layer, "event regions override 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::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
|
|
{
|
|
mChildEpoch = aLayerObserverEpoch;
|
|
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(this, aTime)) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvLeaveTestMode()
|
|
{
|
|
mCompositorBridge->LeaveTestMode(this);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvGetAnimationOpacity(const uint64_t& aCompositorAnimationsId,
|
|
float* aOpacity,
|
|
bool* aHasAnimationOpacity)
|
|
{
|
|
*aHasAnimationOpacity = false;
|
|
if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
mCompositorBridge->ApplyAsyncProperties(this);
|
|
|
|
CompositorAnimationStorage* storage =
|
|
mCompositorBridge->GetAnimationStorage(GetId());
|
|
|
|
if (!storage) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
auto value = storage->GetAnimatedValue(aCompositorAnimationsId);
|
|
|
|
if (!value || value->mType != AnimatedValue::OPACITY) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
*aOpacity = value->mOpacity;
|
|
*aHasAnimationOpacity = true;
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvGetAnimationTransform(const uint64_t& aCompositorAnimationsId,
|
|
MaybeTransform* aTransform)
|
|
{
|
|
if (mDestroyed || !layer_manager() || layer_manager()->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);
|
|
|
|
CompositorAnimationStorage* storage =
|
|
mCompositorBridge->GetAnimationStorage(GetId());
|
|
|
|
if (!storage) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
auto value = storage->GetAnimatedValue(aCompositorAnimationsId);
|
|
|
|
if (!value || value->mType != AnimatedValue::TRANSFORM) {
|
|
*aTransform = mozilla::void_t();
|
|
return IPC_OK();
|
|
}
|
|
|
|
Matrix4x4 transform = value->mTransform.mFrameTransform;
|
|
const TransformData& data = value->mTransform.mData;
|
|
float scale = data.appUnitsPerDevPixel();
|
|
Point3D transformOrigin = data.transformOrigin();
|
|
|
|
// Undo the rebasing applied by
|
|
// nsDisplayTransform::GetResultingTransformMatrixInternal
|
|
transform.ChangeBasis(-transformOrigin);
|
|
|
|
// Convert to CSS pixels (this undoes the operations performed by
|
|
// nsStyleTransformMatrix::ProcessTranslatePart which is called from
|
|
// nsDisplayTransform::GetResultingTransformMatrix)
|
|
double devPerCss =
|
|
double(scale) / double(nsDeviceContext::AppUnitsPerCSSPixel());
|
|
transform._41 *= devPerCss;
|
|
transform._42 *= devPerCss;
|
|
transform._43 *= devPerCss;
|
|
|
|
*aTransform = transform;
|
|
return IPC_OK();
|
|
}
|
|
|
|
static AsyncPanZoomController*
|
|
GetAPZCForViewID(Layer* aLayer, FrameMetrics::ViewID aScrollID)
|
|
{
|
|
AsyncPanZoomController* resultApzc = nullptr;
|
|
ForEachNode<ForwardIterator>(
|
|
aLayer,
|
|
[aScrollID, &resultApzc] (Layer* layer)
|
|
{
|
|
for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
|
|
if (layer->GetFrameMetrics(i).GetScrollId() == aScrollID) {
|
|
resultApzc = layer->GetAsyncPanZoomController(i);
|
|
return TraversalFlag::Abort;
|
|
}
|
|
}
|
|
return TraversalFlag::Continue;
|
|
});
|
|
return resultApzc;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aScrollID,
|
|
const float& aX, const float& aY)
|
|
{
|
|
if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID);
|
|
if (!controller) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
controller->SetTestAsyncScrollOffset(CSSPoint(aX, aY));
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollID,
|
|
const float& aValue)
|
|
{
|
|
if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID);
|
|
if (!controller) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
controller->SetTestAsyncZoom(LayerToParentLayerScale(aValue));
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvFlushApzRepaints()
|
|
{
|
|
mCompositorBridge->FlushApzRepaints(this);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData)
|
|
{
|
|
mCompositorBridge->GetAPZTestData(this, 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)
|
|
{
|
|
mCompositorBridge->SetConfirmedTargetAPZC(this, aBlockId, 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();
|
|
|
|
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::RecvForceComposite()
|
|
{
|
|
mCompositorBridge->ForceComposite(this);
|
|
return IPC_OK();
|
|
}
|
|
|
|
void
|
|
LayerTransactionParent::ActorDestroy(ActorDestroyReason why)
|
|
{
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void
|
|
LayerTransactionParent::DeallocShmem(ipc::Shmem& aShmem)
|
|
{
|
|
if (!mIPCOpen || mDestroyed) {
|
|
return;
|
|
}
|
|
PLayerTransactionParent::DeallocShmem(aShmem);
|
|
}
|
|
|
|
bool LayerTransactionParent::IsSameProcess() const
|
|
{
|
|
return OtherPid() == base::GetCurrentProcId();
|
|
}
|
|
|
|
void
|
|
LayerTransactionParent::SendAsyncMessage(const InfallibleTArray<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 || mLayerMap.Contains(aHandle.Value())) {
|
|
return false;
|
|
}
|
|
|
|
mLayerMap.Put(aHandle.Value(), aLayer);
|
|
return true;
|
|
}
|
|
|
|
Layer*
|
|
LayerTransactionParent::AsLayer(const LayerHandle& aHandle)
|
|
{
|
|
if (!aHandle) {
|
|
return nullptr;
|
|
}
|
|
return mLayerMap.Get(aHandle.Value()).get();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvNewCompositable(const CompositableHandle& aHandle, const TextureInfo& aInfo)
|
|
{
|
|
if (!AddCompositable(aHandle, aInfo)) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
LayerTransactionParent::RecvReleaseLayer(const LayerHandle& aHandle)
|
|
{
|
|
if (!aHandle || !mLayerMap.Contains(aHandle.Value())) {
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
Maybe<RefPtr<Layer>> maybeLayer = mLayerMap.GetAndRemove(aHandle.Value());
|
|
if (maybeLayer) {
|
|
(*maybeLayer)->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();
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|