void LayerManagerComposite::InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const IntRect& aBounds) { bool drawFps = gfxPrefs::LayersDrawFPS(); bool drawFrameCounter = gfxPrefs::DrawFrameCounter(); bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars(); bool drawPaintTimes = gfxPrefs::AlwaysPaint(); if (drawFps || drawFrameCounter) { aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 256, 256)); } if (drawFrameColorBars) { aInvalidRegion.Or(aInvalidRegion, nsIntRect(0, 0, 10, aBounds.height)); } if (drawPaintTimes) { aInvalidRegion.Or(aInvalidRegion, nsIntRect(PaintCounter::GetPaintRect())); } }
static inline void AddWrappedRegion(const nsIntRegion& aInput, nsIntRegion& aOutput, const nsIntSize& aSize, const nsIntPoint& aShift) { nsIntRegion tempRegion; tempRegion.And(nsIntRect(aShift, aSize), aInput); tempRegion.MoveBy(-aShift); aOutput.Or(aOutput, tempRegion); }
static void AddTransformedRegion(nsIntRegion& aDest, const nsIntRegion& aSource, const Matrix4x4& aTransform) { nsIntRegionRectIterator iter(aSource); const IntRect *r; while ((r = iter.Next())) { aDest.Or(aDest, TransformRect(*r, aTransform)); } aDest.SimplifyOutward(20); }
void LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion) { nsIntRegion localOpaque; Matrix transform2d; bool isTranslation = false; // If aLayer has a simple transform (only an integer translation) then we // can easily convert aOpaqueRegion into pre-transform coordinates and include // that region. if (aLayer->GetLocalTransform().Is2D(&transform2d)) { if (transform2d.IsIntegerTranslation()) { isTranslation = true; localOpaque = aOpaqueRegion; localOpaque.MoveBy(-transform2d._31, -transform2d._32); } } // Subtract any areas that we know to be opaque from our // visible region. LayerComposite *composite = aLayer->AsLayerComposite(); if (!localOpaque.IsEmpty()) { nsIntRegion visible = composite->GetShadowVisibleRegion(); visible.Sub(visible, localOpaque); composite->SetShadowVisibleRegion(visible); } // Compute occlusions for our descendants (in front-to-back order) and allow them to // contribute to localOpaque. for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { ApplyOcclusionCulling(child, localOpaque); } // If we have a simple transform, then we can add our opaque area into // aOpaqueRegion. if (isTranslation && !aLayer->HasMaskLayers() && aLayer->GetLocalOpacity() == 1.0f) { if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) { localOpaque.Or(localOpaque, composite->GetFullyRenderedRegion()); } localOpaque.MoveBy(transform2d._31, transform2d._32); const Maybe<ParentLayerIntRect>& clip = aLayer->GetEffectiveClipRect(); if (clip) { localOpaque.And(localOpaque, ParentLayerIntRect::ToUntyped(*clip)); } aOpaqueRegion.Or(aOpaqueRegion, localOpaque); } }
bool ClientTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion, nsIntRegion& aInvalidRegion, const nsIntRegion& aOldValidRegion, BasicTiledLayerPaintData* aPaintData, LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData) { bool repeat = false; bool isBufferChanged = false; do { // Compute the region that should be updated. Repeat as many times as // is required. nsIntRegion regionToPaint; repeat = ComputeProgressiveUpdateRegion(aInvalidRegion, aOldValidRegion, regionToPaint, aPaintData, repeat); // There's no further work to be done. if (regionToPaint.IsEmpty()) { break; } isBufferChanged = true; // Keep track of what we're about to refresh. aValidRegion.Or(aValidRegion, regionToPaint); // aValidRegion may have been altered by InvalidateRegion, but we still // want to display stale content until it gets progressively updated. // Create a region that includes stale content. nsIntRegion validOrStale; validOrStale.Or(aValidRegion, aOldValidRegion); // Paint the computed region and subtract it from the invalid region. PaintThebes(validOrStale, regionToPaint, aCallback, aCallbackData); aInvalidRegion.Sub(aInvalidRegion, regionToPaint); } while (repeat); // Return false if nothing has been drawn, or give what has been drawn // to the shadow layer to upload. return isBufferChanged; }
static void AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource) { aDest.Or(aDest, aSource); aDest.SimplifyOutward(20); }
bool ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOldValidRegion, nsIntRegion& aRegionToPaint, BasicTiledLayerPaintData* aPaintData, bool aIsRepeated) { aRegionToPaint = aInvalidRegion; // If the composition bounds rect is empty, we can't make any sensible // decision about how to update coherently. In this case, just update // everything in one transaction. if (aPaintData->mCompositionBounds.IsEmpty()) { aPaintData->mPaintFinished = true; return false; } // If this is a low precision buffer, we force progressive updates. The // assumption is that the contents is less important, so visual coherency // is lower priority than speed. bool drawingLowPrecision = IsLowPrecision(); // Find out if we have any non-stale content to update. nsIntRegion staleRegion; staleRegion.And(aInvalidRegion, aOldValidRegion); // Find out the current view transform to determine which tiles to draw // first, and see if we should just abort this paint. Aborting is usually // caused by there being an incoming, more relevant paint. ParentLayerRect compositionBounds; CSSToParentLayerScale zoom; #if defined(MOZ_WIDGET_ANDROID) bool abortPaint = mManager->ProgressiveUpdateCallback(!staleRegion.Contains(aInvalidRegion), compositionBounds, zoom, !drawingLowPrecision); #else MOZ_ASSERT(mSharedFrameMetricsHelper); ContainerLayer* parent = mThebesLayer->AsLayer()->GetParent(); bool abortPaint = mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics( parent, !staleRegion.Contains(aInvalidRegion), drawingLowPrecision, compositionBounds, zoom); #endif if (abortPaint) { // We ignore if front-end wants to abort if this is the first, // non-low-precision paint, as in that situation, we're about to override // front-end's page/viewport metrics. if (!aPaintData->mFirstPaint || drawingLowPrecision) { PROFILER_LABEL("ContentClient", "Abort painting"); aRegionToPaint.SetEmpty(); return aIsRepeated; } } // Transform the screen coordinates into transformed layout device coordinates. LayoutDeviceRect transformedCompositionBounds = TransformCompositionBounds(compositionBounds, zoom, aPaintData->mScrollOffset, aPaintData->mResolution, aPaintData->mTransformParentLayerToLayout); // Paint tiles that have stale content or that intersected with the screen // at the time of issuing the draw command in a single transaction first. // This is to avoid rendering glitches on animated page content, and when // layers change size/shape. LayoutDeviceRect coherentUpdateRect = transformedCompositionBounds.Intersect(aPaintData->mCompositionBounds); nsIntRect roundedCoherentUpdateRect = LayoutDeviceIntRect::ToUntyped(RoundedOut(coherentUpdateRect)); aRegionToPaint.And(aInvalidRegion, roundedCoherentUpdateRect); aRegionToPaint.Or(aRegionToPaint, staleRegion); bool drawingStale = !aRegionToPaint.IsEmpty(); if (!drawingStale) { aRegionToPaint = aInvalidRegion; } // Prioritise tiles that are currently visible on the screen. bool paintVisible = false; if (aRegionToPaint.Intersects(roundedCoherentUpdateRect)) { aRegionToPaint.And(aRegionToPaint, roundedCoherentUpdateRect); paintVisible = true; } // Paint area that's visible and overlaps previously valid content to avoid // visible glitches in animated elements, such as gifs. bool paintInSingleTransaction = paintVisible && (drawingStale || aPaintData->mFirstPaint); // The following code decides what order to draw tiles in, based on the // current scroll direction of the primary scrollable layer. NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!"); nsIntRect paintBounds = aRegionToPaint.GetBounds(); int startX, incX, startY, incY; int tileLength = GetScaledTileLength(); if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) { startX = RoundDownToTileEdge(paintBounds.x); incX = tileLength; } else { startX = RoundDownToTileEdge(paintBounds.XMost() - 1); incX = -tileLength; } if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) { startY = RoundDownToTileEdge(paintBounds.y); incY = tileLength; } else { startY = RoundDownToTileEdge(paintBounds.YMost() - 1); incY = -tileLength; } // Find a tile to draw. nsIntRect tileBounds(startX, startY, tileLength, tileLength); int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x; int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y; // This loop will always terminate, as there is at least one tile area // along the first/last row/column intersecting with regionToPaint, or its // bounds would have been smaller. while (true) { aRegionToPaint.And(aInvalidRegion, tileBounds); if (!aRegionToPaint.IsEmpty()) { break; } if (Abs(scrollDiffY) >= Abs(scrollDiffX)) { tileBounds.x += incX; } else { tileBounds.y += incY; } } if (!aRegionToPaint.Contains(aInvalidRegion)) { // The region needed to paint is larger then our progressive chunk size // therefore update what we want to paint and ask for a new paint transaction. // If we need to draw more than one tile to maintain coherency, make // sure it happens in the same transaction by requesting this work be // repeated immediately. // If this is unnecessary, the remaining work will be done tile-by-tile in // subsequent transactions. if (!drawingLowPrecision && paintInSingleTransaction) { return true; } mManager->SetRepeatTransaction(); return false; } // We're not repeating painting and we've not requested a repeat transaction, // so the paint is finished. If there's still a separate low precision // paint to do, it will get marked as unfinished later. aPaintData->mPaintFinished = true; return false; }