void TiledThebesLayerOGL::ProcessUploadQueue() { if (!mPendingUpload) return; // We should only be retaining old tiles if we're not fixed position. // Fixed position layers don't/shouldn't move on the screen, so retaining // tiles is not useful and often results in rendering artifacts. if (mReusableTileStore && mIsFixedPosition) { delete mReusableTileStore; mReusableTileStore = nullptr; } else if (gfxPlatform::UseReusableTileStore() && !mReusableTileStore && !mIsFixedPosition) { // XXX Add a pref for reusable tile store size mReusableTileStore = new ReusableTileStoreOGL(gl(), 1); } gfxSize resolution(1, 1); if (mReusableTileStore) { // 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. for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& metrics = parent->GetFrameMetrics(); resolution.width *= metrics.mResolution.width; resolution.height *= metrics.mResolution.height; } mReusableTileStore->HarvestTiles(this, &mVideoMemoryTiledBuffer, mVideoMemoryTiledBuffer.GetValidRegion(), mMainMemoryTiledBuffer.GetValidRegion(), mVideoMemoryTiledBuffer.GetFrameResolution(), resolution); } // If we coalesce uploads while the layers' valid region is changing we will // end up trying to upload area outside of the valid region. (bug 756555) mRegionToUpload.And(mRegionToUpload, mMainMemoryTiledBuffer.GetValidRegion()); mVideoMemoryTiledBuffer.Upload(&mMainMemoryTiledBuffer, mMainMemoryTiledBuffer.GetValidRegion(), mRegionToUpload, resolution); mValidRegion = mVideoMemoryTiledBuffer.GetValidRegion(); mMainMemoryTiledBuffer.ReadUnlock(); // Release all the tiles by replacing the tile buffer with an empty // tiled buffer. This will prevent us from doing a double unlock when // calling ~TiledThebesLayerOGL. // FIXME: This wont be needed when we do progressive upload and lock // tile by tile. mMainMemoryTiledBuffer = BasicTiledLayerBuffer(); mRegionToUpload = nsIntRegion(); mPendingUpload = false; }
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); }
float Layer::GetEffectiveOpacity() { float opacity = GetLocalOpacity(); for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface(); c = c->GetParent()) { opacity *= c->GetLocalOpacity(); } return opacity; }
CSSToScreenScale ThebesLayerComposite::GetEffectiveResolution() { for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& metrics = parent->GetFrameMetrics(); if (metrics.mScrollId != FrameMetrics::NULL_SCROLL_ID) { return metrics.mZoom; } } return CSSToScreenScale(1.0); }
CompositionOp Layer::GetEffectiveMixBlendMode() { if(mMixBlendMode != CompositionOp::OP_OVER) return mMixBlendMode; for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface(); c = c->GetParent()) { if(c->mMixBlendMode != CompositionOp::OP_OVER) return c->mMixBlendMode; } return mMixBlendMode; }
gfxContext::GraphicsOperator Layer::GetEffectiveMixBlendMode() { if(mMixBlendMode != gfxContext::OPERATOR_OVER) return mMixBlendMode; for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface(); c = c->GetParent()) { if(c->mMixBlendMode != gfxContext::OPERATOR_OVER) return c->mMixBlendMode; } return mMixBlendMode; }
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; }
gfxRect ThebesLayerComposite::GetDisplayPort() { // We use GetTransform instead of GetEffectiveTransform in this function // as we want the transform of the shadowable layers and not that of the // shadow layers, which may have been modified due to async scrolling/ // zooming. gfx3DMatrix transform = GetTransform(); // Find out the area of the nearest display-port to invalidate retained // tiles. gfxRect displayPort; gfxSize parentResolution = GetEffectiveResolution(); for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& metrics = parent->GetFrameMetrics(); if (displayPort.IsEmpty()) { if (!metrics.mDisplayPort.IsEmpty()) { // We use the bounds to cut down on complication/computation time. // This will be incorrect when the transform involves rotation, but // it'd be quite hard to retain invalid tiles correctly in this // situation anyway. displayPort = gfxRect(metrics.mDisplayPort.x, metrics.mDisplayPort.y, metrics.mDisplayPort.width, metrics.mDisplayPort.height); displayPort.ScaleRoundOut(parentResolution.width, parentResolution.height); } parentResolution.width /= metrics.mResolution.scale; parentResolution.height /= metrics.mResolution.scale; } if (parent->UseIntermediateSurface()) { transform.PreMultiply(parent->GetTransform()); } } // If no display port was found, use the widget size from the layer manager. if (displayPort.IsEmpty()) { LayerManagerComposite* manager = static_cast<LayerManagerComposite*>(Manager()); const nsIntSize& widgetSize = manager->GetWidgetSize(); displayPort.width = widgetSize.width; displayPort.height = widgetSize.height; } // Transform the display port into layer space. displayPort = transform.Inverse().TransformBounds(displayPort); return displayPort; }
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; }
void TiledThebesLayerOGL::ProcessUploadQueue() { if (mRegionToUpload.IsEmpty()) return; gfxSize resolution(1, 1); if (mReusableTileStore) { // 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. for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { const FrameMetrics& metrics = parent->GetFrameMetrics(); resolution.width *= metrics.mResolution.width; resolution.height *= metrics.mResolution.height; } mReusableTileStore->HarvestTiles(this, &mVideoMemoryTiledBuffer, mVideoMemoryTiledBuffer.GetValidRegion(), mMainMemoryTiledBuffer.GetValidRegion(), mVideoMemoryTiledBuffer.GetResolution(), resolution); } // If we coalesce uploads while the layers' valid region is changing we will // end up trying to upload area outside of the valid region. (bug 756555) mRegionToUpload.And(mRegionToUpload, mMainMemoryTiledBuffer.GetValidRegion()); mVideoMemoryTiledBuffer.Upload(&mMainMemoryTiledBuffer, mMainMemoryTiledBuffer.GetValidRegion(), mRegionToUpload, resolution); mValidRegion = mVideoMemoryTiledBuffer.GetValidRegion(); mMainMemoryTiledBuffer.ReadUnlock(); // Release all the tiles by replacing the tile buffer with an empty // tiled buffer. This will prevent us from doing a double unlock when // calling ~TiledThebesLayerOGL. // FIXME: This wont be needed when we do progressive upload and lock // tile by tile. mMainMemoryTiledBuffer = BasicTiledLayerBuffer(); mRegionToUpload = nsIntRegion(); }
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; }
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); }
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 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)); } }