Files
tubestation/gfx/layers/ipc/LayerTransactionParent.cpp
David Anderson 32519375c6 Make finer-grained decisions about whether to update hit testing trees. (bug 1352918, r=kats)
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.
2017-04-13 21:25:16 -07:00

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