summaryrefslogtreecommitdiffstats
path: root/gfx/layers/client/ClientTiledPaintedLayer.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/client/ClientTiledPaintedLayer.cpp
parentInitial commit. (diff)
downloadfirefox-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.cpp653
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