|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "ClientTiledThebesLayer.h" |
|
6 #include "FrameMetrics.h" // for FrameMetrics |
|
7 #include "Units.h" // for ScreenIntRect, CSSPoint, etc |
|
8 #include "UnitTransforms.h" // for TransformTo |
|
9 #include "ClientLayerManager.h" // for ClientLayerManager, etc |
|
10 #include "gfx3DMatrix.h" // for gfx3DMatrix |
|
11 #include "gfxPlatform.h" // for gfxPlatform |
|
12 #include "gfxPrefs.h" // for gfxPrefs |
|
13 #include "gfxRect.h" // for gfxRect |
|
14 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc |
|
15 #include "mozilla/gfx/BaseSize.h" // for BaseSize |
|
16 #include "mozilla/gfx/Rect.h" // for Rect, RectTyped |
|
17 #include "mozilla/layers/LayersMessages.h" |
|
18 #include "mozilla/mozalloc.h" // for operator delete, etc |
|
19 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc |
|
20 #include "nsRect.h" // for nsIntRect |
|
21 |
|
22 namespace mozilla { |
|
23 namespace layers { |
|
24 |
|
25 |
|
26 ClientTiledThebesLayer::ClientTiledThebesLayer(ClientLayerManager* const aManager) |
|
27 : ThebesLayer(aManager, |
|
28 static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST())) |
|
29 , mContentClient() |
|
30 { |
|
31 MOZ_COUNT_CTOR(ClientTiledThebesLayer); |
|
32 mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0); |
|
33 mPaintData.mFirstPaint = true; |
|
34 } |
|
35 |
|
36 ClientTiledThebesLayer::~ClientTiledThebesLayer() |
|
37 { |
|
38 MOZ_COUNT_DTOR(ClientTiledThebesLayer); |
|
39 } |
|
40 |
|
41 void |
|
42 ClientTiledThebesLayer::ClearCachedResources() |
|
43 { |
|
44 if (mContentClient) { |
|
45 mContentClient->ClearCachedResources(); |
|
46 } |
|
47 } |
|
48 |
|
49 void |
|
50 ClientTiledThebesLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) |
|
51 { |
|
52 aAttrs = ThebesLayerAttributes(GetValidRegion()); |
|
53 } |
|
54 |
|
55 static LayoutDeviceRect |
|
56 ApplyParentLayerToLayoutTransform(const gfx3DMatrix& aTransform, const ParentLayerRect& aParentLayerRect) |
|
57 { |
|
58 return TransformTo<LayoutDevicePixel>(aTransform, aParentLayerRect); |
|
59 } |
|
60 |
|
61 void |
|
62 ClientTiledThebesLayer::BeginPaint() |
|
63 { |
|
64 if (ClientManager()->IsRepeatTransaction()) { |
|
65 return; |
|
66 } |
|
67 |
|
68 mPaintData.mLowPrecisionPaintCount = 0; |
|
69 mPaintData.mPaintFinished = false; |
|
70 mPaintData.mCompositionBounds.SetEmpty(); |
|
71 mPaintData.mCriticalDisplayPort.SetEmpty(); |
|
72 |
|
73 if (!GetBaseTransform().Is2DIntegerTranslation()) { |
|
74 // Give up if the layer is transformed. The code below assumes that there |
|
75 // is no transform set, and not making that assumption would cause huge |
|
76 // complication to handle a quite rare case. |
|
77 // |
|
78 // FIXME The intention is to bail out of this function when there's a CSS |
|
79 // transform set on the layer, but unfortunately there's no way to |
|
80 // distinguish transforms due to scrolling from transforms due to |
|
81 // CSS transforms. |
|
82 // |
|
83 // Because of this, there may be unintended behaviour when setting |
|
84 // 2d CSS translations on the children of scrollable displayport |
|
85 // layers. |
|
86 return; |
|
87 } |
|
88 |
|
89 #ifdef MOZ_WIDGET_ANDROID |
|
90 // Subframes on Fennec are not async scrollable because they have no displayport. |
|
91 // However, the code in RenderLayer() picks up a displayport from the nearest |
|
92 // scrollable ancestor container layer anyway, which is incorrect for Fennec. This |
|
93 // behaviour results in the subframe getting clipped improperly and perma-blank areas |
|
94 // while scrolling the subframe. To work around this, we detect if this layer is |
|
95 // the primary scrollable layer and disable the tiling behaviour if it is not. |
|
96 bool isPrimaryScrollableThebesLayer = false; |
|
97 if (Layer* scrollable = ClientManager()->GetPrimaryScrollableLayer()) { |
|
98 if (GetParent() == scrollable) { |
|
99 for (Layer* child = scrollable->GetFirstChild(); child; child = child->GetNextSibling()) { |
|
100 if (child->GetType() == Layer::TYPE_THEBES) { |
|
101 if (child == this) { |
|
102 // |this| is the first thebes layer child of the GetPrimaryScrollableLayer() |
|
103 isPrimaryScrollableThebesLayer = true; |
|
104 } |
|
105 break; |
|
106 } |
|
107 } |
|
108 } |
|
109 } |
|
110 if (!isPrimaryScrollableThebesLayer) { |
|
111 return; |
|
112 } |
|
113 #endif |
|
114 |
|
115 // Get the metrics of the nearest scrollable layer and the nearest layer |
|
116 // with a displayport. |
|
117 ContainerLayer* displayPortParent = nullptr; |
|
118 ContainerLayer* scrollParent = nullptr; |
|
119 for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { |
|
120 const FrameMetrics& metrics = parent->GetFrameMetrics(); |
|
121 if (!scrollParent && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) { |
|
122 scrollParent = parent; |
|
123 } |
|
124 if (!metrics.mDisplayPort.IsEmpty()) { |
|
125 displayPortParent = parent; |
|
126 // Any layer that has a displayport must be scrollable, so we can break |
|
127 // here. |
|
128 break; |
|
129 } |
|
130 } |
|
131 |
|
132 if (!displayPortParent || !scrollParent) { |
|
133 // No displayport or scroll parent, so we can't do progressive rendering. |
|
134 // Just set the composition bounds to empty and return. |
|
135 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G) |
|
136 // Both Android and b2g are guaranteed to have a displayport set, so this |
|
137 // should never happen. |
|
138 NS_WARNING("Tiled Thebes layer with no scrollable container parent"); |
|
139 #endif |
|
140 return; |
|
141 } |
|
142 |
|
143 // Note, not handling transformed layers lets us assume that LayoutDevice |
|
144 // space of the scroll parent layer is the same as LayoutDevice space of |
|
145 // this layer. |
|
146 const FrameMetrics& scrollMetrics = scrollParent->GetFrameMetrics(); |
|
147 const FrameMetrics& displayportMetrics = displayPortParent->GetFrameMetrics(); |
|
148 |
|
149 // Calculate the transform required to convert ParentLayer space of our |
|
150 // display port parent to LayoutDevice space of this layer. |
|
151 gfx::Matrix4x4 transform = scrollParent->GetTransform(); |
|
152 ContainerLayer* displayPortParentParent = displayPortParent->GetParent() ? |
|
153 displayPortParent->GetParent()->GetParent() : nullptr; |
|
154 for (ContainerLayer* parent = scrollParent->GetParent(); |
|
155 parent != displayPortParentParent; |
|
156 parent = parent->GetParent()) { |
|
157 transform = transform * parent->GetTransform(); |
|
158 } |
|
159 gfx3DMatrix layoutDeviceToScrollParentLayer; |
|
160 gfx::To3DMatrix(transform, layoutDeviceToScrollParentLayer); |
|
161 layoutDeviceToScrollParentLayer.ScalePost(scrollMetrics.mCumulativeResolution.scale, |
|
162 scrollMetrics.mCumulativeResolution.scale, |
|
163 1.f); |
|
164 |
|
165 mPaintData.mTransformParentLayerToLayoutDevice = layoutDeviceToScrollParentLayer.Inverse(); |
|
166 |
|
167 // Compute the critical display port of the display port layer in |
|
168 // LayoutDevice space of this layer. |
|
169 ParentLayerRect criticalDisplayPort = |
|
170 (displayportMetrics.mCriticalDisplayPort + displayportMetrics.GetScrollOffset()) * |
|
171 displayportMetrics.GetZoomToParent(); |
|
172 mPaintData.mCriticalDisplayPort = LayoutDeviceIntRect::ToUntyped(RoundedOut( |
|
173 ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayoutDevice, |
|
174 criticalDisplayPort))); |
|
175 |
|
176 // Compute the viewport of the display port layer in LayoutDevice space of |
|
177 // this layer. |
|
178 ParentLayerRect viewport = |
|
179 (displayportMetrics.mViewport + displayportMetrics.GetScrollOffset()) * |
|
180 displayportMetrics.GetZoomToParent(); |
|
181 mPaintData.mViewport = ApplyParentLayerToLayoutTransform( |
|
182 mPaintData.mTransformParentLayerToLayoutDevice, viewport); |
|
183 |
|
184 // Store the scroll parent resolution. Because this is Gecko-side, before any |
|
185 // async transforms have occurred, we can use the zoom for this. |
|
186 mPaintData.mResolution = displayportMetrics.GetZoomToParent(); |
|
187 |
|
188 // Store the parent composition bounds in LayoutDevice units. |
|
189 // This is actually in LayoutDevice units of the scrollParent's parent layer, |
|
190 // but because there is no transform, we can assume that these are the same. |
|
191 mPaintData.mCompositionBounds = |
|
192 scrollMetrics.mCompositionBounds / scrollMetrics.GetParentResolution(); |
|
193 |
|
194 // Calculate the scroll offset since the last transaction |
|
195 mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoomToParent(); |
|
196 } |
|
197 |
|
198 void |
|
199 ClientTiledThebesLayer::EndPaint(bool aFinish) |
|
200 { |
|
201 if (!aFinish && !mPaintData.mPaintFinished) { |
|
202 return; |
|
203 } |
|
204 |
|
205 mPaintData.mLastScrollOffset = mPaintData.mScrollOffset; |
|
206 mPaintData.mPaintFinished = true; |
|
207 mPaintData.mFirstPaint = false; |
|
208 } |
|
209 |
|
210 void |
|
211 ClientTiledThebesLayer::RenderLayer() |
|
212 { |
|
213 LayerManager::DrawThebesLayerCallback callback = |
|
214 ClientManager()->GetThebesLayerCallback(); |
|
215 void *data = ClientManager()->GetThebesLayerCallbackData(); |
|
216 if (!callback) { |
|
217 ClientManager()->SetTransactionIncomplete(); |
|
218 return; |
|
219 } |
|
220 |
|
221 if (!mContentClient) { |
|
222 mContentClient = new TiledContentClient(this, ClientManager()); |
|
223 |
|
224 mContentClient->Connect(); |
|
225 ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); |
|
226 MOZ_ASSERT(mContentClient->GetForwarder()); |
|
227 } |
|
228 |
|
229 if (mContentClient->mTiledBuffer.HasFormatChanged()) { |
|
230 mValidRegion = nsIntRegion(); |
|
231 } |
|
232 |
|
233 nsIntRegion invalidRegion = mVisibleRegion; |
|
234 invalidRegion.Sub(invalidRegion, mValidRegion); |
|
235 if (invalidRegion.IsEmpty()) { |
|
236 EndPaint(true); |
|
237 return; |
|
238 } |
|
239 |
|
240 // Only paint the mask layer on the first transaction. |
|
241 if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) { |
|
242 ToClientLayer(GetMaskLayer())->RenderLayer(); |
|
243 } |
|
244 |
|
245 bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition(); |
|
246 |
|
247 // Fast path for no progressive updates, no low-precision updates and no |
|
248 // critical display-port set, or no display-port set, or this is a fixed |
|
249 // position layer/contained in a fixed position layer |
|
250 const FrameMetrics& parentMetrics = GetParent()->GetFrameMetrics(); |
|
251 if ((!gfxPrefs::UseProgressiveTilePainting() && |
|
252 !gfxPrefs::UseLowPrecisionBuffer() && |
|
253 parentMetrics.mCriticalDisplayPort.IsEmpty()) || |
|
254 parentMetrics.mDisplayPort.IsEmpty() || |
|
255 isFixed) { |
|
256 mValidRegion = mVisibleRegion; |
|
257 |
|
258 NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer"); |
|
259 |
|
260 mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, |
|
261 callback, data); |
|
262 |
|
263 ClientManager()->Hold(this); |
|
264 mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); |
|
265 |
|
266 return; |
|
267 } |
|
268 |
|
269 // Calculate everything we need to perform the paint. |
|
270 BeginPaint(); |
|
271 if (mPaintData.mPaintFinished) { |
|
272 return; |
|
273 } |
|
274 |
|
275 // Make sure that tiles that fall outside of the visible region are |
|
276 // discarded on the first update. |
|
277 if (!ClientManager()->IsRepeatTransaction()) { |
|
278 mValidRegion.And(mValidRegion, mVisibleRegion); |
|
279 if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { |
|
280 // Make sure that tiles that fall outside of the critical displayport are |
|
281 // discarded on the first update. |
|
282 mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort); |
|
283 } |
|
284 } |
|
285 |
|
286 nsIntRegion lowPrecisionInvalidRegion; |
|
287 if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { |
|
288 if (gfxPrefs::UseLowPrecisionBuffer()) { |
|
289 // Calculate the invalid region for the low precision buffer |
|
290 lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion); |
|
291 |
|
292 // Remove the valid region from the low precision valid region (we don't |
|
293 // validate this part of the low precision buffer). |
|
294 lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); |
|
295 } |
|
296 |
|
297 // Clip the invalid region to the critical display-port |
|
298 invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort); |
|
299 if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) { |
|
300 EndPaint(true); |
|
301 return; |
|
302 } |
|
303 } |
|
304 |
|
305 if (!invalidRegion.IsEmpty() && mPaintData.mLowPrecisionPaintCount == 0) { |
|
306 bool updatedBuffer = false; |
|
307 // Only draw progressively when the resolution is unchanged. |
|
308 if (gfxPrefs::UseProgressiveTilePainting() && |
|
309 !ClientManager()->HasShadowTarget() && |
|
310 mContentClient->mTiledBuffer.GetFrameResolution() == mPaintData.mResolution) { |
|
311 // Store the old valid region, then clear it before painting. |
|
312 // We clip the old valid region to the visible region, as it only gets |
|
313 // used to decide stale content (currently valid and previously visible) |
|
314 nsIntRegion oldValidRegion = mContentClient->mTiledBuffer.GetValidRegion(); |
|
315 oldValidRegion.And(oldValidRegion, mVisibleRegion); |
|
316 if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { |
|
317 oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort); |
|
318 } |
|
319 |
|
320 updatedBuffer = |
|
321 mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, invalidRegion, |
|
322 oldValidRegion, &mPaintData, |
|
323 callback, data); |
|
324 } else { |
|
325 updatedBuffer = true; |
|
326 mValidRegion = mVisibleRegion; |
|
327 if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { |
|
328 mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort); |
|
329 } |
|
330 mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution); |
|
331 mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, |
|
332 callback, data); |
|
333 } |
|
334 |
|
335 if (updatedBuffer) { |
|
336 ClientManager()->Hold(this); |
|
337 mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); |
|
338 |
|
339 // If there are low precision updates, mark the paint as unfinished and |
|
340 // request a repeat transaction. |
|
341 if (!lowPrecisionInvalidRegion.IsEmpty() && mPaintData.mPaintFinished) { |
|
342 ClientManager()->SetRepeatTransaction(); |
|
343 mPaintData.mLowPrecisionPaintCount = 1; |
|
344 mPaintData.mPaintFinished = false; |
|
345 } |
|
346 |
|
347 // Return so that low precision updates aren't performed in the same |
|
348 // transaction as high-precision updates. |
|
349 EndPaint(false); |
|
350 return; |
|
351 } |
|
352 } |
|
353 |
|
354 // Render the low precision buffer, if there's area to invalidate and the |
|
355 // visible region is larger than the critical display port. |
|
356 bool updatedLowPrecision = false; |
|
357 if (!lowPrecisionInvalidRegion.IsEmpty() && |
|
358 !nsIntRegion(mPaintData.mCriticalDisplayPort).Contains(mVisibleRegion)) { |
|
359 nsIntRegion oldValidRegion = |
|
360 mContentClient->mLowPrecisionTiledBuffer.GetValidRegion(); |
|
361 oldValidRegion.And(oldValidRegion, mVisibleRegion); |
|
362 |
|
363 // If the frame resolution or format have changed, invalidate the buffer |
|
364 if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution || |
|
365 mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) { |
|
366 if (!mLowPrecisionValidRegion.IsEmpty()) { |
|
367 updatedLowPrecision = true; |
|
368 } |
|
369 oldValidRegion.SetEmpty(); |
|
370 mLowPrecisionValidRegion.SetEmpty(); |
|
371 mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution); |
|
372 lowPrecisionInvalidRegion = mVisibleRegion; |
|
373 } |
|
374 |
|
375 // Invalidate previously valid content that is no longer visible |
|
376 if (mPaintData.mLowPrecisionPaintCount == 1) { |
|
377 mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, mVisibleRegion); |
|
378 } |
|
379 mPaintData.mLowPrecisionPaintCount++; |
|
380 |
|
381 // Remove the valid high-precision region from the invalid low-precision |
|
382 // region. We don't want to spend time drawing things twice. |
|
383 lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); |
|
384 |
|
385 if (!lowPrecisionInvalidRegion.IsEmpty()) { |
|
386 updatedLowPrecision = mContentClient->mLowPrecisionTiledBuffer |
|
387 .ProgressiveUpdate(mLowPrecisionValidRegion, |
|
388 lowPrecisionInvalidRegion, |
|
389 oldValidRegion, &mPaintData, |
|
390 callback, data); |
|
391 } |
|
392 } else if (!mLowPrecisionValidRegion.IsEmpty()) { |
|
393 // Clear the low precision tiled buffer |
|
394 updatedLowPrecision = true; |
|
395 mLowPrecisionValidRegion.SetEmpty(); |
|
396 mContentClient->mLowPrecisionTiledBuffer.PaintThebes(mLowPrecisionValidRegion, |
|
397 mLowPrecisionValidRegion, |
|
398 callback, data); |
|
399 } |
|
400 |
|
401 // We send a Painted callback if we clear the valid region of the low |
|
402 // precision buffer, so that the shadow buffer's valid region can be updated |
|
403 // and the associated resources can be freed. |
|
404 if (updatedLowPrecision) { |
|
405 ClientManager()->Hold(this); |
|
406 mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER); |
|
407 } |
|
408 |
|
409 EndPaint(false); |
|
410 } |
|
411 |
|
412 } // mozilla |
|
413 } // layers |