nsIntRect Layer::CalculateScissorRect(const nsIntRect& aCurrentScissorRect, const gfxMatrix* aWorldTransform) { ContainerLayer* container = GetParent(); NS_ASSERTION(container, "This can't be called on the root!"); // Establish initial clip rect: it's either the one passed in, or // if the parent has an intermediate surface, it's the extents of that surface. nsIntRect currentClip; if (container->UseIntermediateSurface()) { currentClip.SizeTo(container->GetIntermediateSurfaceRect().Size()); } else { currentClip = aCurrentScissorRect; } const nsIntRect *clipRect = GetEffectiveClipRect(); if (!clipRect) return currentClip; if (clipRect->IsEmpty()) { // We might have a non-translation transform in the container so we can't // use the code path below. return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0)); } nsIntRect scissor = *clipRect; if (!container->UseIntermediateSurface()) { gfxMatrix matrix; DebugOnly<bool> is2D = container->GetEffectiveTransform().Is2D(&matrix); // See DefaultComputeEffectiveTransforms below NS_ASSERTION(is2D && matrix.PreservesAxisAlignedRectangles(), "Non preserves axis aligned transform with clipped child should have forced intermediate surface"); gfxRect r(scissor.x, scissor.y, scissor.width, scissor.height); gfxRect trScissor = matrix.TransformBounds(r); trScissor.Round(); if (!gfxUtils::GfxRectToIntRect(trScissor, &scissor)) { return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0)); } // Find the nearest ancestor with an intermediate surface do { container = container->GetParent(); } while (container && !container->UseIntermediateSurface()); } if (container) { scissor.MoveBy(-container->GetIntermediateSurfaceRect().TopLeft()); } else if (aWorldTransform) { gfxRect r(scissor.x, scissor.y, scissor.width, scissor.height); gfxRect trScissor = aWorldTransform->TransformBounds(r); trScissor.Round(); if (!gfxUtils::GfxRectToIntRect(trScissor, &scissor)) return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0)); } return currentClip.Intersect(scissor); }
gfxSize ThebesLayerComposite::GetEffectiveResolution() { // Work out render resolution by multiplying the resolution of our ancestors. // Only container layers can have frame metrics, so we start off with a // resolution of 1, 1. // XXX For large layer trees, it would be faster to do this once from the // root node upwards and store the value on each layer. gfxSize resolution(1, 1); for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& metrics = parent->GetFrameMetrics(); resolution.width *= metrics.mResolution.scale; resolution.height *= metrics.mResolution.scale; } return resolution; }
/* static */ void LayerProperties::ClearInvalidations(Layer *aLayer) { aLayer->ClearInvalidRect(); if (aLayer->GetMaskLayer()) { ClearInvalidations(aLayer->GetMaskLayer()); } ContainerLayer* container = aLayer->AsContainerLayer(); if (!container) { return; } for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { ClearInvalidations(child); } }
// Do a breadth-first search to find the first layer in the tree that is // scrollable. Layer* CompositorParent::GetPrimaryScrollableLayer() { Layer* root = mLayerManager->GetRoot(); // FIXME: We're currently getting passed layers that are not part of our content, but // we are drawing them anyway. This is causing severe rendering corruption to our background // and checkerboarding. The real fix here is to assert that we don't have any useless layers // and ensure that layout isn't giving us any. This is being tracked in bug 728284. // For now just clip them to the empty rect so we don't draw them. Layer* discardLayer = root->GetFirstChild(); while (discardLayer) { if (!discardLayer->AsContainerLayer()) { discardLayer->IntersectClipRect(nsIntRect()); SetShadowProperties(discardLayer); } discardLayer = discardLayer->GetNextSibling(); } nsTArray<Layer*> queue; queue.AppendElement(root); while (queue.Length()) { ContainerLayer* containerLayer = queue[0]->AsContainerLayer(); queue.RemoveElementAt(0); if (!containerLayer) { continue; } const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics(); if (frameMetrics.IsScrollable()) { return containerLayer; } Layer* child = containerLayer->GetFirstChild(); while (child) { queue.AppendElement(child); child = child->GetNextSibling(); } } return root; }
/** * Walks over this layer, and all descendant layers. * If any of these are a ContainerLayer that reports invalidations to a PresShell, * then report that the entire bounds have changed. */ static void NotifySubdocumentInvalidationRecursive(Layer* aLayer, NotifySubDocInvalidationFunc aCallback) { aLayer->ClearInvalidRect(); ContainerLayer* container = aLayer->AsContainerLayer(); if (aLayer->GetMaskLayer()) { NotifySubdocumentInvalidationRecursive(aLayer->GetMaskLayer(), aCallback); } if (!container) { return; } for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { NotifySubdocumentInvalidationRecursive(child, aCallback); } aCallback(container, container->GetVisibleRegion()); }
virtual nsIntRect ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback) { ContainerLayer* container = mLayer->AsContainerLayer(); nsIntRegion result; PRUint32 i = 0; for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { if (i >= mChildren.Length() || child != mChildren[i]->mLayer) { // Child change. Invalidate the full areas. // TODO: We could be smarter here if non-overlapping children // swap order. result.Or(result, TransformRect(child->GetVisibleRegion().GetBounds(), child->GetTransform())); if (i < mChildren.Length()) { result.Or(result, mChildren[i]->OldTransformedBounds()); } if (aCallback) { NotifySubdocumentInvalidationRecursive(child, aCallback); } else { ClearInvalidations(child); } } else { // Same child, check for differences within the child result.Or(result, mChildren[i]->ComputeChange(aCallback)); } i++; } // Process remaining removed children. while (i < mChildren.Length()) { result.Or(result, mChildren[i]->OldTransformedBounds()); i++; } if (aCallback) { aCallback(container, result); } return TransformRect(result.GetBounds(), mLayer->GetTransform()); }
void ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray) { nsAutoTArray<Layer*, 10> toSort; for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) { ContainerLayer* container = l->AsContainerLayer(); if (container && container->GetContentFlags() & CONTENT_PRESERVE_3D) { toSort.AppendElement(l); } else { if (toSort.Length() > 0) { SortLayersBy3DZOrder(toSort); aArray.MoveElementsFrom(toSort); } aArray.AppendElement(l); } } if (toSort.Length() > 0) { SortLayersBy3DZOrder(toSort); aArray.MoveElementsFrom(toSort); } }
void SharedFrameMetricsHelper::FindFallbackContentFrameMetrics(ContainerLayer* aLayer, ParentLayerRect& aCompositionBounds, CSSToParentLayerScale& aZoom) { if (!aLayer) { return; } ContainerLayer* layer = aLayer; const FrameMetrics* contentMetrics = &(layer->GetFrameMetrics()); // Walk up the layer tree until a valid composition bounds is found while (layer && contentMetrics->mCompositionBounds.IsEmpty()) { layer = layer->GetParent(); contentMetrics = layer ? &(layer->GetFrameMetrics()) : contentMetrics; } MOZ_ASSERT(!contentMetrics->mCompositionBounds.IsEmpty()); aCompositionBounds = ParentLayerRect(contentMetrics->mCompositionBounds); aZoom = contentMetrics->GetZoomToParent(); // TODO(botond): double-check this return; }
bool LayerTransactionParent::RecvSetAsyncScrollOffset(PLayerParent* aLayer, const int32_t& aX, const int32_t& aY) { if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) { return false; } Layer* layer = cast(aLayer)->AsLayer(); if (!layer) { return false; } ContainerLayer* containerLayer = layer->AsContainerLayer(); if (!containerLayer) { return false; } AsyncPanZoomController* controller = containerLayer->GetAsyncPanZoomController(); if (!controller) { return false; } controller->SetTestAsyncScrollOffset(CSSPoint(aX, aY)); return true; }
gfxRect ThebesLayerComposite::GetCompositionBounds() { // Walk up the tree, looking for a display-port - if we find one, we know // that this layer represents a content node and we can use its first // scrollable child, in conjunction with its content area and viewport offset // to establish the screen coordinates to which the content area will be // rendered. gfxRect compositionBounds; ContainerLayer* scrollableLayer = nullptr; for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& parentMetrics = parent->GetFrameMetrics(); if (parentMetrics.IsScrollable()) scrollableLayer = parent; if (!parentMetrics.mDisplayPort.IsEmpty() && scrollableLayer) { // Get the composition bounds, so as not to waste rendering time. compositionBounds = gfxRect(parentMetrics.mCompositionBounds.x, parentMetrics.mCompositionBounds.y, parentMetrics.mCompositionBounds.width, parentMetrics.mCompositionBounds.height); // Calculate the scale transform applied to the root layer to determine // the content resolution. Layer* rootLayer = Manager()->GetRoot(); const gfx3DMatrix& rootTransform = rootLayer->GetTransform(); LayerToCSSScale scale(rootTransform.GetXScale(), rootTransform.GetYScale()); // Get the content document bounds, in screen-space. const FrameMetrics& metrics = scrollableLayer->GetFrameMetrics(); const LayerIntRect content = RoundedToInt(metrics.mScrollableRect / scale); // !!! WTF. this code is just wrong. See bug 881451. gfx::Point scrollOffset = gfx::Point((metrics.mScrollOffset.x * metrics.LayersPixelsPerCSSPixel().scale) / scale.scale, (metrics.mScrollOffset.y * metrics.LayersPixelsPerCSSPixel().scale) / scale.scale); const nsIntPoint contentOrigin( content.x - NS_lround(scrollOffset.x), content.y - NS_lround(scrollOffset.y)); gfxRect contentRect = gfxRect(contentOrigin.x, contentOrigin.y, content.width, content.height); gfxRect contentBounds = scrollableLayer->GetEffectiveTransform(). TransformBounds(contentRect); // Clip the composition bounds to the content bounds compositionBounds.IntersectRect(compositionBounds, contentBounds); break; } } return compositionBounds; }
virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback, bool& aGeometryChanged) { ContainerLayer* container = mLayer->AsContainerLayer(); nsIntRegion result; bool childrenChanged = false; if (mPreXScale != container->GetPreXScale() || mPreYScale != container->GetPreYScale()) { aGeometryChanged = true; result = OldTransformedBounds(); AddRegion(result, NewTransformedBounds()); childrenChanged = true; // Can't bail out early, we need to update the child container layers } // A low frame rate is especially visible to users when scrolling, so we // particularly want to avoid unnecessary invalidation at that time. For us // here, that means avoiding unnecessary invalidation of child items when // other children are added to or removed from our container layer, since // that may be caused by children being scrolled in or out of view. We are // less concerned with children changing order. // TODO: Consider how we could avoid unnecessary invalidation when children // change order, and whether the overhead would be worth it. nsDataHashtable<nsPtrHashKey<Layer>, uint32_t> oldIndexMap(mChildren.Length()); for (uint32_t i = 0; i < mChildren.Length(); ++i) { oldIndexMap.Put(mChildren[i]->mLayer, i); } uint32_t i = 0; // cursor into the old child list mChildren for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { bool invalidateChildsCurrentArea = false; if (i < mChildren.Length()) { uint32_t childsOldIndex; if (oldIndexMap.Get(child, &childsOldIndex)) { if (childsOldIndex >= i) { // Invalidate the old areas of layers that used to be between the // current |child| and the previous |child| that was also in the // old list mChildren (if any of those children have been reordered // rather than removed, we will invalidate their new area when we // encounter them in the new list): for (uint32_t j = i; j < childsOldIndex; ++j) { AddRegion(result, mChildren[j]->OldTransformedBounds()); childrenChanged |= true; } // Invalidate any regions of the child that have changed: nsIntRegion region = mChildren[childsOldIndex]->ComputeChange(aCallback, aGeometryChanged); i = childsOldIndex + 1; if (!region.IsEmpty()) { AddRegion(result, region); childrenChanged |= true; } } else { // We've already seen this child in mChildren (which means it must // have been reordered) and invalidated its old area. We need to // invalidate its new area too: invalidateChildsCurrentArea = true; } } else { // |child| is new invalidateChildsCurrentArea = true; } } else { // |child| is new, or was reordered to a higher index invalidateChildsCurrentArea = true; } if (invalidateChildsCurrentArea) { aGeometryChanged = true; AddTransformedRegion(result, child->GetVisibleRegion(), child->GetLocalTransform()); if (aCallback) { NotifySubdocumentInvalidationRecursive(child, aCallback); } else { ClearInvalidations(child); } } childrenChanged |= invalidateChildsCurrentArea; } // Process remaining removed children. while (i < mChildren.Length()) { childrenChanged |= true; AddRegion(result, mChildren[i]->OldTransformedBounds()); i++; } if (aCallback) { aCallback(container, result); } if (childrenChanged) { container->SetChildrenChanged(true); } result.Transform(gfx::To3DMatrix(mLayer->GetLocalTransform())); return result; }
void ClientTiledThebesLayer::BeginPaint() { if (ClientManager()->IsRepeatTransaction()) { return; } mPaintData.mLowPrecisionPaintCount = 0; mPaintData.mPaintFinished = false; // Get the metrics of the nearest scroll container. ContainerLayer* scrollParent = nullptr; for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& metrics = parent->GetFrameMetrics(); if (metrics.mScrollId != FrameMetrics::NULL_SCROLL_ID) { scrollParent = parent; break; } } if (!scrollParent) { // XXX I don't think this can happen, but if it does, warn and set the // composition bounds to empty so that progressive updates are disabled. NS_WARNING("Tiled Thebes layer with no scrollable container parent"); mPaintData.mCompositionBounds.SetEmpty(); return; } const FrameMetrics& metrics = scrollParent->GetFrameMetrics(); // Calculate the transform required to convert screen space into transformed // layout device space. gfx::Matrix4x4 effectiveTransform = GetEffectiveTransform(); for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { if (parent->UseIntermediateSurface()) { effectiveTransform = effectiveTransform * parent->GetEffectiveTransform(); } } gfx3DMatrix layoutToScreen; gfx::To3DMatrix(effectiveTransform, layoutToScreen); layoutToScreen.ScalePost(metrics.mCumulativeResolution.scale, metrics.mCumulativeResolution.scale, 1.f); mPaintData.mTransformScreenToLayout = layoutToScreen.Inverse(); // Compute the critical display port in layer space. mPaintData.mLayoutCriticalDisplayPort.SetEmpty(); if (!metrics.mCriticalDisplayPort.IsEmpty()) { // Convert the display port to screen space first so that we can transform // it into layout device space. const ScreenRect& criticalDisplayPort = metrics.mCriticalDisplayPort * metrics.mZoom; LayoutDeviceRect transformedCriticalDisplayPort = ApplyScreenToLayoutTransform(mPaintData.mTransformScreenToLayout, criticalDisplayPort); mPaintData.mLayoutCriticalDisplayPort = LayoutDeviceIntRect::ToUntyped(RoundedOut(transformedCriticalDisplayPort)); } // Calculate the frame resolution. Because this is Gecko-side, before any // async transforms have occurred, we can use mZoom for this. mPaintData.mResolution = metrics.mZoom; // Calculate the scroll offset since the last transaction, and the // composition bounds. mPaintData.mCompositionBounds.SetEmpty(); mPaintData.mScrollOffset.MoveTo(0, 0); Layer* primaryScrollable = ClientManager()->GetPrimaryScrollableLayer(); if (primaryScrollable) { const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); mPaintData.mScrollOffset = metrics.mScrollOffset * metrics.mZoom; mPaintData.mCompositionBounds = ApplyScreenToLayoutTransform(mPaintData.mTransformScreenToLayout, ScreenRect(metrics.mCompositionBounds)); } }
// Recursively create a new array of scrollables, preserving any scrollables // that are still in the layer tree. // // aXScale and aYScale are used to calculate any values that need to be in // chrome-document CSS pixels and aren't part of the rendering loop, such as // the initial scroll offset for a new view. static void BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews, nsFrameLoader* aFrameLoader, Layer* aLayer, float aXScale = 1, float aYScale = 1, float aAccConfigXScale = 1, float aAccConfigYScale = 1) { ContainerLayer* container = aLayer->AsContainerLayer(); if (!container) return; const FrameMetrics metrics = container->GetFrameMetrics(); const ViewID scrollId = metrics.mScrollId; const gfx3DMatrix transform = aLayer->GetTransform(); aXScale *= GetXScale(transform); aYScale *= GetYScale(transform); if (metrics.IsScrollable()) { nscoord auPerDevPixel = aFrameLoader->GetPrimaryFrameOfOwningContent() ->PresContext()->AppUnitsPerDevPixel(); nscoord auPerCSSPixel = auPerDevPixel * metrics.mDevPixelsPerCSSPixel.scale; nsContentView* view = FindViewForId(oldContentViews, scrollId); if (view) { // View already exists. Be sure to propagate scales for any values // that need to be calculated something in chrome-doc CSS pixels. ViewConfig config = view->GetViewConfig(); aXScale *= config.mXScale; aYScale *= config.mYScale; view->mFrameLoader = aFrameLoader; // If scale has changed, then we should update // current scroll offset to new scaled value if (aAccConfigXScale != view->mParentScaleX || aAccConfigYScale != view->mParentScaleY) { float xscroll = 0, yscroll = 0; view->GetScrollX(&xscroll); view->GetScrollY(&yscroll); xscroll = xscroll * (aAccConfigXScale / view->mParentScaleX); yscroll = yscroll * (aAccConfigYScale / view->mParentScaleY); view->ScrollTo(xscroll, yscroll); view->mParentScaleX = aAccConfigXScale; view->mParentScaleY = aAccConfigYScale; } // Collect only config scale values for scroll compensation aAccConfigXScale *= config.mXScale; aAccConfigYScale *= config.mYScale; } else { // View doesn't exist, so generate one. We start the view scroll offset at // the same position as the framemetric's scroll offset from the layer. // The default scale is 1, so no need to propagate scale down. ViewConfig config; config.mScrollOffset = nsPoint( NSIntPixelsToAppUnits(metrics.mScrollOffset.x, auPerCSSPixel) * aXScale, NSIntPixelsToAppUnits(metrics.mScrollOffset.y, auPerCSSPixel) * aYScale); view = new nsContentView(aFrameLoader, scrollId, metrics.mIsRoot, config); view->mParentScaleX = aAccConfigXScale; view->mParentScaleY = aAccConfigYScale; } // I don't know what units mViewportSize is in, hence use ToUnknownRect // here to mark the current frontier in type info propagation gfx::Rect viewport = metrics.mViewport.ToUnknownRect(); view->mViewportSize = nsSize( NSIntPixelsToAppUnits(viewport.width, auPerDevPixel) * aXScale, NSIntPixelsToAppUnits(viewport.height, auPerDevPixel) * aYScale); view->mContentSize = nsSize( NSFloatPixelsToAppUnits(metrics.mScrollableRect.width, auPerCSSPixel) * aXScale, NSFloatPixelsToAppUnits(metrics.mScrollableRect.height, auPerCSSPixel) * aYScale); newContentViews[scrollId] = view; } for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { BuildViewMap(oldContentViews, newContentViews, aFrameLoader, child, aXScale, aYScale, aAccConfigXScale, aAccConfigYScale); } }
static const FrameMetrics* GetFrameMetrics(Layer* aLayer) { ContainerLayer* container = aLayer->AsContainerLayer(); return container ? &container->GetFrameMetrics() : nullptr; }
already_AddRefed<Layer> RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, LayerManager* aManager, const nsIntRect& aVisibleRect) { NS_ABORT_IF_FALSE(aFrame, "makes no sense to have a shadow tree without a frame"); NS_ABORT_IF_FALSE(!mContainer || IsTempLayerManager(aManager) || mContainer->Manager() == aManager, "retaining manager changed out from under us ... HELP!"); if (mContainer && mContainer->Manager() != aManager) { // This can happen if aManager is a "temporary" manager, or if the // widget's layer manager changed out from under us. We need to // FIXME handle the former case somehow, probably with an API to // draw a manager's subtree. The latter is bad bad bad, but the // the NS_ABORT_IF_FALSE() above will flag it. Returning NULL // here will just cause the shadow subtree not to be rendered. return nsnull; } if (mContainer) { ClearContainer(mContainer); } ContainerLayer* shadowRoot = GetRootLayer(); if (!shadowRoot) { mContainer = nsnull; return nsnull; } NS_ABORT_IF_FALSE(!shadowRoot || shadowRoot->Manager() == aManager, "retaining manager changed out from under us ... HELP!"); // Wrap the shadow layer tree in mContainer. if (!mContainer) { mContainer = aManager->CreateContainerLayer(); } NS_ABORT_IF_FALSE(!mContainer->GetFirstChild(), "container of shadow tree shouldn't have a 'root' here"); mContainer->InsertAfter(shadowRoot, nsnull); AssertInTopLevelChromeDoc(mContainer, aFrame); ViewTransform transform; TransformShadowTree(aBuilder, mFrameLoader, aFrame, shadowRoot, transform); mContainer->SetClipRect(nsnull); if (mFrameLoader->AsyncScrollEnabled()) { const nsContentView* view = GetContentView(FrameMetrics::ROOT_SCROLL_ID); BuildBackgroundPatternFor(mContainer, shadowRoot, view->GetViewConfig(), mBackgroundColor, aManager, aFrame); } mContainer->SetVisibleRegion(aVisibleRect); return nsRefPtr<Layer>(mContainer).forget(); }
void ClientTiledThebesLayer::BeginPaint() { if (ClientManager()->IsRepeatTransaction()) { return; } mPaintData.mLowPrecisionPaintCount = 0; mPaintData.mPaintFinished = false; mPaintData.mCompositionBounds.SetEmpty(); mPaintData.mCriticalDisplayPort.SetEmpty(); if (!GetBaseTransform().Is2D()) { // Give up if there is a complex CSS transform on the layer. We might // eventually support these but for now it's too complicated to handle // given that it's a pretty rare scenario. return; } // Get the metrics of the nearest scrollable layer and the nearest layer // with a displayport. ContainerLayer* scrollAncestor = nullptr; ContainerLayer* displayPortAncestor = nullptr; for (ContainerLayer* ancestor = GetParent(); ancestor; ancestor = ancestor->GetParent()) { const FrameMetrics& metrics = ancestor->GetFrameMetrics(); if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) { scrollAncestor = ancestor; } if (!metrics.mDisplayPort.IsEmpty()) { displayPortAncestor = ancestor; // Any layer that has a displayport must be scrollable, so we can break // here. break; } } if (!displayPortAncestor || !scrollAncestor) { // No displayport or scroll ancestor, so we can't do progressive rendering. #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G) // Both Android and b2g are guaranteed to have a displayport set, so this // should never happen. NS_WARNING("Tiled Thebes layer with no scrollable container ancestor"); #endif return; } TILING_PRLOG(("TILING 0x%p: Found scrollAncestor 0x%p and displayPortAncestor 0x%p\n", this, scrollAncestor, displayPortAncestor)); const FrameMetrics& scrollMetrics = scrollAncestor->GetFrameMetrics(); const FrameMetrics& displayportMetrics = displayPortAncestor->GetFrameMetrics(); // Calculate the transform required to convert ParentLayer space of our // display port ancestor to the Layer space of this layer. gfx3DMatrix transformToDisplayPort = GetTransformToAncestorsParentLayer(this, displayPortAncestor); mPaintData.mTransformDisplayPortToLayer = transformToDisplayPort.Inverse(); // Note that below we use GetZoomToParent() in a number of places. Because this // code runs on the client side, the mTransformScale field of the FrameMetrics // will not have been set. This can result in incorrect values being returned // by GetZoomToParent() when we have CSS transforms set on some of these layers. // This code should be audited and updated as part of fixing bug 993525. // Compute the critical display port that applies to this layer in the // LayoutDevice space of this layer. ParentLayerRect criticalDisplayPort = (displayportMetrics.mCriticalDisplayPort * displayportMetrics.GetZoomToParent()) + displayportMetrics.mCompositionBounds.TopLeft(); mPaintData.mCriticalDisplayPort = RoundedOut( ApplyParentLayerToLayerTransform(mPaintData.mTransformDisplayPortToLayer, criticalDisplayPort)); TILING_PRLOG_OBJ(("TILING 0x%p: Critical displayport %s\n", this, tmpstr.get()), mPaintData.mCriticalDisplayPort); // Compute the viewport that applies to this layer in the LayoutDevice // space of this layer. ParentLayerRect viewport = (displayportMetrics.mViewport * displayportMetrics.GetZoomToParent()) + displayportMetrics.mCompositionBounds.TopLeft(); mPaintData.mViewport = ApplyParentLayerToLayerTransform( mPaintData.mTransformDisplayPortToLayer, viewport); TILING_PRLOG_OBJ(("TILING 0x%p: Viewport %s\n", this, tmpstr.get()), mPaintData.mViewport); // Store the resolution from the displayport ancestor layer. Because this is Gecko-side, // before any async transforms have occurred, we can use the zoom for this. mPaintData.mResolution = displayportMetrics.GetZoomToParent(); TILING_PRLOG(("TILING 0x%p: Resolution %f\n", this, mPaintData.mResolution.scale)); // Store the applicable composition bounds in this layer's Layer units. gfx3DMatrix transformToCompBounds = GetTransformToAncestorsParentLayer(this, scrollAncestor); mPaintData.mCompositionBounds = ApplyParentLayerToLayerTransform( transformToCompBounds.Inverse(), ParentLayerRect(scrollMetrics.mCompositionBounds)); TILING_PRLOG_OBJ(("TILING 0x%p: Composition bounds %s\n", this, tmpstr.get()), mPaintData.mCompositionBounds); // Calculate the scroll offset since the last transaction mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoomToParent(); TILING_PRLOG_OBJ(("TILING 0x%p: Scroll offset %s\n", this, tmpstr.get()), mPaintData.mScrollOffset); }
bool LayerTransactionParent::RecvUpdate(const InfallibleTArray<Edit>& cset, const TargetConfig& targetConfig, const bool& isFirstPaint, InfallibleTArray<EditReply>* reply) { #ifdef COMPOSITOR_PERFORMANCE_WARNING TimeStamp updateStart = TimeStamp::Now(); #endif MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length())); if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) { return true; } EditReplyVector replyv; layer_manager()->BeginTransactionWithTarget(NULL); for (EditArray::index_type i = 0; i < cset.Length(); ++i) { const Edit& edit = cset[i]; switch (edit.type()) { // Create* ops case Edit::TOpCreateThebesLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateThebesLayer")); nsRefPtr<ThebesLayerComposite> layer = layer_manager()->CreateThebesLayerComposite(); AsLayerComposite(edit.get_OpCreateThebesLayer())->Bind(layer); break; } case Edit::TOpCreateContainerLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer")); nsRefPtr<ContainerLayer> layer = layer_manager()->CreateContainerLayerComposite(); AsLayerComposite(edit.get_OpCreateContainerLayer())->Bind(layer); break; } case Edit::TOpCreateImageLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer")); nsRefPtr<ImageLayerComposite> layer = layer_manager()->CreateImageLayerComposite(); AsLayerComposite(edit.get_OpCreateImageLayer())->Bind(layer); break; } case Edit::TOpCreateColorLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer")); nsRefPtr<ColorLayerComposite> layer = layer_manager()->CreateColorLayerComposite(); AsLayerComposite(edit.get_OpCreateColorLayer())->Bind(layer); break; } case Edit::TOpCreateCanvasLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer")); nsRefPtr<CanvasLayerComposite> layer = layer_manager()->CreateCanvasLayerComposite(); AsLayerComposite(edit.get_OpCreateCanvasLayer())->Bind(layer); break; } case Edit::TOpCreateRefLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer")); nsRefPtr<RefLayerComposite> layer = layer_manager()->CreateRefLayerComposite(); AsLayerComposite(edit.get_OpCreateRefLayer())->Bind(layer); break; } // Attributes case Edit::TOpSetLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes")); const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes(); Layer* layer = AsLayerComposite(osla)->AsLayer(); const LayerAttributes& attrs = osla.attrs(); const CommonLayerAttributes& common = attrs.common(); layer->SetVisibleRegion(common.visibleRegion()); layer->SetContentFlags(common.contentFlags()); layer->SetOpacity(common.opacity()); layer->SetClipRect(common.useClipRect() ? &common.clipRect() : NULL); layer->SetBaseTransform(common.transform().value()); layer->SetPostScale(common.postXScale(), common.postYScale()); layer->SetIsFixedPosition(common.isFixedPosition()); layer->SetFixedPositionAnchor(common.fixedPositionAnchor()); layer->SetFixedPositionMargins(common.fixedPositionMargin()); if (PLayerParent* maskLayer = common.maskLayerParent()) { layer->SetMaskLayer(cast(maskLayer)->AsLayer()); } else { layer->SetMaskLayer(NULL); } layer->SetAnimations(common.animations()); typedef SpecificLayerAttributes Specific; const SpecificLayerAttributes& specific = attrs.specific(); switch (specific.type()) { case Specific::Tnull_t: break; case Specific::TThebesLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] thebes layer")); ThebesLayerComposite* thebesLayer = static_cast<ThebesLayerComposite*>(layer); const ThebesLayerAttributes& attrs = specific.get_ThebesLayerAttributes(); thebesLayer->SetValidRegion(attrs.validRegion()); break; } case Specific::TContainerLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] container layer")); ContainerLayer* containerLayer = static_cast<ContainerLayer*>(layer); const ContainerLayerAttributes& attrs = specific.get_ContainerLayerAttributes(); containerLayer->SetFrameMetrics(attrs.metrics()); containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale()); containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale()); break; } case Specific::TColorLayerAttributes: MOZ_LAYERS_LOG(("[ParentSide] color layer")); static_cast<ColorLayer*>(layer)->SetColor( specific.get_ColorLayerAttributes().color().value()); break; case Specific::TCanvasLayerAttributes: MOZ_LAYERS_LOG(("[ParentSide] canvas layer")); static_cast<CanvasLayer*>(layer)->SetFilter( specific.get_CanvasLayerAttributes().filter()); static_cast<CanvasLayerComposite*>(layer)->SetBounds( specific.get_CanvasLayerAttributes().bounds()); break; case Specific::TRefLayerAttributes: MOZ_LAYERS_LOG(("[ParentSide] ref layer")); static_cast<RefLayer*>(layer)->SetReferentId( specific.get_RefLayerAttributes().id()); break; case Specific::TImageLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] image layer")); ImageLayer* imageLayer = static_cast<ImageLayer*>(layer); const ImageLayerAttributes& attrs = specific.get_ImageLayerAttributes(); imageLayer->SetFilter(attrs.filter()); imageLayer->SetScaleToSize(attrs.scaleToSize(), attrs.scaleMode()); break; } default: NS_RUNTIMEABORT("not reached"); } break; } case Edit::TOpSetColoredBorders: { if (edit.get_OpSetColoredBorders().enabled()) { mLayerManager->GetCompositor()->EnableColoredBorders(); } else { mLayerManager->GetCompositor()->DisableColoredBorders(); } break; } // Tree ops case Edit::TOpSetRoot: { MOZ_LAYERS_LOG(("[ParentSide] SetRoot")); mRoot = AsLayerComposite(edit.get_OpSetRoot())->AsContainer(); break; } case Edit::TOpInsertAfter: { MOZ_LAYERS_LOG(("[ParentSide] InsertAfter")); const OpInsertAfter& oia = edit.get_OpInsertAfter(); ShadowContainer(oia)->AsContainer()->InsertAfter( ShadowChild(oia)->AsLayer(), ShadowAfter(oia)->AsLayer()); break; } case Edit::TOpAppendChild: { MOZ_LAYERS_LOG(("[ParentSide] AppendChild")); const OpAppendChild& oac = edit.get_OpAppendChild(); ShadowContainer(oac)->AsContainer()->InsertAfter( ShadowChild(oac)->AsLayer(), NULL); break; } case Edit::TOpRemoveChild: { MOZ_LAYERS_LOG(("[ParentSide] RemoveChild")); const OpRemoveChild& orc = edit.get_OpRemoveChild(); Layer* childLayer = ShadowChild(orc)->AsLayer(); ShadowContainer(orc)->AsContainer()->RemoveChild(childLayer); break; } case Edit::TOpRepositionChild: { MOZ_LAYERS_LOG(("[ParentSide] RepositionChild")); const OpRepositionChild& orc = edit.get_OpRepositionChild(); ShadowContainer(orc)->AsContainer()->RepositionChild( ShadowChild(orc)->AsLayer(), ShadowAfter(orc)->AsLayer()); break; } case Edit::TOpRaiseToTopChild: { MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild")); const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild(); ShadowContainer(rtc)->AsContainer()->RepositionChild( ShadowChild(rtc)->AsLayer(), NULL); break; } case Edit::TCompositableOperation: { ReceiveCompositableUpdate(edit.get_CompositableOperation(), replyv); break; } case Edit::TOpAttachCompositable: { const OpAttachCompositable& op = edit.get_OpAttachCompositable(); Attach(cast(op.layerParent()), cast(op.compositableParent())); break; } case Edit::TOpAttachAsyncCompositable: { const OpAttachAsyncCompositable& op = edit.get_OpAttachAsyncCompositable(); CompositableParent* compositableParent = CompositableMap::Get(op.containerID()); MOZ_ASSERT(compositableParent, "CompositableParent not found in the map"); Attach(cast(op.layerParent()), compositableParent); compositableParent->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID()); break; } default: NS_RUNTIMEABORT("not reached"); } } layer_manager()->EndTransaction(NULL, NULL, LayerManager::END_NO_IMMEDIATE_REDRAW); if (reply) { reply->SetCapacity(replyv.size()); if (replyv.size() > 0) { reply->AppendElements(&replyv.front(), replyv.size()); } } // 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(); mShadowLayersManager->ShadowLayersUpdated(this, targetConfig, isFirstPaint); #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 return true; }
void AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer, const LayoutDeviceToLayerScale& aResolution) { LayerComposite* layerComposite = aLayer->AsLayerComposite(); ContainerLayer* container = aLayer->AsContainerLayer(); const FrameMetrics& metrics = container->GetFrameMetrics(); // We must apply the resolution scale before a pan/zoom transform, so we call // GetTransform here. const gfx3DMatrix& currentTransform = aLayer->GetTransform(); gfx3DMatrix oldTransform = currentTransform; gfx3DMatrix treeTransform; CSSToLayerScale geckoZoom = metrics.mDevPixelsPerCSSPixel * aResolution; LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.mScrollOffset * geckoZoom); if (mIsFirstPaint) { mContentRect = metrics.mScrollableRect; SetFirstPaintViewport(scrollOffsetLayerPixels, geckoZoom, mContentRect); mIsFirstPaint = false; } else if (!metrics.mScrollableRect.IsEqualEdges(mContentRect)) { mContentRect = metrics.mScrollableRect; SetPageRect(mContentRect); } // We synchronise the viewport information with Java after sending the above // notifications, so that Java can take these into account in its response. // Calculate the absolute display port to send to Java LayerIntRect displayPort = RoundedToInt( (metrics.mCriticalDisplayPort.IsEmpty() ? metrics.mDisplayPort : metrics.mCriticalDisplayPort ) * geckoZoom); displayPort += scrollOffsetLayerPixels; LayerMargin fixedLayerMargins(0, 0, 0, 0); ScreenPoint offset(0, 0); // Ideally we would initialize userZoom to AsyncPanZoomController::CalculateResolution(metrics) // but this causes a reftest-ipc test to fail (see bug 883646 comment 27). The reason for this // appears to be that metrics.mZoom is poorly initialized in some scenarios. In these scenarios, // however, we can assume there is no async zooming in progress and so the following statement // works fine. CSSToScreenScale userZoom(metrics.mDevPixelsPerCSSPixel.scale * metrics.mResolution.scale); ScreenPoint userScroll = metrics.mScrollOffset * userZoom; SyncViewportInfo(displayPort, geckoZoom, mLayersUpdated, userScroll, userZoom, fixedLayerMargins, offset); mLayersUpdated = false; // Apply the render offset mLayerManager->GetCompositor()->SetScreenRenderOffset(offset); // Handle transformations for asynchronous panning and zooming. We determine the // zoom used by Gecko from the transformation set on the root layer, and we // determine the scroll offset used by Gecko from the frame metrics of the // primary scrollable layer. We compare this to the user zoom and scroll // offset in the view transform we obtained from Java in order to compute the // transformation we need to apply. LayerToScreenScale zoomAdjust = userZoom / geckoZoom; LayerIntPoint geckoScroll(0, 0); if (metrics.IsScrollable()) { geckoScroll = scrollOffsetLayerPixels; } LayerPoint translation = (userScroll / zoomAdjust) - geckoScroll; treeTransform = gfx3DMatrix(ViewTransform(-translation, userZoom / metrics.mDevPixelsPerCSSPixel)); // The transform already takes the resolution scale into account. Since we // will apply the resolution scale again when computing the effective // transform, we must apply the inverse resolution scale here. gfx3DMatrix computedTransform = treeTransform * currentTransform; computedTransform.Scale(1.0f/container->GetPreXScale(), 1.0f/container->GetPreYScale(), 1); computedTransform.ScalePost(1.0f/container->GetPostXScale(), 1.0f/container->GetPostYScale(), 1); layerComposite->SetShadowTransform(computedTransform); NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(), "overwriting animated transform!"); // Apply resolution scaling to the old transform - the layer tree as it is // doesn't have the necessary transform to display correctly. oldTransform.Scale(aResolution.scale, aResolution.scale, 1); // Make sure that overscroll and under-zoom are represented in the old // transform so that fixed position content moves and scales accordingly. // These calculations will effectively scale and offset fixed position layers // in screen space when the compensatory transform is performed in // AlignFixedLayersForAnchorPoint. ScreenRect contentScreenRect = mContentRect * userZoom; gfxPoint3D overscrollTranslation; if (userScroll.x < contentScreenRect.x) { overscrollTranslation.x = contentScreenRect.x - userScroll.x; } else if (userScroll.x + metrics.mCompositionBounds.width > contentScreenRect.XMost()) { overscrollTranslation.x = contentScreenRect.XMost() - (userScroll.x + metrics.mCompositionBounds.width); } if (userScroll.y < contentScreenRect.y) { overscrollTranslation.y = contentScreenRect.y - userScroll.y; } else if (userScroll.y + metrics.mCompositionBounds.height > contentScreenRect.YMost()) { overscrollTranslation.y = contentScreenRect.YMost() - (userScroll.y + metrics.mCompositionBounds.height); } oldTransform.Translate(overscrollTranslation); gfxSize underZoomScale(1.0f, 1.0f); if (mContentRect.width * userZoom.scale < metrics.mCompositionBounds.width) { underZoomScale.width = (mContentRect.width * userZoom.scale) / metrics.mCompositionBounds.width; } if (mContentRect.height * userZoom.scale < metrics.mCompositionBounds.height) { underZoomScale.height = (mContentRect.height * userZoom.scale) / metrics.mCompositionBounds.height; } oldTransform.Scale(underZoomScale.width, underZoomScale.height, 1); // Make sure fixed position layers don't move away from their anchor points // when we're asynchronously panning or zooming AlignFixedLayersForAnchorPoint(aLayer, aLayer, oldTransform, fixedLayerMargins); }
bool AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, Layer *aLayer, bool* aWantNextFrame) { bool appliedTransform = false; for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { appliedTransform |= ApplyAsyncContentTransformToTree(aCurrentFrame, child, aWantNextFrame); } ContainerLayer* container = aLayer->AsContainerLayer(); if (!container) { return appliedTransform; } if (AsyncPanZoomController* controller = container->GetAsyncPanZoomController()) { LayerComposite* layerComposite = aLayer->AsLayerComposite(); gfx3DMatrix oldTransform = aLayer->GetTransform(); ViewTransform treeTransform; ScreenPoint scrollOffset; *aWantNextFrame |= controller->SampleContentTransformForFrame(aCurrentFrame, &treeTransform, scrollOffset); const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform(); const FrameMetrics& metrics = container->GetFrameMetrics(); // XXX We use rootTransform instead of metrics.mResolution here because on // Fennec the resolution is set on the root layer rather than the scrollable layer. // The SyncFrameMetrics call and the paintScale variable are used on Fennec only // so it doesn't affect any other platforms. See bug 732971. CSSToLayerScale paintScale = metrics.mDevPixelsPerCSSPixel / LayerToLayoutDeviceScale(rootTransform.GetXScale(), rootTransform.GetYScale()); CSSRect displayPort(metrics.mCriticalDisplayPort.IsEmpty() ? metrics.mDisplayPort : metrics.mCriticalDisplayPort); LayerMargin fixedLayerMargins(0, 0, 0, 0); ScreenPoint offset(0, 0); SyncFrameMetrics(scrollOffset, treeTransform.mScale.scale, metrics.mScrollableRect, mLayersUpdated, displayPort, paintScale, mIsFirstPaint, fixedLayerMargins, offset); mIsFirstPaint = false; mLayersUpdated = false; // Apply the render offset mLayerManager->GetCompositor()->SetScreenRenderOffset(offset); gfx3DMatrix transform(gfx3DMatrix(treeTransform) * aLayer->GetTransform()); // The transform already takes the resolution scale into account. Since we // will apply the resolution scale again when computing the effective // transform, we must apply the inverse resolution scale here. transform.Scale(1.0f/container->GetPreXScale(), 1.0f/container->GetPreYScale(), 1); transform.ScalePost(1.0f/aLayer->GetPostXScale(), 1.0f/aLayer->GetPostYScale(), 1); layerComposite->SetShadowTransform(transform); NS_ASSERTION(!layerComposite->GetShadowTransformSetByAnimation(), "overwriting animated transform!"); // Apply resolution scaling to the old transform - the layer tree as it is // doesn't have the necessary transform to display correctly. #ifdef MOZ_WIDGET_ANDROID // XXX We use rootTransform instead of the resolution on the individual layer's // FrameMetrics on Fennec because the resolution is set on the root layer rather // than the scrollable layer. See bug 732971. On non-Fennec we do the right thing. LayoutDeviceToLayerScale resolution(1.0 / rootTransform.GetXScale(), 1.0 / rootTransform.GetYScale()); #else LayoutDeviceToLayerScale resolution = metrics.mResolution; #endif oldTransform.Scale(resolution.scale, resolution.scale, 1); AlignFixedLayersForAnchorPoint(aLayer, aLayer, oldTransform, fixedLayerMargins); appliedTransform = true; } return appliedTransform; }
bool ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset, const TargetConfig& targetConfig, const bool& isFirstPaint, InfallibleTArray<EditReply>* reply) { #ifdef COMPOSITOR_PERFORMANCE_WARNING TimeStamp updateStart = TimeStamp::Now(); #endif MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length())); if (mDestroyed || layer_manager()->IsDestroyed()) { return true; } EditReplyVector replyv; layer_manager()->BeginTransactionWithTarget(NULL); for (EditArray::index_type i = 0; i < cset.Length(); ++i) { const Edit& edit = cset[i]; switch (edit.type()) { // Create* ops case Edit::TOpCreateThebesLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateThebesLayer")); nsRefPtr<ShadowThebesLayer> layer = layer_manager()->CreateShadowThebesLayer(); layer->SetAllocator(this); AsShadowLayer(edit.get_OpCreateThebesLayer())->Bind(layer); break; } case Edit::TOpCreateContainerLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer")); nsRefPtr<ContainerLayer> layer = layer_manager()->CreateShadowContainerLayer(); AsShadowLayer(edit.get_OpCreateContainerLayer())->Bind(layer); break; } case Edit::TOpCreateImageLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer")); nsRefPtr<ShadowImageLayer> layer = layer_manager()->CreateShadowImageLayer(); AsShadowLayer(edit.get_OpCreateImageLayer())->Bind(layer); break; } case Edit::TOpCreateColorLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer")); nsRefPtr<ShadowColorLayer> layer = layer_manager()->CreateShadowColorLayer(); AsShadowLayer(edit.get_OpCreateColorLayer())->Bind(layer); break; } case Edit::TOpCreateCanvasLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer")); nsRefPtr<ShadowCanvasLayer> layer = layer_manager()->CreateShadowCanvasLayer(); layer->SetAllocator(this); AsShadowLayer(edit.get_OpCreateCanvasLayer())->Bind(layer); break; } case Edit::TOpCreateRefLayer: { MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer")); nsRefPtr<ShadowRefLayer> layer = layer_manager()->CreateShadowRefLayer(); layer->SetAllocator(this); AsShadowLayer(edit.get_OpCreateRefLayer())->Bind(layer); break; } // Attributes case Edit::TOpSetLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes")); const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes(); Layer* layer = AsShadowLayer(osla)->AsLayer(); const LayerAttributes& attrs = osla.attrs(); const CommonLayerAttributes& common = attrs.common(); layer->SetVisibleRegion(common.visibleRegion()); layer->SetContentFlags(common.contentFlags()); layer->SetOpacity(common.opacity()); layer->SetClipRect(common.useClipRect() ? &common.clipRect() : NULL); layer->SetBaseTransform(common.transform().value()); layer->SetPostScale(common.postXScale(), common.postYScale()); static bool fixedPositionLayersEnabled = getenv("MOZ_ENABLE_FIXED_POSITION_LAYERS") != 0; if (fixedPositionLayersEnabled) { layer->SetIsFixedPosition(common.isFixedPosition()); layer->SetFixedPositionAnchor(common.fixedPositionAnchor()); } if (PLayerParent* maskLayer = common.maskLayerParent()) { layer->SetMaskLayer(cast(maskLayer)->AsLayer()); } else { layer->SetMaskLayer(NULL); } layer->SetAnimations(common.animations()); typedef SpecificLayerAttributes Specific; const SpecificLayerAttributes& specific = attrs.specific(); switch (specific.type()) { case Specific::Tnull_t: break; case Specific::TThebesLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] thebes layer")); ShadowThebesLayer* thebesLayer = static_cast<ShadowThebesLayer*>(layer); const ThebesLayerAttributes& attrs = specific.get_ThebesLayerAttributes(); thebesLayer->SetValidRegion(attrs.validRegion()); break; } case Specific::TContainerLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] container layer")); ContainerLayer* containerLayer = static_cast<ContainerLayer*>(layer); const ContainerLayerAttributes& attrs = specific.get_ContainerLayerAttributes(); containerLayer->SetFrameMetrics(attrs.metrics()); containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale()); break; } case Specific::TColorLayerAttributes: MOZ_LAYERS_LOG(("[ParentSide] color layer")); static_cast<ColorLayer*>(layer)->SetColor( specific.get_ColorLayerAttributes().color().value()); break; case Specific::TCanvasLayerAttributes: MOZ_LAYERS_LOG(("[ParentSide] canvas layer")); static_cast<CanvasLayer*>(layer)->SetFilter( specific.get_CanvasLayerAttributes().filter()); break; case Specific::TRefLayerAttributes: MOZ_LAYERS_LOG(("[ParentSide] ref layer")); static_cast<RefLayer*>(layer)->SetReferentId( specific.get_RefLayerAttributes().id()); break; case Specific::TImageLayerAttributes: { MOZ_LAYERS_LOG(("[ParentSide] image layer")); ImageLayer* imageLayer = static_cast<ImageLayer*>(layer); const ImageLayerAttributes& attrs = specific.get_ImageLayerAttributes(); imageLayer->SetFilter(attrs.filter()); imageLayer->SetForceSingleTile(attrs.forceSingleTile()); break; } default: NS_RUNTIMEABORT("not reached"); } break; } // Tree ops case Edit::TOpSetRoot: { MOZ_LAYERS_LOG(("[ParentSide] SetRoot")); mRoot = AsShadowLayer(edit.get_OpSetRoot())->AsContainer(); break; } case Edit::TOpInsertAfter: { MOZ_LAYERS_LOG(("[ParentSide] InsertAfter")); const OpInsertAfter& oia = edit.get_OpInsertAfter(); ShadowContainer(oia)->AsContainer()->InsertAfter( ShadowChild(oia)->AsLayer(), ShadowAfter(oia)->AsLayer()); break; } case Edit::TOpAppendChild: { MOZ_LAYERS_LOG(("[ParentSide] AppendChild")); const OpAppendChild& oac = edit.get_OpAppendChild(); ShadowContainer(oac)->AsContainer()->InsertAfter( ShadowChild(oac)->AsLayer(), NULL); break; } case Edit::TOpRemoveChild: { MOZ_LAYERS_LOG(("[ParentSide] RemoveChild")); const OpRemoveChild& orc = edit.get_OpRemoveChild(); Layer* childLayer = ShadowChild(orc)->AsLayer(); ShadowContainer(orc)->AsContainer()->RemoveChild(childLayer); break; } case Edit::TOpRepositionChild: { MOZ_LAYERS_LOG(("[ParentSide] RepositionChild")); const OpRepositionChild& orc = edit.get_OpRepositionChild(); ShadowContainer(orc)->AsContainer()->RepositionChild( ShadowChild(orc)->AsLayer(), ShadowAfter(orc)->AsLayer()); break; } case Edit::TOpRaiseToTopChild: { MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild")); const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild(); ShadowContainer(rtc)->AsContainer()->RepositionChild( ShadowChild(rtc)->AsLayer(), NULL); break; } case Edit::TOpPaintTiledLayerBuffer: { MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer")); const OpPaintTiledLayerBuffer& op = edit.get_OpPaintTiledLayerBuffer(); ShadowLayerParent* shadow = AsShadowLayer(op); ShadowThebesLayer* shadowLayer = static_cast<ShadowThebesLayer*>(shadow->AsLayer()); TiledLayerComposer* tileComposer = shadowLayer->AsTiledLayerComposer(); NS_ASSERTION(tileComposer, "shadowLayer is not a tile composer"); BasicTiledLayerBuffer* p = (BasicTiledLayerBuffer*)op.tiledLayerBuffer(); tileComposer->PaintedTiledLayerBuffer(p); break; } case Edit::TOpPaintThebesBuffer: { MOZ_LAYERS_LOG(("[ParentSide] Paint ThebesLayer")); const OpPaintThebesBuffer& op = edit.get_OpPaintThebesBuffer(); ShadowLayerParent* shadow = AsShadowLayer(op); ShadowThebesLayer* thebes = static_cast<ShadowThebesLayer*>(shadow->AsLayer()); const ThebesBuffer& newFront = op.newFrontBuffer(); RenderTraceInvalidateStart(thebes, "FF00FF", op.updatedRegion().GetBounds()); OptionalThebesBuffer newBack; nsIntRegion newValidRegion; OptionalThebesBuffer readonlyFront; nsIntRegion frontUpdatedRegion; thebes->Swap(newFront, op.updatedRegion(), &newBack, &newValidRegion, &readonlyFront, &frontUpdatedRegion); replyv.push_back( OpThebesBufferSwap( shadow, NULL, newBack, newValidRegion, readonlyFront, frontUpdatedRegion)); RenderTraceInvalidateEnd(thebes, "FF00FF"); break; } case Edit::TOpPaintCanvas: { MOZ_LAYERS_LOG(("[ParentSide] Paint CanvasLayer")); const OpPaintCanvas& op = edit.get_OpPaintCanvas(); ShadowLayerParent* shadow = AsShadowLayer(op); ShadowCanvasLayer* canvas = static_cast<ShadowCanvasLayer*>(shadow->AsLayer()); RenderTraceInvalidateStart(canvas, "FF00FF", canvas->GetVisibleRegion().GetBounds()); canvas->SetAllocator(this); CanvasSurface newBack; canvas->Swap(op.newFrontBuffer(), op.needYFlip(), &newBack); canvas->Updated(); replyv.push_back(OpBufferSwap(shadow, NULL, newBack)); RenderTraceInvalidateEnd(canvas, "FF00FF"); break; } case Edit::TOpPaintImage: { MOZ_LAYERS_LOG(("[ParentSide] Paint ImageLayer")); const OpPaintImage& op = edit.get_OpPaintImage(); ShadowLayerParent* shadow = AsShadowLayer(op); ShadowImageLayer* image = static_cast<ShadowImageLayer*>(shadow->AsLayer()); RenderTraceInvalidateStart(image, "FF00FF", image->GetVisibleRegion().GetBounds()); image->SetAllocator(this); SharedImage newBack; image->Swap(op.newFrontBuffer(), &newBack); replyv.push_back(OpImageSwap(shadow, NULL, newBack)); RenderTraceInvalidateEnd(image, "FF00FF"); break; } default: NS_RUNTIMEABORT("not reached"); } } layer_manager()->EndTransaction(NULL, NULL, LayerManager::END_NO_IMMEDIATE_REDRAW); reply->SetCapacity(replyv.size()); if (replyv.size() > 0) { reply->AppendElements(&replyv.front(), replyv.size()); } // Ensure that any pending operations involving back and front // buffers have completed, so that neither process stomps on the // other's buffer contents. ShadowLayerManager::PlatformSyncBeforeReplyUpdate(); mShadowLayersManager->ShadowLayersUpdated(this, targetConfig, isFirstPaint); #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 return true; }
void ClientTiledThebesLayer::BeginPaint() { if (ClientManager()->IsRepeatTransaction()) { return; } mPaintData.mLowPrecisionPaintCount = 0; mPaintData.mPaintFinished = false; // Calculate the transform required to convert screen space into layer space mPaintData.mTransformScreenToLayer = GetEffectiveTransform(); // XXX Not sure if this code for intermediate surfaces is correct. // It rarely gets hit though, and shouldn't have terrible consequences // even if it is wrong. for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { if (parent->UseIntermediateSurface()) { mPaintData.mTransformScreenToLayer.PreMultiply(parent->GetEffectiveTransform()); } } mPaintData.mTransformScreenToLayer.Invert(); // Compute the critical display port in layer space. mPaintData.mLayerCriticalDisplayPort.SetEmpty(); const gfx::Rect& criticalDisplayPort = GetParent()->GetFrameMetrics().mCriticalDisplayPort; if (!criticalDisplayPort.IsEmpty()) { gfxRect transformedCriticalDisplayPort = mPaintData.mTransformScreenToLayer.TransformBounds( gfxRect(criticalDisplayPort.x, criticalDisplayPort.y, criticalDisplayPort.width, criticalDisplayPort.height)); transformedCriticalDisplayPort.RoundOut(); mPaintData.mLayerCriticalDisplayPort = nsIntRect(transformedCriticalDisplayPort.x, transformedCriticalDisplayPort.y, transformedCriticalDisplayPort.width, transformedCriticalDisplayPort.height); } // Calculate the frame resolution. mPaintData.mResolution.SizeTo(1, 1); for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& metrics = parent->GetFrameMetrics(); mPaintData.mResolution.width *= metrics.mResolution.width; mPaintData.mResolution.height *= metrics.mResolution.height; } // Calculate the scroll offset since the last transaction, and the // composition bounds. mPaintData.mCompositionBounds.SetEmpty(); mPaintData.mScrollOffset.MoveTo(0, 0); Layer* primaryScrollable = ClientManager()->GetPrimaryScrollableLayer(); if (primaryScrollable) { const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); mPaintData.mScrollOffset = metrics.mScrollOffset; gfxRect transformedViewport = mPaintData.mTransformScreenToLayer.TransformBounds( gfxRect(metrics.mCompositionBounds.x, metrics.mCompositionBounds.y, metrics.mCompositionBounds.width, metrics.mCompositionBounds.height)); transformedViewport.RoundOut(); mPaintData.mCompositionBounds = nsIntRect(transformedViewport.x, transformedViewport.y, transformedViewport.width, transformedViewport.height); } }
void CompositorParent::TransformShadowTree() { Layer* layer = GetPrimaryScrollableLayer(); ShadowLayer* shadow = layer->AsShadowLayer(); ContainerLayer* container = layer->AsContainerLayer(); const FrameMetrics& metrics = container->GetFrameMetrics(); const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform(); const gfx3DMatrix& currentTransform = layer->GetTransform(); float rootScaleX = rootTransform.GetXScale(); float rootScaleY = rootTransform.GetYScale(); if (mIsFirstPaint) { mContentRect = metrics.mContentRect; SetFirstPaintViewport(metrics.mViewportScrollOffset, 1/rootScaleX, mContentRect, metrics.mCSSContentRect); mIsFirstPaint = false; } else if (!metrics.mContentRect.IsEqualEdges(mContentRect)) { mContentRect = metrics.mContentRect; SetPageRect(1/rootScaleX, mContentRect, metrics.mCSSContentRect); } // We synchronise the viewport information with Java after sending the above // notifications, so that Java can take these into account in its response. // Calculate the absolute display port to send to Java nsIntRect displayPort = metrics.mDisplayPort; nsIntPoint scrollOffset = metrics.mViewportScrollOffset; displayPort.x += scrollOffset.x; displayPort.y += scrollOffset.y; SyncViewportInfo(displayPort, 1/rootScaleX, mLayersUpdated, mScrollOffset, mXScale, mYScale); mLayersUpdated = false; // Handle transformations for asynchronous panning and zooming. We determine the // zoom used by Gecko from the transformation set on the root layer, and we // determine the scroll offset used by Gecko from the frame metrics of the // primary scrollable layer. We compare this to the desired zoom and scroll // offset in the view transform we obtained from Java in order to compute the // transformation we need to apply. float tempScaleDiffX = rootScaleX * mXScale; float tempScaleDiffY = rootScaleY * mYScale; nsIntPoint metricsScrollOffset(0, 0); if (metrics.IsScrollable()) metricsScrollOffset = metrics.mViewportScrollOffset; nsIntPoint scrollCompensation( (mScrollOffset.x / tempScaleDiffX - metricsScrollOffset.x) * mXScale, (mScrollOffset.y / tempScaleDiffY - metricsScrollOffset.y) * mYScale); ViewTransform treeTransform(-scrollCompensation, mXScale, mYScale); shadow->SetShadowTransform(gfx3DMatrix(treeTransform) * currentTransform); // Alter the scroll offset so that fixed position layers remain within // the page area. float offsetX = mScrollOffset.x / tempScaleDiffX; float offsetY = mScrollOffset.y / tempScaleDiffY; offsetX = NS_MAX((float)mContentRect.x, NS_MIN(offsetX, (float)(mContentRect.XMost() - mWidgetSize.width))); offsetY = NS_MAX((float)mContentRect.y, NS_MIN(offsetY, (float)(mContentRect.YMost() - mWidgetSize.height))); gfxPoint reverseViewTranslation(offsetX - metricsScrollOffset.x, offsetY - metricsScrollOffset.y); TranslateFixedLayers(layer, reverseViewTranslation); }
/* static */ void LayerManagerComposite::ComputeRenderIntegrityInternal(Layer* aLayer, nsIntRegion& aScreenRegion, nsIntRegion& aLowPrecisionScreenRegion, const Matrix4x4& aTransform) { if (aLayer->GetOpacity() <= 0.f || (aScreenRegion.IsEmpty() && aLowPrecisionScreenRegion.IsEmpty())) { return; } // If the layer's a container, recurse into all of its children ContainerLayer* container = aLayer->AsContainerLayer(); if (container) { // Accumulate the transform of intermediate surfaces Matrix4x4 transform = aTransform; if (container->UseIntermediateSurface()) { transform = aLayer->GetEffectiveTransform(); transform = aTransform * transform; } for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { ComputeRenderIntegrityInternal(child, aScreenRegion, aLowPrecisionScreenRegion, transform); } return; } // Only painted layers can be incomplete PaintedLayer* paintedLayer = aLayer->AsPaintedLayer(); if (!paintedLayer) { return; } // See if there's any incomplete rendering nsIntRegion incompleteRegion = aLayer->GetEffectiveVisibleRegion(); incompleteRegion.Sub(incompleteRegion, paintedLayer->GetValidRegion()); if (!incompleteRegion.IsEmpty()) { // Calculate the transform to get between screen and layer space Matrix4x4 transformToScreen = aLayer->GetEffectiveTransform(); transformToScreen = aTransform * transformToScreen; SubtractTransformedRegion(aScreenRegion, incompleteRegion, transformToScreen); // See if there's any incomplete low-precision rendering TiledContentHost* composer = nullptr; LayerComposite* shadow = aLayer->AsLayerComposite(); if (shadow) { composer = shadow->GetCompositableHost()->AsTiledContentHost(); if (composer) { incompleteRegion.Sub(incompleteRegion, composer->GetValidLowPrecisionRegion()); if (!incompleteRegion.IsEmpty()) { SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen); } } } // If we can't get a valid low precision region, assume it's the same as // the high precision region. if (!composer) { SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen); } } }
void CompositorParent::TransformShadowTree() { Layer* layer = GetPrimaryScrollableLayer(); ShadowLayer* shadow = layer->AsShadowLayer(); ContainerLayer* container = layer->AsContainerLayer(); const FrameMetrics* metrics = &container->GetFrameMetrics(); const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform(); const gfx3DMatrix& currentTransform = layer->GetTransform(); float rootScaleX = rootTransform.GetXScale(); float rootScaleY = rootTransform.GetYScale(); if (mIsFirstPaint && metrics) { nsIntPoint scrollOffset = metrics->mViewportScrollOffset; mContentSize = metrics->mContentSize; SetFirstPaintViewport(scrollOffset.x, scrollOffset.y, 1/rootScaleX, mContentSize.width, mContentSize.height, metrics->mCSSContentSize.width, metrics->mCSSContentSize.height); mIsFirstPaint = false; } else if (metrics && (metrics->mContentSize != mContentSize)) { mContentSize = metrics->mContentSize; SetPageSize(1/rootScaleX, mContentSize.width, mContentSize.height, metrics->mCSSContentSize.width, metrics->mCSSContentSize.height); } // We synchronise the viewport information with Java after sending the above // notifications, so that Java can take these into account in its response. if (metrics) { // Calculate the absolute display port to send to Java nsIntRect displayPort = metrics->mDisplayPort; nsIntPoint scrollOffset = metrics->mViewportScrollOffset; displayPort.x += scrollOffset.x; displayPort.y += scrollOffset.y; SyncViewportInfo(displayPort, 1/rootScaleX, mLayersUpdated, mScrollOffset, mXScale, mYScale); mLayersUpdated = false; } // Handle transformations for asynchronous panning and zooming. We determine the // zoom used by Gecko from the transformation set on the root layer, and we // determine the scroll offset used by Gecko from the frame metrics of the // primary scrollable layer. We compare this to the desired zoom and scroll // offset in the view transform we obtained from Java in order to compute the // transformation we need to apply. if (metrics) { float tempScaleDiffX = rootScaleX * mXScale; float tempScaleDiffY = rootScaleY * mYScale; nsIntPoint metricsScrollOffset(0, 0); if (metrics->IsScrollable()) metricsScrollOffset = metrics->mViewportScrollOffset; nsIntPoint scrollCompensation( (mScrollOffset.x / tempScaleDiffX - metricsScrollOffset.x) * mXScale, (mScrollOffset.y / tempScaleDiffY - metricsScrollOffset.y) * mYScale); ViewTransform treeTransform(-scrollCompensation, mXScale, mYScale); shadow->SetShadowTransform(gfx3DMatrix(treeTransform) * currentTransform); } else { ViewTransform treeTransform(nsIntPoint(0,0), mXScale, mYScale); shadow->SetShadowTransform(gfx3DMatrix(treeTransform) * currentTransform); } }