1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/layers/client/ClientTiledThebesLayer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,413 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "ClientTiledThebesLayer.h" 1.9 +#include "FrameMetrics.h" // for FrameMetrics 1.10 +#include "Units.h" // for ScreenIntRect, CSSPoint, etc 1.11 +#include "UnitTransforms.h" // for TransformTo 1.12 +#include "ClientLayerManager.h" // for ClientLayerManager, etc 1.13 +#include "gfx3DMatrix.h" // for gfx3DMatrix 1.14 +#include "gfxPlatform.h" // for gfxPlatform 1.15 +#include "gfxPrefs.h" // for gfxPrefs 1.16 +#include "gfxRect.h" // for gfxRect 1.17 +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc 1.18 +#include "mozilla/gfx/BaseSize.h" // for BaseSize 1.19 +#include "mozilla/gfx/Rect.h" // for Rect, RectTyped 1.20 +#include "mozilla/layers/LayersMessages.h" 1.21 +#include "mozilla/mozalloc.h" // for operator delete, etc 1.22 +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc 1.23 +#include "nsRect.h" // for nsIntRect 1.24 + 1.25 +namespace mozilla { 1.26 +namespace layers { 1.27 + 1.28 + 1.29 +ClientTiledThebesLayer::ClientTiledThebesLayer(ClientLayerManager* const aManager) 1.30 + : ThebesLayer(aManager, 1.31 + static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST())) 1.32 + , mContentClient() 1.33 +{ 1.34 + MOZ_COUNT_CTOR(ClientTiledThebesLayer); 1.35 + mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0); 1.36 + mPaintData.mFirstPaint = true; 1.37 +} 1.38 + 1.39 +ClientTiledThebesLayer::~ClientTiledThebesLayer() 1.40 +{ 1.41 + MOZ_COUNT_DTOR(ClientTiledThebesLayer); 1.42 +} 1.43 + 1.44 +void 1.45 +ClientTiledThebesLayer::ClearCachedResources() 1.46 +{ 1.47 + if (mContentClient) { 1.48 + mContentClient->ClearCachedResources(); 1.49 + } 1.50 +} 1.51 + 1.52 +void 1.53 +ClientTiledThebesLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) 1.54 +{ 1.55 + aAttrs = ThebesLayerAttributes(GetValidRegion()); 1.56 +} 1.57 + 1.58 +static LayoutDeviceRect 1.59 +ApplyParentLayerToLayoutTransform(const gfx3DMatrix& aTransform, const ParentLayerRect& aParentLayerRect) 1.60 +{ 1.61 + return TransformTo<LayoutDevicePixel>(aTransform, aParentLayerRect); 1.62 +} 1.63 + 1.64 +void 1.65 +ClientTiledThebesLayer::BeginPaint() 1.66 +{ 1.67 + if (ClientManager()->IsRepeatTransaction()) { 1.68 + return; 1.69 + } 1.70 + 1.71 + mPaintData.mLowPrecisionPaintCount = 0; 1.72 + mPaintData.mPaintFinished = false; 1.73 + mPaintData.mCompositionBounds.SetEmpty(); 1.74 + mPaintData.mCriticalDisplayPort.SetEmpty(); 1.75 + 1.76 + if (!GetBaseTransform().Is2DIntegerTranslation()) { 1.77 + // Give up if the layer is transformed. The code below assumes that there 1.78 + // is no transform set, and not making that assumption would cause huge 1.79 + // complication to handle a quite rare case. 1.80 + // 1.81 + // FIXME The intention is to bail out of this function when there's a CSS 1.82 + // transform set on the layer, but unfortunately there's no way to 1.83 + // distinguish transforms due to scrolling from transforms due to 1.84 + // CSS transforms. 1.85 + // 1.86 + // Because of this, there may be unintended behaviour when setting 1.87 + // 2d CSS translations on the children of scrollable displayport 1.88 + // layers. 1.89 + return; 1.90 + } 1.91 + 1.92 +#ifdef MOZ_WIDGET_ANDROID 1.93 + // Subframes on Fennec are not async scrollable because they have no displayport. 1.94 + // However, the code in RenderLayer() picks up a displayport from the nearest 1.95 + // scrollable ancestor container layer anyway, which is incorrect for Fennec. This 1.96 + // behaviour results in the subframe getting clipped improperly and perma-blank areas 1.97 + // while scrolling the subframe. To work around this, we detect if this layer is 1.98 + // the primary scrollable layer and disable the tiling behaviour if it is not. 1.99 + bool isPrimaryScrollableThebesLayer = false; 1.100 + if (Layer* scrollable = ClientManager()->GetPrimaryScrollableLayer()) { 1.101 + if (GetParent() == scrollable) { 1.102 + for (Layer* child = scrollable->GetFirstChild(); child; child = child->GetNextSibling()) { 1.103 + if (child->GetType() == Layer::TYPE_THEBES) { 1.104 + if (child == this) { 1.105 + // |this| is the first thebes layer child of the GetPrimaryScrollableLayer() 1.106 + isPrimaryScrollableThebesLayer = true; 1.107 + } 1.108 + break; 1.109 + } 1.110 + } 1.111 + } 1.112 + } 1.113 + if (!isPrimaryScrollableThebesLayer) { 1.114 + return; 1.115 + } 1.116 +#endif 1.117 + 1.118 + // Get the metrics of the nearest scrollable layer and the nearest layer 1.119 + // with a displayport. 1.120 + ContainerLayer* displayPortParent = nullptr; 1.121 + ContainerLayer* scrollParent = nullptr; 1.122 + for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { 1.123 + const FrameMetrics& metrics = parent->GetFrameMetrics(); 1.124 + if (!scrollParent && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) { 1.125 + scrollParent = parent; 1.126 + } 1.127 + if (!metrics.mDisplayPort.IsEmpty()) { 1.128 + displayPortParent = parent; 1.129 + // Any layer that has a displayport must be scrollable, so we can break 1.130 + // here. 1.131 + break; 1.132 + } 1.133 + } 1.134 + 1.135 + if (!displayPortParent || !scrollParent) { 1.136 + // No displayport or scroll parent, so we can't do progressive rendering. 1.137 + // Just set the composition bounds to empty and return. 1.138 +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G) 1.139 + // Both Android and b2g are guaranteed to have a displayport set, so this 1.140 + // should never happen. 1.141 + NS_WARNING("Tiled Thebes layer with no scrollable container parent"); 1.142 +#endif 1.143 + return; 1.144 + } 1.145 + 1.146 + // Note, not handling transformed layers lets us assume that LayoutDevice 1.147 + // space of the scroll parent layer is the same as LayoutDevice space of 1.148 + // this layer. 1.149 + const FrameMetrics& scrollMetrics = scrollParent->GetFrameMetrics(); 1.150 + const FrameMetrics& displayportMetrics = displayPortParent->GetFrameMetrics(); 1.151 + 1.152 + // Calculate the transform required to convert ParentLayer space of our 1.153 + // display port parent to LayoutDevice space of this layer. 1.154 + gfx::Matrix4x4 transform = scrollParent->GetTransform(); 1.155 + ContainerLayer* displayPortParentParent = displayPortParent->GetParent() ? 1.156 + displayPortParent->GetParent()->GetParent() : nullptr; 1.157 + for (ContainerLayer* parent = scrollParent->GetParent(); 1.158 + parent != displayPortParentParent; 1.159 + parent = parent->GetParent()) { 1.160 + transform = transform * parent->GetTransform(); 1.161 + } 1.162 + gfx3DMatrix layoutDeviceToScrollParentLayer; 1.163 + gfx::To3DMatrix(transform, layoutDeviceToScrollParentLayer); 1.164 + layoutDeviceToScrollParentLayer.ScalePost(scrollMetrics.mCumulativeResolution.scale, 1.165 + scrollMetrics.mCumulativeResolution.scale, 1.166 + 1.f); 1.167 + 1.168 + mPaintData.mTransformParentLayerToLayoutDevice = layoutDeviceToScrollParentLayer.Inverse(); 1.169 + 1.170 + // Compute the critical display port of the display port layer in 1.171 + // LayoutDevice space of this layer. 1.172 + ParentLayerRect criticalDisplayPort = 1.173 + (displayportMetrics.mCriticalDisplayPort + displayportMetrics.GetScrollOffset()) * 1.174 + displayportMetrics.GetZoomToParent(); 1.175 + mPaintData.mCriticalDisplayPort = LayoutDeviceIntRect::ToUntyped(RoundedOut( 1.176 + ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayoutDevice, 1.177 + criticalDisplayPort))); 1.178 + 1.179 + // Compute the viewport of the display port layer in LayoutDevice space of 1.180 + // this layer. 1.181 + ParentLayerRect viewport = 1.182 + (displayportMetrics.mViewport + displayportMetrics.GetScrollOffset()) * 1.183 + displayportMetrics.GetZoomToParent(); 1.184 + mPaintData.mViewport = ApplyParentLayerToLayoutTransform( 1.185 + mPaintData.mTransformParentLayerToLayoutDevice, viewport); 1.186 + 1.187 + // Store the scroll parent resolution. Because this is Gecko-side, before any 1.188 + // async transforms have occurred, we can use the zoom for this. 1.189 + mPaintData.mResolution = displayportMetrics.GetZoomToParent(); 1.190 + 1.191 + // Store the parent composition bounds in LayoutDevice units. 1.192 + // This is actually in LayoutDevice units of the scrollParent's parent layer, 1.193 + // but because there is no transform, we can assume that these are the same. 1.194 + mPaintData.mCompositionBounds = 1.195 + scrollMetrics.mCompositionBounds / scrollMetrics.GetParentResolution(); 1.196 + 1.197 + // Calculate the scroll offset since the last transaction 1.198 + mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoomToParent(); 1.199 +} 1.200 + 1.201 +void 1.202 +ClientTiledThebesLayer::EndPaint(bool aFinish) 1.203 +{ 1.204 + if (!aFinish && !mPaintData.mPaintFinished) { 1.205 + return; 1.206 + } 1.207 + 1.208 + mPaintData.mLastScrollOffset = mPaintData.mScrollOffset; 1.209 + mPaintData.mPaintFinished = true; 1.210 + mPaintData.mFirstPaint = false; 1.211 +} 1.212 + 1.213 +void 1.214 +ClientTiledThebesLayer::RenderLayer() 1.215 +{ 1.216 + LayerManager::DrawThebesLayerCallback callback = 1.217 + ClientManager()->GetThebesLayerCallback(); 1.218 + void *data = ClientManager()->GetThebesLayerCallbackData(); 1.219 + if (!callback) { 1.220 + ClientManager()->SetTransactionIncomplete(); 1.221 + return; 1.222 + } 1.223 + 1.224 + if (!mContentClient) { 1.225 + mContentClient = new TiledContentClient(this, ClientManager()); 1.226 + 1.227 + mContentClient->Connect(); 1.228 + ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); 1.229 + MOZ_ASSERT(mContentClient->GetForwarder()); 1.230 + } 1.231 + 1.232 + if (mContentClient->mTiledBuffer.HasFormatChanged()) { 1.233 + mValidRegion = nsIntRegion(); 1.234 + } 1.235 + 1.236 + nsIntRegion invalidRegion = mVisibleRegion; 1.237 + invalidRegion.Sub(invalidRegion, mValidRegion); 1.238 + if (invalidRegion.IsEmpty()) { 1.239 + EndPaint(true); 1.240 + return; 1.241 + } 1.242 + 1.243 + // Only paint the mask layer on the first transaction. 1.244 + if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) { 1.245 + ToClientLayer(GetMaskLayer())->RenderLayer(); 1.246 + } 1.247 + 1.248 + bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition(); 1.249 + 1.250 + // Fast path for no progressive updates, no low-precision updates and no 1.251 + // critical display-port set, or no display-port set, or this is a fixed 1.252 + // position layer/contained in a fixed position layer 1.253 + const FrameMetrics& parentMetrics = GetParent()->GetFrameMetrics(); 1.254 + if ((!gfxPrefs::UseProgressiveTilePainting() && 1.255 + !gfxPrefs::UseLowPrecisionBuffer() && 1.256 + parentMetrics.mCriticalDisplayPort.IsEmpty()) || 1.257 + parentMetrics.mDisplayPort.IsEmpty() || 1.258 + isFixed) { 1.259 + mValidRegion = mVisibleRegion; 1.260 + 1.261 + NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer"); 1.262 + 1.263 + mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, 1.264 + callback, data); 1.265 + 1.266 + ClientManager()->Hold(this); 1.267 + mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); 1.268 + 1.269 + return; 1.270 + } 1.271 + 1.272 + // Calculate everything we need to perform the paint. 1.273 + BeginPaint(); 1.274 + if (mPaintData.mPaintFinished) { 1.275 + return; 1.276 + } 1.277 + 1.278 + // Make sure that tiles that fall outside of the visible region are 1.279 + // discarded on the first update. 1.280 + if (!ClientManager()->IsRepeatTransaction()) { 1.281 + mValidRegion.And(mValidRegion, mVisibleRegion); 1.282 + if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { 1.283 + // Make sure that tiles that fall outside of the critical displayport are 1.284 + // discarded on the first update. 1.285 + mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort); 1.286 + } 1.287 + } 1.288 + 1.289 + nsIntRegion lowPrecisionInvalidRegion; 1.290 + if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { 1.291 + if (gfxPrefs::UseLowPrecisionBuffer()) { 1.292 + // Calculate the invalid region for the low precision buffer 1.293 + lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion); 1.294 + 1.295 + // Remove the valid region from the low precision valid region (we don't 1.296 + // validate this part of the low precision buffer). 1.297 + lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); 1.298 + } 1.299 + 1.300 + // Clip the invalid region to the critical display-port 1.301 + invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort); 1.302 + if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) { 1.303 + EndPaint(true); 1.304 + return; 1.305 + } 1.306 + } 1.307 + 1.308 + if (!invalidRegion.IsEmpty() && mPaintData.mLowPrecisionPaintCount == 0) { 1.309 + bool updatedBuffer = false; 1.310 + // Only draw progressively when the resolution is unchanged. 1.311 + if (gfxPrefs::UseProgressiveTilePainting() && 1.312 + !ClientManager()->HasShadowTarget() && 1.313 + mContentClient->mTiledBuffer.GetFrameResolution() == mPaintData.mResolution) { 1.314 + // Store the old valid region, then clear it before painting. 1.315 + // We clip the old valid region to the visible region, as it only gets 1.316 + // used to decide stale content (currently valid and previously visible) 1.317 + nsIntRegion oldValidRegion = mContentClient->mTiledBuffer.GetValidRegion(); 1.318 + oldValidRegion.And(oldValidRegion, mVisibleRegion); 1.319 + if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { 1.320 + oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort); 1.321 + } 1.322 + 1.323 + updatedBuffer = 1.324 + mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, invalidRegion, 1.325 + oldValidRegion, &mPaintData, 1.326 + callback, data); 1.327 + } else { 1.328 + updatedBuffer = true; 1.329 + mValidRegion = mVisibleRegion; 1.330 + if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { 1.331 + mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort); 1.332 + } 1.333 + mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution); 1.334 + mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, 1.335 + callback, data); 1.336 + } 1.337 + 1.338 + if (updatedBuffer) { 1.339 + ClientManager()->Hold(this); 1.340 + mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); 1.341 + 1.342 + // If there are low precision updates, mark the paint as unfinished and 1.343 + // request a repeat transaction. 1.344 + if (!lowPrecisionInvalidRegion.IsEmpty() && mPaintData.mPaintFinished) { 1.345 + ClientManager()->SetRepeatTransaction(); 1.346 + mPaintData.mLowPrecisionPaintCount = 1; 1.347 + mPaintData.mPaintFinished = false; 1.348 + } 1.349 + 1.350 + // Return so that low precision updates aren't performed in the same 1.351 + // transaction as high-precision updates. 1.352 + EndPaint(false); 1.353 + return; 1.354 + } 1.355 + } 1.356 + 1.357 + // Render the low precision buffer, if there's area to invalidate and the 1.358 + // visible region is larger than the critical display port. 1.359 + bool updatedLowPrecision = false; 1.360 + if (!lowPrecisionInvalidRegion.IsEmpty() && 1.361 + !nsIntRegion(mPaintData.mCriticalDisplayPort).Contains(mVisibleRegion)) { 1.362 + nsIntRegion oldValidRegion = 1.363 + mContentClient->mLowPrecisionTiledBuffer.GetValidRegion(); 1.364 + oldValidRegion.And(oldValidRegion, mVisibleRegion); 1.365 + 1.366 + // If the frame resolution or format have changed, invalidate the buffer 1.367 + if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution || 1.368 + mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) { 1.369 + if (!mLowPrecisionValidRegion.IsEmpty()) { 1.370 + updatedLowPrecision = true; 1.371 + } 1.372 + oldValidRegion.SetEmpty(); 1.373 + mLowPrecisionValidRegion.SetEmpty(); 1.374 + mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution); 1.375 + lowPrecisionInvalidRegion = mVisibleRegion; 1.376 + } 1.377 + 1.378 + // Invalidate previously valid content that is no longer visible 1.379 + if (mPaintData.mLowPrecisionPaintCount == 1) { 1.380 + mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, mVisibleRegion); 1.381 + } 1.382 + mPaintData.mLowPrecisionPaintCount++; 1.383 + 1.384 + // Remove the valid high-precision region from the invalid low-precision 1.385 + // region. We don't want to spend time drawing things twice. 1.386 + lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); 1.387 + 1.388 + if (!lowPrecisionInvalidRegion.IsEmpty()) { 1.389 + updatedLowPrecision = mContentClient->mLowPrecisionTiledBuffer 1.390 + .ProgressiveUpdate(mLowPrecisionValidRegion, 1.391 + lowPrecisionInvalidRegion, 1.392 + oldValidRegion, &mPaintData, 1.393 + callback, data); 1.394 + } 1.395 + } else if (!mLowPrecisionValidRegion.IsEmpty()) { 1.396 + // Clear the low precision tiled buffer 1.397 + updatedLowPrecision = true; 1.398 + mLowPrecisionValidRegion.SetEmpty(); 1.399 + mContentClient->mLowPrecisionTiledBuffer.PaintThebes(mLowPrecisionValidRegion, 1.400 + mLowPrecisionValidRegion, 1.401 + callback, data); 1.402 + } 1.403 + 1.404 + // We send a Painted callback if we clear the valid region of the low 1.405 + // precision buffer, so that the shadow buffer's valid region can be updated 1.406 + // and the associated resources can be freed. 1.407 + if (updatedLowPrecision) { 1.408 + ClientManager()->Hold(this); 1.409 + mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER); 1.410 + } 1.411 + 1.412 + EndPaint(false); 1.413 +} 1.414 + 1.415 +} // mozilla 1.416 +} // layers