зеркало из https://github.com/mozilla/gecko-dev.git
655 строки
25 KiB
C++
655 строки
25 KiB
C++
/* -*- 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 "LayersLogging.h"
|
|
#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.GetScrollOffset() * 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.GetScrollOffset().x,
|
|
aParentMetrics.GetScrollOffset().x,
|
|
COORDINATE_EPSILON) ||
|
|
!FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().y,
|
|
aParentMetrics.GetScrollOffset().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
|