/src/mozilla-central/gfx/layers/client/ClientTiledPaintedLayer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
5 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "ClientTiledPaintedLayer.h" |
8 | | #include "FrameMetrics.h" // for FrameMetrics |
9 | | #include "Units.h" // for ScreenIntRect, CSSPoint, etc |
10 | | #include "UnitTransforms.h" // for TransformTo |
11 | | #include "ClientLayerManager.h" // for ClientLayerManager, etc |
12 | | #include "gfxPlatform.h" // for gfxPlatform |
13 | | #include "gfxPrefs.h" // for gfxPrefs |
14 | | #include "gfxRect.h" // for gfxRect |
15 | | #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc |
16 | | #include "mozilla/gfx/BaseSize.h" // for BaseSize |
17 | | #include "mozilla/gfx/gfxVars.h" |
18 | | #include "mozilla/gfx/Rect.h" // for Rect, RectTyped |
19 | | #include "mozilla/layers/CompositorBridgeChild.h" |
20 | | #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper |
21 | | #include "mozilla/layers/LayersMessages.h" |
22 | | #include "mozilla/layers/PaintThread.h" |
23 | | #include "mozilla/mozalloc.h" // for operator delete, etc |
24 | | #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc |
25 | | #include "LayersLogging.h" |
26 | | #include "mozilla/layers/MultiTiledContentClient.h" |
27 | | #include "mozilla/layers/SingleTiledContentClient.h" |
28 | | |
29 | | namespace mozilla { |
30 | | namespace layers { |
31 | | |
32 | | using gfx::Rect; |
33 | | using gfx::IntRect; |
34 | | using gfx::IntSize; |
35 | | |
36 | | ClientTiledPaintedLayer::ClientTiledPaintedLayer(ClientLayerManager* const aManager, |
37 | | ClientLayerManager::PaintedLayerCreationHint aCreationHint) |
38 | | : PaintedLayer(aManager, static_cast<ClientLayer*>(this), aCreationHint) |
39 | | , mContentClient() |
40 | | , mHaveSingleTiledContentClient(false) |
41 | 0 | { |
42 | 0 | MOZ_COUNT_CTOR(ClientTiledPaintedLayer); |
43 | 0 | mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0); |
44 | 0 | mPaintData.mFirstPaint = true; |
45 | 0 | } |
46 | | |
47 | | ClientTiledPaintedLayer::~ClientTiledPaintedLayer() |
48 | 0 | { |
49 | 0 | MOZ_COUNT_DTOR(ClientTiledPaintedLayer); |
50 | 0 | } |
51 | | |
52 | | void |
53 | | ClientTiledPaintedLayer::ClearCachedResources() |
54 | 0 | { |
55 | 0 | if (mContentClient) { |
56 | 0 | mContentClient->ClearCachedResources(); |
57 | 0 | } |
58 | 0 | ClearValidRegion(); |
59 | 0 | mContentClient = nullptr; |
60 | 0 | } |
61 | | |
62 | | void |
63 | | ClientTiledPaintedLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) |
64 | 0 | { |
65 | 0 | aAttrs = PaintedLayerAttributes(GetValidRegion()); |
66 | 0 | } |
67 | | |
68 | | static Maybe<LayerRect> |
69 | | ApplyParentLayerToLayerTransform(const ParentLayerToLayerMatrix4x4& aTransform, |
70 | | const ParentLayerRect& aParentLayerRect, |
71 | | const LayerRect& aClip) |
72 | 0 | { |
73 | 0 | return UntransformBy(aTransform, aParentLayerRect, aClip); |
74 | 0 | } |
75 | | |
76 | | static LayerToParentLayerMatrix4x4 |
77 | | GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAncestor) |
78 | 0 | { |
79 | 0 | gfx::Matrix4x4 transform; |
80 | 0 | const LayerMetricsWrapper& ancestorParent = aAncestor.GetParent(); |
81 | 0 | for (LayerMetricsWrapper iter(aStart, LayerMetricsWrapper::StartAt::BOTTOM); |
82 | 0 | ancestorParent ? iter != ancestorParent : iter.IsValid(); |
83 | 0 | iter = iter.GetParent()) { |
84 | 0 | transform = transform * iter.GetTransform(); |
85 | 0 |
|
86 | 0 | if (gfxPrefs::LayoutUseContainersForRootFrames()) { |
87 | 0 | // When scrolling containers, layout adds a post-scale into the transform |
88 | 0 | // of the displayport-ancestor (which we pick up in GetTransform() above) |
89 | 0 | // to cancel out the pres shell resolution (for historical reasons). The |
90 | 0 | // compositor in turn cancels out this post-scale (i.e., scales by the |
91 | 0 | // pres shell resolution), and to get correct calculations, we need to do |
92 | 0 | // so here, too. |
93 | 0 | // |
94 | 0 | // With containerless scrolling, the offending post-scale is on the |
95 | 0 | // parent layer of the displayport-ancestor, which we don't reach in this |
96 | 0 | // loop, so we don't need to worry about it. |
97 | 0 | float presShellResolution = iter.GetPresShellResolution(); |
98 | 0 | transform.PostScale(presShellResolution, presShellResolution, 1.0f); |
99 | 0 | } |
100 | 0 | } |
101 | 0 | return ViewAs<LayerToParentLayerMatrix4x4>(transform); |
102 | 0 | } |
103 | | |
104 | | void |
105 | | ClientTiledPaintedLayer::GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor, |
106 | | LayerMetricsWrapper* aOutDisplayPortAncestor, |
107 | | bool* aOutHasTransformAnimation) |
108 | 0 | { |
109 | 0 | LayerMetricsWrapper scrollAncestor; |
110 | 0 | LayerMetricsWrapper displayPortAncestor; |
111 | 0 | bool hasTransformAnimation = false; |
112 | 0 | for (LayerMetricsWrapper ancestor(this, LayerMetricsWrapper::StartAt::BOTTOM); ancestor; ancestor = ancestor.GetParent()) { |
113 | 0 | hasTransformAnimation |= ancestor.HasTransformAnimation(); |
114 | 0 | const FrameMetrics& metrics = ancestor.Metrics(); |
115 | 0 | if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) { |
116 | 0 | scrollAncestor = ancestor; |
117 | 0 | } |
118 | 0 | if (!metrics.GetDisplayPort().IsEmpty()) { |
119 | 0 | displayPortAncestor = ancestor; |
120 | 0 | // Any layer that has a displayport must be scrollable, so we can break |
121 | 0 | // here. |
122 | 0 | break; |
123 | 0 | } |
124 | 0 | } |
125 | 0 | if (aOutScrollAncestor) { |
126 | 0 | *aOutScrollAncestor = scrollAncestor; |
127 | 0 | } |
128 | 0 | if (aOutDisplayPortAncestor) { |
129 | 0 | *aOutDisplayPortAncestor = displayPortAncestor; |
130 | 0 | } |
131 | 0 | if (aOutHasTransformAnimation) { |
132 | 0 | *aOutHasTransformAnimation = hasTransformAnimation; |
133 | 0 | } |
134 | 0 | } |
135 | | |
136 | | void |
137 | | ClientTiledPaintedLayer::BeginPaint() |
138 | 0 | { |
139 | 0 | mPaintData.ResetPaintData(); |
140 | 0 |
|
141 | 0 | if (!GetBaseTransform().Is2D()) { |
142 | 0 | // Give up if there is a complex CSS transform on the layer. We might |
143 | 0 | // eventually support these but for now it's too complicated to handle |
144 | 0 | // given that it's a pretty rare scenario. |
145 | 0 | return; |
146 | 0 | } |
147 | 0 | |
148 | 0 | // Get the metrics of the nearest scrollable layer and the nearest layer |
149 | 0 | // with a displayport. |
150 | 0 | LayerMetricsWrapper scrollAncestor; |
151 | 0 | LayerMetricsWrapper displayPortAncestor; |
152 | 0 | bool hasTransformAnimation; |
153 | 0 | GetAncestorLayers(&scrollAncestor, &displayPortAncestor, &hasTransformAnimation); |
154 | 0 |
|
155 | 0 | if (!displayPortAncestor || !scrollAncestor) { |
156 | 0 | // No displayport or scroll ancestor, so we can't do progressive rendering. |
157 | | #if defined(MOZ_WIDGET_ANDROID) |
158 | | // Android are guaranteed to have a displayport set, so this |
159 | | // should never happen. |
160 | | NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor"); |
161 | | #endif |
162 | | return; |
163 | 0 | } |
164 | 0 |
|
165 | 0 | TILING_LOG("TILING %p: Found scrollAncestor %p, displayPortAncestor %p, transform %d\n", this, |
166 | 0 | scrollAncestor.GetLayer(), displayPortAncestor.GetLayer(), hasTransformAnimation); |
167 | 0 |
|
168 | 0 | const FrameMetrics& scrollMetrics = scrollAncestor.Metrics(); |
169 | 0 | const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics(); |
170 | 0 |
|
171 | 0 | // Calculate the transform required to convert ParentLayer space of our |
172 | 0 | // display port ancestor to the Layer space of this layer. |
173 | 0 | ParentLayerToLayerMatrix4x4 transformDisplayPortToLayer = |
174 | 0 | GetTransformToAncestorsParentLayer(this, displayPortAncestor).Inverse(); |
175 | 0 |
|
176 | 0 | LayerRect layerBounds(GetVisibleRegion().GetBounds()); |
177 | 0 |
|
178 | 0 | // Compute the critical display port that applies to this layer in the |
179 | 0 | // LayoutDevice space of this layer, but only if there is no OMT animation |
180 | 0 | // on this layer. If there is an OMT animation then we need to draw the whole |
181 | 0 | // visible region of this layer as determined by layout, because we don't know |
182 | 0 | // what parts of it might move into view in the compositor. |
183 | 0 | mPaintData.mHasTransformAnimation = hasTransformAnimation; |
184 | 0 | if (!mPaintData.mHasTransformAnimation && |
185 | 0 | mContentClient->GetLowPrecisionTiledBuffer()) { |
186 | 0 | ParentLayerRect criticalDisplayPort = |
187 | 0 | (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom()) |
188 | 0 | + displayportMetrics.GetCompositionBounds().TopLeft(); |
189 | 0 | Maybe<LayerRect> criticalDisplayPortTransformed = |
190 | 0 | ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort, layerBounds); |
191 | 0 | if (criticalDisplayPortTransformed) { |
192 | 0 | mPaintData.mCriticalDisplayPort = Some(RoundedToInt(*criticalDisplayPortTransformed)); |
193 | 0 | } else { |
194 | 0 | mPaintData.mCriticalDisplayPort = Some(LayerIntRect(0, 0, 0, 0)); |
195 | 0 | } |
196 | 0 | } |
197 | 0 | TILING_LOG("TILING %p: Critical displayport %s\n", this, |
198 | 0 | mPaintData.mCriticalDisplayPort ? |
199 | 0 | Stringify(*mPaintData.mCriticalDisplayPort).c_str() : "not set"); |
200 | 0 |
|
201 | 0 | // Store the resolution from the displayport ancestor layer. Because this is Gecko-side, |
202 | 0 | // before any async transforms have occurred, we can use the zoom for this. |
203 | 0 | mPaintData.mResolution = displayportMetrics.GetZoom(); |
204 | 0 | TILING_LOG("TILING %p: Resolution %s\n", this, Stringify(mPaintData.mResolution).c_str()); |
205 | 0 |
|
206 | 0 | // Store the applicable composition bounds in this layer's Layer units. |
207 | 0 | mPaintData.mTransformToCompBounds = |
208 | 0 | GetTransformToAncestorsParentLayer(this, scrollAncestor); |
209 | 0 | ParentLayerToLayerMatrix4x4 transformToBounds = mPaintData.mTransformToCompBounds.Inverse(); |
210 | 0 | Maybe<LayerRect> compositionBoundsTransformed = ApplyParentLayerToLayerTransform( |
211 | 0 | transformToBounds, scrollMetrics.GetCompositionBounds(), layerBounds); |
212 | 0 | if (compositionBoundsTransformed) { |
213 | 0 | mPaintData.mCompositionBounds = *compositionBoundsTransformed; |
214 | 0 | } else { |
215 | 0 | mPaintData.mCompositionBounds.SetEmpty(); |
216 | 0 | } |
217 | 0 | TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str()); |
218 | 0 |
|
219 | 0 | // Calculate the scroll offset since the last transaction |
220 | 0 | mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom(); |
221 | 0 | TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str()); |
222 | 0 | } |
223 | | |
224 | | bool |
225 | | ClientTiledPaintedLayer::IsScrollingOnCompositor(const FrameMetrics& aParentMetrics) |
226 | 0 | { |
227 | 0 | CompositorBridgeChild* compositor = nullptr; |
228 | 0 | if (Manager() && Manager()->AsClientLayerManager()) { |
229 | 0 | compositor = Manager()->AsClientLayerManager()->GetCompositorBridgeChild(); |
230 | 0 | } |
231 | 0 |
|
232 | 0 | if (!compositor) { |
233 | 0 | return false; |
234 | 0 | } |
235 | 0 | |
236 | 0 | FrameMetrics compositorMetrics; |
237 | 0 | if (!compositor->LookupCompositorFrameMetrics(aParentMetrics.GetScrollId(), |
238 | 0 | compositorMetrics)) { |
239 | 0 | return false; |
240 | 0 | } |
241 | 0 | |
242 | 0 | // 1 is a tad high for a fuzzy equals epsilon however if our scroll delta |
243 | 0 | // is so small then we have nothing to gain from using paint heuristics. |
244 | 0 | float COORDINATE_EPSILON = 1.f; |
245 | 0 |
|
246 | 0 | return !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().x, |
247 | 0 | aParentMetrics.GetScrollOffset().x, |
248 | 0 | COORDINATE_EPSILON) || |
249 | 0 | !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().y, |
250 | 0 | aParentMetrics.GetScrollOffset().y, |
251 | 0 | COORDINATE_EPSILON); |
252 | 0 | } |
253 | | |
254 | | bool |
255 | 0 | ClientTiledPaintedLayer::UseProgressiveDraw() { |
256 | 0 | if (!gfxPrefs::ProgressivePaint()) { |
257 | 0 | // pref is disabled, so never do progressive |
258 | 0 | return false; |
259 | 0 | } |
260 | 0 | |
261 | 0 | if (!mContentClient->GetTiledBuffer()->SupportsProgressiveUpdate()) { |
262 | 0 | return false; |
263 | 0 | } |
264 | 0 | |
265 | 0 | if (ClientManager()->HasShadowTarget()) { |
266 | 0 | // This condition is true when we are in a reftest scenario. We don't want |
267 | 0 | // to draw progressively here because it can cause intermittent reftest |
268 | 0 | // failures because the harness won't wait for all the tiles to be drawn. |
269 | 0 | return false; |
270 | 0 | } |
271 | 0 | |
272 | 0 | if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) { |
273 | 0 | // This layer is fixed-position and so even if it does have a scrolling |
274 | 0 | // ancestor it will likely be entirely on-screen all the time, so we |
275 | 0 | // should draw it all at once |
276 | 0 | return false; |
277 | 0 | } |
278 | 0 | |
279 | 0 | if (mPaintData.mHasTransformAnimation) { |
280 | 0 | // The compositor is going to animate this somehow, so we want it all |
281 | 0 | // on the screen at once. |
282 | 0 | return false; |
283 | 0 | } |
284 | 0 | |
285 | 0 | if (ClientManager()->AsyncPanZoomEnabled()) { |
286 | 0 | LayerMetricsWrapper scrollAncestor; |
287 | 0 | GetAncestorLayers(&scrollAncestor, nullptr, nullptr); |
288 | 0 | MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is set |
289 | 0 | if (!scrollAncestor) { |
290 | 0 | return false; |
291 | 0 | } |
292 | 0 | const FrameMetrics& parentMetrics = scrollAncestor.Metrics(); |
293 | 0 | if (!IsScrollingOnCompositor(parentMetrics)) { |
294 | 0 | return false; |
295 | 0 | } |
296 | 0 | } |
297 | 0 | |
298 | 0 | return true; |
299 | 0 | } |
300 | | |
301 | | bool |
302 | | ClientTiledPaintedLayer::RenderHighPrecision(const nsIntRegion& aInvalidRegion, |
303 | | const nsIntRegion& aVisibleRegion, |
304 | | LayerManager::DrawPaintedLayerCallback aCallback, |
305 | | void* aCallbackData) |
306 | 0 | { |
307 | 0 | // If we have started drawing low-precision already, then we |
308 | 0 | // shouldn't do anything there. |
309 | 0 | if (mPaintData.mLowPrecisionPaintCount != 0) { |
310 | 0 | return false; |
311 | 0 | } |
312 | 0 | |
313 | 0 | // Only draw progressively when there is something to paint and the |
314 | 0 | // resolution is unchanged |
315 | 0 | if (!aInvalidRegion.IsEmpty() && |
316 | 0 | UseProgressiveDraw() && |
317 | 0 | mContentClient->GetTiledBuffer()->GetFrameResolution() == mPaintData.mResolution) { |
318 | 0 | // Store the old valid region, then clear it before painting. |
319 | 0 | // We clip the old valid region to the visible region, as it only gets |
320 | 0 | // used to decide stale content (currently valid and previously visible) |
321 | 0 | nsIntRegion oldValidRegion = mContentClient->GetTiledBuffer()->GetValidRegion(); |
322 | 0 | oldValidRegion.And(oldValidRegion, aVisibleRegion); |
323 | 0 | if (mPaintData.mCriticalDisplayPort) { |
324 | 0 | oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect()); |
325 | 0 | } |
326 | 0 |
|
327 | 0 | TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str()); |
328 | 0 |
|
329 | 0 | nsIntRegion drawnRegion; |
330 | 0 | bool updatedBuffer = |
331 | 0 | mContentClient->GetTiledBuffer()->ProgressiveUpdate(GetValidRegion(), aInvalidRegion, |
332 | 0 | oldValidRegion, drawnRegion, &mPaintData, aCallback, aCallbackData); |
333 | 0 | AddToValidRegion(drawnRegion); |
334 | 0 | return updatedBuffer; |
335 | 0 | } |
336 | 0 |
|
337 | 0 | // Otherwise do a non-progressive paint. We must do this even when |
338 | 0 | // the region to paint is empty as the valid region may have shrunk. |
339 | 0 |
|
340 | 0 | nsIntRegion validRegion = aVisibleRegion; |
341 | 0 | if (mPaintData.mCriticalDisplayPort) { |
342 | 0 | validRegion.AndWith(mPaintData.mCriticalDisplayPort->ToUnknownRect()); |
343 | 0 | } |
344 | 0 | SetValidRegion(validRegion); |
345 | 0 |
|
346 | 0 | TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str()); |
347 | 0 | TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(GetValidRegion()).c_str()); |
348 | 0 |
|
349 | 0 | TilePaintFlags flags = PaintThread::Get() |
350 | 0 | ? TilePaintFlags::Async |
351 | 0 | : TilePaintFlags::None; |
352 | 0 |
|
353 | 0 | mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution); |
354 | 0 | mContentClient->GetTiledBuffer()->PaintThebes(GetValidRegion(), aInvalidRegion, aInvalidRegion, |
355 | 0 | aCallback, aCallbackData, flags); |
356 | 0 | mPaintData.mPaintFinished = true; |
357 | 0 | return true; |
358 | 0 | } |
359 | | |
360 | | bool |
361 | | ClientTiledPaintedLayer::RenderLowPrecision(const nsIntRegion& aInvalidRegion, |
362 | | const nsIntRegion& aVisibleRegion, |
363 | | LayerManager::DrawPaintedLayerCallback aCallback, |
364 | | void* aCallbackData) |
365 | 0 | { |
366 | 0 | nsIntRegion invalidRegion = aInvalidRegion; |
367 | 0 |
|
368 | 0 | // Render the low precision buffer, if the visible region is larger than the |
369 | 0 | // critical display port. |
370 | 0 | if (!mPaintData.mCriticalDisplayPort || |
371 | 0 | !nsIntRegion(mPaintData.mCriticalDisplayPort->ToUnknownRect()).Contains(aVisibleRegion)) { |
372 | 0 | nsIntRegion oldValidRegion = mContentClient->GetLowPrecisionTiledBuffer()->GetValidRegion(); |
373 | 0 | oldValidRegion.And(oldValidRegion, aVisibleRegion); |
374 | 0 |
|
375 | 0 | bool updatedBuffer = false; |
376 | 0 |
|
377 | 0 | // If the frame resolution or format have changed, invalidate the buffer |
378 | 0 | if (mContentClient->GetLowPrecisionTiledBuffer()->GetFrameResolution() != mPaintData.mResolution || |
379 | 0 | mContentClient->GetLowPrecisionTiledBuffer()->HasFormatChanged()) { |
380 | 0 | if (!mLowPrecisionValidRegion.IsEmpty()) { |
381 | 0 | updatedBuffer = true; |
382 | 0 | } |
383 | 0 | oldValidRegion.SetEmpty(); |
384 | 0 | mLowPrecisionValidRegion.SetEmpty(); |
385 | 0 | mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState(); |
386 | 0 | mContentClient->GetLowPrecisionTiledBuffer()->SetFrameResolution(mPaintData.mResolution); |
387 | 0 | invalidRegion = aVisibleRegion; |
388 | 0 | } |
389 | 0 |
|
390 | 0 | // Invalidate previously valid content that is no longer visible |
391 | 0 | if (mPaintData.mLowPrecisionPaintCount == 1) { |
392 | 0 | mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, aVisibleRegion); |
393 | 0 | } |
394 | 0 | mPaintData.mLowPrecisionPaintCount++; |
395 | 0 |
|
396 | 0 | // Remove the valid high-precision region from the invalid low-precision |
397 | 0 | // region. We don't want to spend time drawing things twice. |
398 | 0 | invalidRegion.SubOut(GetValidRegion()); |
399 | 0 |
|
400 | 0 | TILING_LOG("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, Stringify(invalidRegion).c_str()); |
401 | 0 | TILING_LOG("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, Stringify(oldValidRegion).c_str()); |
402 | 0 |
|
403 | 0 | if (!invalidRegion.IsEmpty()) { |
404 | 0 | nsIntRegion drawnRegion; |
405 | 0 | updatedBuffer = mContentClient->GetLowPrecisionTiledBuffer()->ProgressiveUpdate( |
406 | 0 | mLowPrecisionValidRegion, invalidRegion, oldValidRegion, |
407 | 0 | drawnRegion, &mPaintData, aCallback, aCallbackData); |
408 | 0 | mLowPrecisionValidRegion.OrWith(drawnRegion); |
409 | 0 | } |
410 | 0 |
|
411 | 0 | TILING_LOG("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, Stringify(mLowPrecisionValidRegion).c_str()); |
412 | 0 | return updatedBuffer; |
413 | 0 | } |
414 | 0 | if (!mLowPrecisionValidRegion.IsEmpty()) { |
415 | 0 | TILING_LOG("TILING %p: Clearing low-precision buffer\n", this); |
416 | 0 | // Clear the low precision tiled buffer. |
417 | 0 | mLowPrecisionValidRegion.SetEmpty(); |
418 | 0 | mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState(); |
419 | 0 | // Return true here so we send a Painted callback after clearing the valid |
420 | 0 | // region of the low precision buffer. This allows the shadow buffer's valid |
421 | 0 | // region to be updated and the associated resources to be freed. |
422 | 0 | return true; |
423 | 0 | } |
424 | 0 | return false; |
425 | 0 | } |
426 | | |
427 | | void |
428 | | ClientTiledPaintedLayer::EndPaint() |
429 | 0 | { |
430 | 0 | mPaintData.mLastScrollOffset = mPaintData.mScrollOffset; |
431 | 0 | mPaintData.mPaintFinished = true; |
432 | 0 | mPaintData.mFirstPaint = false; |
433 | 0 | TILING_LOG("TILING %p: Paint finished\n", this); |
434 | 0 | } |
435 | | |
436 | | void |
437 | | ClientTiledPaintedLayer::RenderLayer() |
438 | 0 | { |
439 | 0 | LayerManager::DrawPaintedLayerCallback callback = |
440 | 0 | ClientManager()->GetPaintedLayerCallback(); |
441 | 0 | void *data = ClientManager()->GetPaintedLayerCallbackData(); |
442 | 0 |
|
443 | 0 | IntSize layerSize = mVisibleRegion.GetBounds().ToUnknownRect().Size(); |
444 | 0 | IntSize tileSize = gfx::gfxVars::TileSize(); |
445 | 0 | bool isHalfTileWidthOrHeight = layerSize.width <= tileSize.width / 2 || |
446 | 0 | layerSize.height <= tileSize.height / 2; |
447 | 0 |
|
448 | 0 | // Use single tile when layer is not scrollable, is smaller than one |
449 | 0 | // tile, or when more than half of the tiles' pixels in either |
450 | 0 | // dimension would be wasted. |
451 | 0 | bool wantSingleTiledContentClient = |
452 | 0 | (mCreationHint == LayerManager::NONE || |
453 | 0 | layerSize <= tileSize || |
454 | 0 | isHalfTileWidthOrHeight) && |
455 | 0 | SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) && |
456 | 0 | gfxPrefs::LayersSingleTileEnabled(); |
457 | 0 |
|
458 | 0 | if (mContentClient && mHaveSingleTiledContentClient && !wantSingleTiledContentClient) { |
459 | 0 | mContentClient = nullptr; |
460 | 0 | ClearValidRegion(); |
461 | 0 | } |
462 | 0 |
|
463 | 0 | if (!mContentClient) { |
464 | 0 | if (wantSingleTiledContentClient) { |
465 | 0 | mContentClient = new SingleTiledContentClient(*this, ClientManager()); |
466 | 0 | mHaveSingleTiledContentClient = true; |
467 | 0 | } else { |
468 | 0 | mContentClient = new MultiTiledContentClient(*this, ClientManager()); |
469 | 0 | mHaveSingleTiledContentClient = false; |
470 | 0 | } |
471 | 0 |
|
472 | 0 | mContentClient->Connect(); |
473 | 0 | ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); |
474 | 0 | MOZ_ASSERT(mContentClient->GetForwarder()); |
475 | 0 | } |
476 | 0 |
|
477 | 0 | if (mContentClient->GetTiledBuffer()->HasFormatChanged()) { |
478 | 0 | ClearValidRegion(); |
479 | 0 | mContentClient->GetTiledBuffer()->ResetPaintedAndValidState(); |
480 | 0 | } |
481 | 0 |
|
482 | 0 | TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str()); |
483 | 0 | TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(GetValidRegion()).c_str()); |
484 | 0 | TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str()); |
485 | 0 |
|
486 | 0 | nsIntRegion neededRegion = mVisibleRegion.ToUnknownRegion(); |
487 | 0 | #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE |
488 | 0 | // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile |
489 | 0 | if (MayResample()) { |
490 | 0 | // If we're resampling then bilinear filtering can read up to 1 pixel |
491 | 0 | // outside of our texture coords. Make the visible region a single rect, |
492 | 0 | // and pad it out by 1 pixel (restricted to tile boundaries) so that |
493 | 0 | // we always have valid content or transparent pixels to sample from. |
494 | 0 | IntRect bounds = neededRegion.GetBounds(); |
495 | 0 | IntRect wholeTiles = bounds; |
496 | 0 | wholeTiles.InflateToMultiple(gfx::gfxVars::TileSize()); |
497 | 0 | IntRect padded = bounds; |
498 | 0 | padded.Inflate(1); |
499 | 0 | padded.IntersectRect(padded, wholeTiles); |
500 | 0 | neededRegion = padded; |
501 | 0 | } |
502 | 0 | #endif |
503 | 0 |
|
504 | 0 | nsIntRegion invalidRegion; |
505 | 0 | invalidRegion.Sub(neededRegion, GetValidRegion()); |
506 | 0 | if (invalidRegion.IsEmpty()) { |
507 | 0 | EndPaint(); |
508 | 0 | return; |
509 | 0 | } |
510 | 0 | |
511 | 0 | if (!callback) { |
512 | 0 | ClientManager()->SetTransactionIncomplete(); |
513 | 0 | return; |
514 | 0 | } |
515 | 0 | |
516 | 0 | if (!ClientManager()->IsRepeatTransaction()) { |
517 | 0 | // Only paint the mask layers on the first transaction. |
518 | 0 | RenderMaskLayers(this); |
519 | 0 |
|
520 | 0 | // For more complex cases we need to calculate a bunch of metrics before we |
521 | 0 | // can do the paint. |
522 | 0 | BeginPaint(); |
523 | 0 | if (mPaintData.mPaintFinished) { |
524 | 0 | return; |
525 | 0 | } |
526 | 0 | |
527 | 0 | // Make sure that tiles that fall outside of the visible region or outside of the |
528 | 0 | // critical displayport are discarded on the first update. Also make sure that we |
529 | 0 | // only draw stuff inside the critical displayport on the first update. |
530 | 0 | nsIntRegion validRegion; |
531 | 0 | validRegion.And(GetValidRegion(), neededRegion); |
532 | 0 | if (mPaintData.mCriticalDisplayPort) { |
533 | 0 | validRegion.AndWith(mPaintData.mCriticalDisplayPort->ToUnknownRect()); |
534 | 0 | invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect()); |
535 | 0 | } |
536 | 0 | SetValidRegion(validRegion); |
537 | 0 |
|
538 | 0 | TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(validRegion).c_str()); |
539 | 0 | TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); |
540 | 0 | } else { |
541 | 0 | if (mPaintData.mCriticalDisplayPort) { |
542 | 0 | invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect()); |
543 | 0 | } |
544 | 0 | TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); |
545 | 0 | } |
546 | 0 |
|
547 | 0 | nsIntRegion lowPrecisionInvalidRegion; |
548 | 0 | if (mContentClient->GetLowPrecisionTiledBuffer()) { |
549 | 0 | // Calculate the invalid region for the low precision buffer. Make sure |
550 | 0 | // to remove the valid high-precision area so we don't double-paint it. |
551 | 0 | lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion); |
552 | 0 | lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, GetValidRegion()); |
553 | 0 | } |
554 | 0 | TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str()); |
555 | 0 |
|
556 | 0 | bool updatedHighPrecision = RenderHighPrecision(invalidRegion, |
557 | 0 | neededRegion, |
558 | 0 | callback, data); |
559 | 0 | if (updatedHighPrecision) { |
560 | 0 | ClientManager()->Hold(this); |
561 | 0 | mContentClient->UpdatedBuffer(TiledContentClient::TILED_BUFFER); |
562 | 0 |
|
563 | 0 | if (!mPaintData.mPaintFinished) { |
564 | 0 | // There is still more high-res stuff to paint, so we're not |
565 | 0 | // done yet. A subsequent transaction will take care of this. |
566 | 0 | ClientManager()->SetRepeatTransaction(); |
567 | 0 | return; |
568 | 0 | } |
569 | 0 | } |
570 | 0 | |
571 | 0 | // If there is nothing to draw in low-precision, then we're done. |
572 | 0 | if (lowPrecisionInvalidRegion.IsEmpty()) { |
573 | 0 | EndPaint(); |
574 | 0 | return; |
575 | 0 | } |
576 | 0 | |
577 | 0 | if (updatedHighPrecision) { |
578 | 0 | // If there are low precision updates, but we just did some high-precision |
579 | 0 | // updates, then mark the paint as unfinished and request a repeat transaction. |
580 | 0 | // This is so that we don't perform low-precision updates in the same transaction |
581 | 0 | // as high-precision updates. |
582 | 0 | TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this); |
583 | 0 | ClientManager()->SetRepeatTransaction(); |
584 | 0 | mPaintData.mLowPrecisionPaintCount = 1; |
585 | 0 | mPaintData.mPaintFinished = false; |
586 | 0 | return; |
587 | 0 | } |
588 | 0 | |
589 | 0 | bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion, |
590 | 0 | neededRegion, |
591 | 0 | callback, data); |
592 | 0 | if (updatedLowPrecision) { |
593 | 0 | ClientManager()->Hold(this); |
594 | 0 | mContentClient->UpdatedBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER); |
595 | 0 |
|
596 | 0 | if (!mPaintData.mPaintFinished) { |
597 | 0 | // There is still more low-res stuff to paint, so we're not |
598 | 0 | // done yet. A subsequent transaction will take care of this. |
599 | 0 | ClientManager()->SetRepeatTransaction(); |
600 | 0 | return; |
601 | 0 | } |
602 | 0 | } |
603 | 0 | |
604 | 0 | // If we get here, we've done all the high- and low-precision |
605 | 0 | // paints we wanted to do, so we can finish the paint and chill. |
606 | 0 | EndPaint(); |
607 | 0 | } |
608 | | |
609 | | bool |
610 | | ClientTiledPaintedLayer::IsOptimizedFor(LayerManager::PaintedLayerCreationHint aHint) |
611 | 0 | { |
612 | 0 | // The only creation hint is whether the layer is scrollable or not, and this |
613 | 0 | // is only respected on OSX, where it's used to determine whether to |
614 | 0 | // use a tiled content client or not. |
615 | 0 | // There are pretty nasty performance consequences for not using tiles on |
616 | 0 | // large, scrollable layers, so we want the layer to be recreated in this |
617 | 0 | // situation. |
618 | 0 | return aHint == GetCreationHint(); |
619 | 0 | } |
620 | | |
621 | | void |
622 | | ClientTiledPaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix) |
623 | 0 | { |
624 | 0 | PaintedLayer::PrintInfo(aStream, aPrefix); |
625 | 0 | if (mContentClient) { |
626 | 0 | aStream << "\n"; |
627 | 0 | nsAutoCString pfx(aPrefix); |
628 | 0 | pfx += " "; |
629 | 0 | mContentClient->PrintInfo(aStream, pfx.get()); |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | | } // namespace layers |
634 | | } // namespace mozilla |