diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/client/ClientTiledPaintedLayer.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/client/ClientTiledPaintedLayer.cpp')
-rw-r--r-- | gfx/layers/client/ClientTiledPaintedLayer.cpp | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/gfx/layers/client/ClientTiledPaintedLayer.cpp b/gfx/layers/client/ClientTiledPaintedLayer.cpp new file mode 100644 index 0000000000..1e93c51f72 --- /dev/null +++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp @@ -0,0 +1,653 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ClientTiledPaintedLayer.h" +#include "FrameMetrics.h" // for FrameMetrics +#include "Units.h" // for ScreenIntRect, CSSPoint, etc +#include "UnitTransforms.h" // for TransformTo +#include "ClientLayerManager.h" // for ClientLayerManager, etc +#include "gfxPlatform.h" // for gfxPlatform +#include "gfxRect.h" // for gfxRect +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/gfx/BaseSize.h" // for BaseSize +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/Rect.h" // for Rect, RectTyped +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper +#include "mozilla/layers/LayersMessages.h" +#include "mozilla/layers/PaintThread.h" +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "mozilla/layers/MultiTiledContentClient.h" +#include "mozilla/layers/SingleTiledContentClient.h" + +namespace mozilla { +namespace layers { + +using gfx::IntRect; +using gfx::IntSize; +using gfx::Rect; + +ClientTiledPaintedLayer::ClientTiledPaintedLayer( + ClientLayerManager* const aManager, + ClientLayerManager::PaintedLayerCreationHint aCreationHint) + : PaintedLayer(aManager, static_cast<ClientLayer*>(this), aCreationHint), + mContentClient(), + mHaveSingleTiledContentClient(false) { + MOZ_COUNT_CTOR(ClientTiledPaintedLayer); + mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0); + mPaintData.mFirstPaint = true; +} + +ClientTiledPaintedLayer::~ClientTiledPaintedLayer() { + MOZ_COUNT_DTOR(ClientTiledPaintedLayer); +} + +void ClientTiledPaintedLayer::ClearCachedResources() { + if (mContentClient) { + mContentClient->ClearCachedResources(); + } + ClearValidRegion(); + mContentClient = nullptr; +} + +void ClientTiledPaintedLayer::FillSpecificAttributes( + SpecificLayerAttributes& aAttrs) { + aAttrs = PaintedLayerAttributes(GetValidRegion()); +} + +static Maybe<LayerRect> ApplyParentLayerToLayerTransform( + const ParentLayerToLayerMatrix4x4& aTransform, + const ParentLayerRect& aParentLayerRect, const LayerRect& aClip) { + return UntransformBy(aTransform, aParentLayerRect, aClip); +} + +static LayerToParentLayerMatrix4x4 GetTransformToAncestorsParentLayer( + Layer* aStart, const LayerMetricsWrapper& aAncestor) { + // If the ancestor layer Combines3DTransformWithAncestors, then the + // scroll offset is contained in the transform of the layer at the + // root of the 3D context. So we must first find that layer, then + // calcuate the transform to its parent. + LayerMetricsWrapper root3dAncestor = aAncestor; + while (root3dAncestor.Combines3DTransformWithAncestors()) { + root3dAncestor = root3dAncestor.GetParent(); + } + + gfx::Matrix4x4 transform; + const LayerMetricsWrapper& ancestorParent = root3dAncestor.GetParent(); + for (LayerMetricsWrapper iter(aStart, LayerMetricsWrapper::StartAt::BOTTOM); + ancestorParent ? iter != ancestorParent : iter.IsValid(); + iter = iter.GetParent()) { + transform = transform * iter.GetTransform(); + } + return ViewAs<LayerToParentLayerMatrix4x4>(transform); +} + +void ClientTiledPaintedLayer::GetAncestorLayers( + LayerMetricsWrapper* aOutScrollAncestor, + LayerMetricsWrapper* aOutDisplayPortAncestor, + bool* aOutHasTransformAnimation) { + LayerMetricsWrapper scrollAncestor; + LayerMetricsWrapper displayPortAncestor; + bool hasTransformAnimation = false; + for (LayerMetricsWrapper ancestor(this, LayerMetricsWrapper::StartAt::BOTTOM); + ancestor; ancestor = ancestor.GetParent()) { + hasTransformAnimation |= ancestor.HasTransformAnimation(); + const FrameMetrics& metrics = ancestor.Metrics(); + if (!scrollAncestor && + metrics.GetScrollId() != ScrollableLayerGuid::NULL_SCROLL_ID) { + scrollAncestor = ancestor; + } + if (!metrics.GetDisplayPort().IsEmpty()) { + displayPortAncestor = ancestor; + // Any layer that has a displayport must be scrollable, so we can break + // here. + break; + } + } + if (aOutScrollAncestor) { + *aOutScrollAncestor = scrollAncestor; + } + if (aOutDisplayPortAncestor) { + *aOutDisplayPortAncestor = displayPortAncestor; + } + if (aOutHasTransformAnimation) { + *aOutHasTransformAnimation = hasTransformAnimation; + } +} + +void ClientTiledPaintedLayer::BeginPaint() { + mPaintData.ResetPaintData(); + + 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. + LayerMetricsWrapper scrollAncestor; + LayerMetricsWrapper displayPortAncestor; + bool hasTransformAnimation; + GetAncestorLayers(&scrollAncestor, &displayPortAncestor, + &hasTransformAnimation); + + if (!displayPortAncestor || !scrollAncestor) { + // No displayport or scroll ancestor, so we can't do progressive rendering. +#if defined(MOZ_WIDGET_ANDROID) + // Android are guaranteed to have a displayport set, so this + // should never happen. + NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor"); +#endif + return; + } + + TILING_LOG( + "TILING %p: Found scrollAncestor %p, displayPortAncestor %p, transform " + "%d\n", + this, scrollAncestor.GetLayer(), displayPortAncestor.GetLayer(), + hasTransformAnimation); + + const FrameMetrics& scrollMetrics = scrollAncestor.Metrics(); + const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics(); + + // Calculate the transform required to convert ParentLayer space of our + // display port ancestor to the Layer space of this layer. + ParentLayerToLayerMatrix4x4 transformDisplayPortToLayer = + GetTransformToAncestorsParentLayer(this, displayPortAncestor).Inverse(); + + LayerRect layerBounds(GetVisibleRegion().GetBounds()); + + // Compute the critical display port that applies to this layer in the + // LayoutDevice space of this layer, but only if there is no OMT animation + // on this layer. If there is an OMT animation then we need to draw the whole + // visible region of this layer as determined by layout, because we don't know + // what parts of it might move into view in the compositor. + mPaintData.mHasTransformAnimation = hasTransformAnimation; + if (!mPaintData.mHasTransformAnimation && + mContentClient->GetLowPrecisionTiledBuffer()) { + ParentLayerRect criticalDisplayPort = + (displayportMetrics.GetCriticalDisplayPort() * + displayportMetrics.GetZoom()) + + displayportMetrics.GetCompositionBounds().TopLeft(); + Maybe<LayerRect> criticalDisplayPortTransformed = + ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, + criticalDisplayPort, layerBounds); + if (criticalDisplayPortTransformed) { + mPaintData.mCriticalDisplayPort = + Some(RoundedToInt(*criticalDisplayPortTransformed)); + } else { + mPaintData.mCriticalDisplayPort = Some(LayerIntRect(0, 0, 0, 0)); + } + } + TILING_LOG("TILING %p: Critical displayport %s\n", this, + mPaintData.mCriticalDisplayPort + ? Stringify(*mPaintData.mCriticalDisplayPort).c_str() + : "not set"); + + // 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.GetZoom(); + TILING_LOG("TILING %p: Resolution %s\n", this, + Stringify(mPaintData.mResolution).c_str()); + + // Store the applicable composition bounds in this layer's Layer units. + mPaintData.mTransformToCompBounds = + GetTransformToAncestorsParentLayer(this, scrollAncestor); + ParentLayerToLayerMatrix4x4 transformToBounds = + mPaintData.mTransformToCompBounds.Inverse(); + Maybe<LayerRect> compositionBoundsTransformed = + ApplyParentLayerToLayerTransform( + transformToBounds, scrollMetrics.GetCompositionBounds(), layerBounds); + if (compositionBoundsTransformed) { + mPaintData.mCompositionBounds = *compositionBoundsTransformed; + } else { + mPaintData.mCompositionBounds.SetEmpty(); + } + TILING_LOG("TILING %p: Composition bounds %s\n", this, + Stringify(mPaintData.mCompositionBounds).c_str()); + + // Calculate the scroll offset since the last transaction + mPaintData.mScrollOffset = + displayportMetrics.GetLayoutScrollOffset() * displayportMetrics.GetZoom(); + TILING_LOG("TILING %p: Scroll offset %s\n", this, + Stringify(mPaintData.mScrollOffset).c_str()); +} + +bool ClientTiledPaintedLayer::IsScrollingOnCompositor( + const FrameMetrics& aParentMetrics) { + CompositorBridgeChild* compositor = nullptr; + if (Manager() && Manager()->AsClientLayerManager()) { + compositor = Manager()->AsClientLayerManager()->GetCompositorBridgeChild(); + } + + if (!compositor) { + return false; + } + + FrameMetrics compositorMetrics; + if (!compositor->LookupCompositorFrameMetrics(aParentMetrics.GetScrollId(), + compositorMetrics)) { + return false; + } + + // 1 is a tad high for a fuzzy equals epsilon however if our scroll delta + // is so small then we have nothing to gain from using paint heuristics. + float COORDINATE_EPSILON = 1.f; + + return !FuzzyEqualsAdditive(compositorMetrics.GetVisualScrollOffset().x, + aParentMetrics.GetVisualScrollOffset().x, + COORDINATE_EPSILON) || + !FuzzyEqualsAdditive(compositorMetrics.GetVisualScrollOffset().y, + aParentMetrics.GetVisualScrollOffset().y, + COORDINATE_EPSILON); +} + +bool ClientTiledPaintedLayer::UseProgressiveDraw() { + if (!StaticPrefs::layers_progressive_paint()) { + // pref is disabled, so never do progressive + return false; + } + + if (!mContentClient->GetTiledBuffer()->SupportsProgressiveUpdate()) { + return false; + } + + if (ClientManager()->HasShadowTarget()) { + // This condition is true when we are in a reftest scenario. We don't want + // to draw progressively here because it can cause intermittent reftest + // failures because the harness won't wait for all the tiles to be drawn. + return false; + } + + if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) { + // This layer is fixed-position and so even if it does have a scrolling + // ancestor it will likely be entirely on-screen all the time, so we + // should draw it all at once + return false; + } + + if (mPaintData.mHasTransformAnimation) { + // The compositor is going to animate this somehow, so we want it all + // on the screen at once. + return false; + } + + if (ClientManager()->AsyncPanZoomEnabled()) { + LayerMetricsWrapper scrollAncestor; + GetAncestorLayers(&scrollAncestor, nullptr, nullptr); + MOZ_ASSERT( + scrollAncestor); // because mPaintData.mCriticalDisplayPort is set + if (!scrollAncestor) { + return false; + } + const FrameMetrics& parentMetrics = scrollAncestor.Metrics(); + if (!IsScrollingOnCompositor(parentMetrics)) { + return false; + } + } + + return true; +} + +bool ClientTiledPaintedLayer::RenderHighPrecision( + const nsIntRegion& aInvalidRegion, const nsIntRegion& aVisibleRegion, + LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) { + // If we have started drawing low-precision already, then we + // shouldn't do anything there. + if (mPaintData.mLowPrecisionPaintCount != 0) { + return false; + } + + // Only draw progressively when there is something to paint and the + // resolution is unchanged + if (!aInvalidRegion.IsEmpty() && UseProgressiveDraw() && + mContentClient->GetTiledBuffer()->GetFrameResolution() == + mPaintData.mResolution) { + // Store the old valid region, then clear it before painting. + // We clip the old valid region to the visible region, as it only gets + // used to decide stale content (currently valid and previously visible) + nsIntRegion oldValidRegion = + mContentClient->GetTiledBuffer()->GetValidRegion(); + oldValidRegion.And(oldValidRegion, aVisibleRegion); + if (mPaintData.mCriticalDisplayPort) { + oldValidRegion.And(oldValidRegion, + mPaintData.mCriticalDisplayPort->ToUnknownRect()); + } + + TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, + Stringify(oldValidRegion).c_str()); + + nsIntRegion drawnRegion; + bool updatedBuffer = mContentClient->GetTiledBuffer()->ProgressiveUpdate( + GetValidRegion(), aInvalidRegion, oldValidRegion, drawnRegion, + &mPaintData, aCallback, aCallbackData); + AddToValidRegion(drawnRegion); + return updatedBuffer; + } + + // Otherwise do a non-progressive paint. We must do this even when + // the region to paint is empty as the valid region may have shrunk. + + nsIntRegion validRegion = aVisibleRegion; + if (mPaintData.mCriticalDisplayPort) { + validRegion.AndWith(mPaintData.mCriticalDisplayPort->ToUnknownRect()); + } + SetValidRegion(validRegion); + + TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, + Stringify(aInvalidRegion).c_str()); + TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, + Stringify(GetValidRegion()).c_str()); + + TilePaintFlags flags = + PaintThread::Get() ? TilePaintFlags::Async : TilePaintFlags::None; + + mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution); + mContentClient->GetTiledBuffer()->PaintThebes( + GetValidRegion(), aInvalidRegion, aInvalidRegion, aCallback, + aCallbackData, flags); + mPaintData.mPaintFinished = true; + return true; +} + +bool ClientTiledPaintedLayer::RenderLowPrecision( + const nsIntRegion& aInvalidRegion, const nsIntRegion& aVisibleRegion, + LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) { + nsIntRegion invalidRegion = aInvalidRegion; + + // Render the low precision buffer, if the visible region is larger than the + // critical display port. + if (!mPaintData.mCriticalDisplayPort || + !nsIntRegion(mPaintData.mCriticalDisplayPort->ToUnknownRect()) + .Contains(aVisibleRegion)) { + nsIntRegion oldValidRegion = + mContentClient->GetLowPrecisionTiledBuffer()->GetValidRegion(); + oldValidRegion.And(oldValidRegion, aVisibleRegion); + + bool updatedBuffer = false; + + // If the frame resolution or format have changed, invalidate the buffer + if (mContentClient->GetLowPrecisionTiledBuffer()->GetFrameResolution() != + mPaintData.mResolution || + mContentClient->GetLowPrecisionTiledBuffer()->HasFormatChanged()) { + if (!mLowPrecisionValidRegion.IsEmpty()) { + updatedBuffer = true; + } + oldValidRegion.SetEmpty(); + mLowPrecisionValidRegion.SetEmpty(); + mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState(); + mContentClient->GetLowPrecisionTiledBuffer()->SetFrameResolution( + mPaintData.mResolution); + invalidRegion = aVisibleRegion; + } + + // Invalidate previously valid content that is no longer visible + if (mPaintData.mLowPrecisionPaintCount == 1) { + mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, aVisibleRegion); + } + mPaintData.mLowPrecisionPaintCount++; + + // Remove the valid high-precision region from the invalid low-precision + // region. We don't want to spend time drawing things twice. + invalidRegion.SubOut(GetValidRegion()); + + TILING_LOG( + "TILING %p: Progressive paint: low-precision invalid region is %s\n", + this, Stringify(invalidRegion).c_str()); + TILING_LOG( + "TILING %p: Progressive paint: low-precision old valid region is %s\n", + this, Stringify(oldValidRegion).c_str()); + + if (!invalidRegion.IsEmpty()) { + nsIntRegion drawnRegion; + updatedBuffer = + mContentClient->GetLowPrecisionTiledBuffer()->ProgressiveUpdate( + mLowPrecisionValidRegion, invalidRegion, oldValidRegion, + drawnRegion, &mPaintData, aCallback, aCallbackData); + mLowPrecisionValidRegion.OrWith(drawnRegion); + } + + TILING_LOG( + "TILING %p: Progressive paint: low-precision new valid region is %s\n", + this, Stringify(mLowPrecisionValidRegion).c_str()); + return updatedBuffer; + } + if (!mLowPrecisionValidRegion.IsEmpty()) { + TILING_LOG("TILING %p: Clearing low-precision buffer\n", this); + // Clear the low precision tiled buffer. + mLowPrecisionValidRegion.SetEmpty(); + mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState(); + // Return true here so we send a Painted callback after clearing the valid + // region of the low precision buffer. This allows the shadow buffer's valid + // region to be updated and the associated resources to be freed. + return true; + } + return false; +} + +void ClientTiledPaintedLayer::EndPaint() { + mPaintData.mLastScrollOffset = mPaintData.mScrollOffset; + mPaintData.mPaintFinished = true; + mPaintData.mFirstPaint = false; + TILING_LOG("TILING %p: Paint finished\n", this); +} + +void ClientTiledPaintedLayer::RenderLayer() { + if (!ClientManager()->IsRepeatTransaction()) { + // Only paint the mask layers on the first transaction. + RenderMaskLayers(this); + } + + LayerManager::DrawPaintedLayerCallback callback = + ClientManager()->GetPaintedLayerCallback(); + void* data = ClientManager()->GetPaintedLayerCallbackData(); + + IntSize layerSize = mVisibleRegion.GetBounds().ToUnknownRect().Size(); + IntSize tileSize = gfx::gfxVars::TileSize(); + bool isHalfTileWidthOrHeight = layerSize.width <= tileSize.width / 2 || + layerSize.height <= tileSize.height / 2; + + // Use single tile when layer is not scrollable, is smaller than one + // tile, or when more than half of the tiles' pixels in either + // dimension would be wasted. + bool wantSingleTiledContentClient = + (mCreationHint == LayerManager::NONE || layerSize <= tileSize || + isHalfTileWidthOrHeight) && + SingleTiledContentClient::ClientSupportsLayerSize(layerSize, + ClientManager()) && + StaticPrefs::layers_single_tile_enabled(); + + if (mContentClient && mHaveSingleTiledContentClient && + !wantSingleTiledContentClient) { + mContentClient = nullptr; + ClearValidRegion(); + } + + if (!mContentClient) { + if (wantSingleTiledContentClient) { + mContentClient = new SingleTiledContentClient(*this, ClientManager()); + mHaveSingleTiledContentClient = true; + } else { + mContentClient = new MultiTiledContentClient(*this, ClientManager()); + mHaveSingleTiledContentClient = false; + } + + mContentClient->Connect(); + ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); + MOZ_ASSERT(mContentClient->GetForwarder()); + } + + if (mContentClient->GetTiledBuffer()->HasFormatChanged()) { + ClearValidRegion(); + mContentClient->GetTiledBuffer()->ResetPaintedAndValidState(); + } + + TILING_LOG("TILING %p: Initial visible region %s\n", this, + Stringify(mVisibleRegion).c_str()); + TILING_LOG("TILING %p: Initial valid region %s\n", this, + Stringify(GetValidRegion()).c_str()); + TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, + Stringify(mLowPrecisionValidRegion).c_str()); + + nsIntRegion neededRegion = mVisibleRegion.ToUnknownRegion(); +#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE + // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for + // mobile + if (MayResample()) { + // If we're resampling then bilinear filtering can read up to 1 pixel + // outside of our texture coords. Make the visible region a single rect, + // and pad it out by 1 pixel (restricted to tile boundaries) so that + // we always have valid content or transparent pixels to sample from. + IntRect bounds = neededRegion.GetBounds(); + IntRect wholeTiles = bounds; + wholeTiles.InflateToMultiple(gfx::gfxVars::TileSize()); + IntRect padded = bounds; + padded.Inflate(1); + padded.IntersectRect(padded, wholeTiles); + neededRegion = padded; + } +#endif + + nsIntRegion invalidRegion; + invalidRegion.Sub(neededRegion, GetValidRegion()); + if (invalidRegion.IsEmpty()) { + EndPaint(); + return; + } + + if (!callback) { + ClientManager()->SetTransactionIncomplete(); + return; + } + + if (!ClientManager()->IsRepeatTransaction()) { + // For more complex cases we need to calculate a bunch of metrics before we + // can do the paint. + BeginPaint(); + if (mPaintData.mPaintFinished) { + return; + } + + // Make sure that tiles that fall outside of the visible region or outside + // of the critical displayport are discarded on the first update. Also make + // sure that we only draw stuff inside the critical displayport on the first + // update. + nsIntRegion validRegion; + validRegion.And(GetValidRegion(), neededRegion); + if (mPaintData.mCriticalDisplayPort) { + validRegion.AndWith(mPaintData.mCriticalDisplayPort->ToUnknownRect()); + invalidRegion.And(invalidRegion, + mPaintData.mCriticalDisplayPort->ToUnknownRect()); + } + SetValidRegion(validRegion); + + TILING_LOG("TILING %p: First-transaction valid region %s\n", this, + Stringify(validRegion).c_str()); + TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, + Stringify(invalidRegion).c_str()); + } else { + if (mPaintData.mCriticalDisplayPort) { + invalidRegion.And(invalidRegion, + mPaintData.mCriticalDisplayPort->ToUnknownRect()); + } + TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, + Stringify(invalidRegion).c_str()); + } + + nsIntRegion lowPrecisionInvalidRegion; + if (mContentClient->GetLowPrecisionTiledBuffer()) { + // Calculate the invalid region for the low precision buffer. Make sure + // to remove the valid high-precision area so we don't double-paint it. + lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion); + lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, GetValidRegion()); + } + TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, + Stringify(lowPrecisionInvalidRegion).c_str()); + + bool updatedHighPrecision = + RenderHighPrecision(invalidRegion, neededRegion, callback, data); + if (updatedHighPrecision) { + ClientManager()->Hold(this); + mContentClient->UpdatedBuffer(TiledContentClient::TILED_BUFFER); + + if (!mPaintData.mPaintFinished) { + // There is still more high-res stuff to paint, so we're not + // done yet. A subsequent transaction will take care of this. + ClientManager()->SetRepeatTransaction(); + return; + } + } + + // If there is nothing to draw in low-precision, then we're done. + if (lowPrecisionInvalidRegion.IsEmpty()) { + EndPaint(); + return; + } + + if (updatedHighPrecision) { + // If there are low precision updates, but we just did some high-precision + // updates, then mark the paint as unfinished and request a repeat + // transaction. This is so that we don't perform low-precision updates in + // the same transaction as high-precision updates. + TILING_LOG( + "TILING %p: Scheduling repeat transaction for low-precision painting\n", + this); + ClientManager()->SetRepeatTransaction(); + mPaintData.mLowPrecisionPaintCount = 1; + mPaintData.mPaintFinished = false; + return; + } + + bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion, + neededRegion, callback, data); + if (updatedLowPrecision) { + ClientManager()->Hold(this); + mContentClient->UpdatedBuffer( + TiledContentClient::LOW_PRECISION_TILED_BUFFER); + + if (!mPaintData.mPaintFinished) { + // There is still more low-res stuff to paint, so we're not + // done yet. A subsequent transaction will take care of this. + ClientManager()->SetRepeatTransaction(); + return; + } + } + + // If we get here, we've done all the high- and low-precision + // paints we wanted to do, so we can finish the paint and chill. + EndPaint(); +} + +bool ClientTiledPaintedLayer::IsOptimizedFor( + LayerManager::PaintedLayerCreationHint aHint) { + // The only creation hint is whether the layer is scrollable or not, and this + // is only respected on OSX, where it's used to determine whether to + // use a tiled content client or not. + // There are pretty nasty performance consequences for not using tiles on + // large, scrollable layers, so we want the layer to be recreated in this + // situation. + return aHint == GetCreationHint(); +} + +void ClientTiledPaintedLayer::PrintInfo(std::stringstream& aStream, + const char* aPrefix) { + PaintedLayer::PrintInfo(aStream, aPrefix); + if (mContentClient) { + aStream << "\n"; + nsAutoCString pfx(aPrefix); + pfx += " "; + mContentClient->PrintInfo(aStream, pfx.get()); + } +} + +} // namespace layers +} // namespace mozilla |