|
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "mozilla/layers/TiledContentClient.h" |
|
7 #include <math.h> // for ceil, ceilf, floor |
|
8 #include "ClientTiledThebesLayer.h" // for ClientTiledThebesLayer |
|
9 #include "GeckoProfiler.h" // for PROFILER_LABEL |
|
10 #include "ClientLayerManager.h" // for ClientLayerManager |
|
11 #include "CompositorChild.h" // for CompositorChild |
|
12 #include "gfxContext.h" // for gfxContext, etc |
|
13 #include "gfxPlatform.h" // for gfxPlatform |
|
14 #include "gfxPrefs.h" // for gfxPrefs |
|
15 #include "gfxRect.h" // for gfxRect |
|
16 #include "mozilla/MathAlgorithms.h" // for Abs |
|
17 #include "mozilla/gfx/Point.h" // for IntSize |
|
18 #include "mozilla/gfx/Rect.h" // for Rect |
|
19 #include "mozilla/layers/CompositableForwarder.h" |
|
20 #include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder |
|
21 #include "TextureClientPool.h" |
|
22 #include "nsDebug.h" // for NS_ASSERTION |
|
23 #include "nsISupportsImpl.h" // for gfxContext::AddRef, etc |
|
24 #include "nsSize.h" // for nsIntSize |
|
25 #include "gfxReusableSharedImageSurfaceWrapper.h" |
|
26 #include "nsMathUtils.h" // for NS_roundf |
|
27 #include "gfx2DGlue.h" |
|
28 |
|
29 // This is the minimum area that we deem reasonable to copy from the front buffer to the |
|
30 // back buffer on tile updates. If the valid region is smaller than this, we just |
|
31 // redraw it and save on the copy (and requisite surface-locking involved). |
|
32 #define MINIMUM_TILE_COPY_AREA (1.f/16.f) |
|
33 |
|
34 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY |
|
35 #include "cairo.h" |
|
36 #include <sstream> |
|
37 using mozilla::layers::Layer; |
|
38 static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y, int width, int height) |
|
39 { |
|
40 gfxContext c(dt); |
|
41 |
|
42 // Draw border |
|
43 c.NewPath(); |
|
44 c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0)); |
|
45 c.Rectangle(gfxRect(0, 0, width, height)); |
|
46 c.Stroke(); |
|
47 |
|
48 // Build tile description |
|
49 std::stringstream ss; |
|
50 ss << x << ", " << y; |
|
51 |
|
52 // Draw text using cairo toy text API |
|
53 cairo_t* cr = c.GetCairo(); |
|
54 cairo_set_font_size(cr, 25); |
|
55 cairo_text_extents_t extents; |
|
56 cairo_text_extents(cr, ss.str().c_str(), &extents); |
|
57 |
|
58 int textWidth = extents.width + 6; |
|
59 |
|
60 c.NewPath(); |
|
61 c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0)); |
|
62 c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30))); |
|
63 c.Fill(); |
|
64 |
|
65 c.NewPath(); |
|
66 c.SetDeviceColor(gfxRGBA(1.0, 0.0, 0.0, 1.0)); |
|
67 c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30))); |
|
68 c.Stroke(); |
|
69 |
|
70 c.NewPath(); |
|
71 cairo_move_to(cr, 4, 28); |
|
72 cairo_show_text(cr, ss.str().c_str()); |
|
73 |
|
74 } |
|
75 |
|
76 #endif |
|
77 |
|
78 namespace mozilla { |
|
79 |
|
80 using namespace gfx; |
|
81 |
|
82 namespace layers { |
|
83 |
|
84 |
|
85 TiledContentClient::TiledContentClient(ClientTiledThebesLayer* aThebesLayer, |
|
86 ClientLayerManager* aManager) |
|
87 : CompositableClient(aManager->AsShadowForwarder()) |
|
88 { |
|
89 MOZ_COUNT_CTOR(TiledContentClient); |
|
90 |
|
91 mTiledBuffer = ClientTiledLayerBuffer(aThebesLayer, this, aManager, |
|
92 &mSharedFrameMetricsHelper); |
|
93 mLowPrecisionTiledBuffer = ClientTiledLayerBuffer(aThebesLayer, this, aManager, |
|
94 &mSharedFrameMetricsHelper); |
|
95 |
|
96 mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution()/1000.f); |
|
97 } |
|
98 |
|
99 void |
|
100 TiledContentClient::ClearCachedResources() |
|
101 { |
|
102 mTiledBuffer.DiscardBackBuffers(); |
|
103 mLowPrecisionTiledBuffer.DiscardBackBuffers(); |
|
104 } |
|
105 |
|
106 void |
|
107 TiledContentClient::UseTiledLayerBuffer(TiledBufferType aType) |
|
108 { |
|
109 ClientTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER |
|
110 ? &mLowPrecisionTiledBuffer |
|
111 : &mTiledBuffer; |
|
112 |
|
113 // Take a ReadLock on behalf of the TiledContentHost. This |
|
114 // reference will be adopted when the descriptor is opened in |
|
115 // TiledLayerBufferComposite. |
|
116 buffer->ReadLock(); |
|
117 |
|
118 mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles()); |
|
119 buffer->ClearPaintedRegion(); |
|
120 } |
|
121 |
|
122 SharedFrameMetricsHelper::SharedFrameMetricsHelper() |
|
123 : mLastProgressiveUpdateWasLowPrecision(false) |
|
124 , mProgressiveUpdateWasInDanger(false) |
|
125 { |
|
126 MOZ_COUNT_CTOR(SharedFrameMetricsHelper); |
|
127 } |
|
128 |
|
129 SharedFrameMetricsHelper::~SharedFrameMetricsHelper() |
|
130 { |
|
131 MOZ_COUNT_DTOR(SharedFrameMetricsHelper); |
|
132 } |
|
133 |
|
134 static inline bool |
|
135 FuzzyEquals(float a, float b) { |
|
136 return (fabsf(a - b) < 1e-6); |
|
137 } |
|
138 |
|
139 bool |
|
140 SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics( |
|
141 ContainerLayer* aLayer, |
|
142 bool aHasPendingNewThebesContent, |
|
143 bool aLowPrecision, |
|
144 ParentLayerRect& aCompositionBounds, |
|
145 CSSToParentLayerScale& aZoom) |
|
146 { |
|
147 MOZ_ASSERT(aLayer); |
|
148 |
|
149 CompositorChild* compositor = CompositorChild::Get(); |
|
150 |
|
151 if (!compositor) { |
|
152 FindFallbackContentFrameMetrics(aLayer, aCompositionBounds, aZoom); |
|
153 return false; |
|
154 } |
|
155 |
|
156 const FrameMetrics& contentMetrics = aLayer->GetFrameMetrics(); |
|
157 FrameMetrics compositorMetrics; |
|
158 |
|
159 if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(), |
|
160 compositorMetrics)) { |
|
161 FindFallbackContentFrameMetrics(aLayer, aCompositionBounds, aZoom); |
|
162 return false; |
|
163 } |
|
164 |
|
165 aCompositionBounds = ParentLayerRect(compositorMetrics.mCompositionBounds); |
|
166 aZoom = compositorMetrics.GetZoomToParent(); |
|
167 |
|
168 // Reset the checkerboard risk flag when switching to low precision |
|
169 // rendering. |
|
170 if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) { |
|
171 // Skip low precision rendering until we're at risk of checkerboarding. |
|
172 if (!mProgressiveUpdateWasInDanger) { |
|
173 return true; |
|
174 } |
|
175 mProgressiveUpdateWasInDanger = false; |
|
176 } |
|
177 mLastProgressiveUpdateWasLowPrecision = aLowPrecision; |
|
178 |
|
179 // Always abort updates if the resolution has changed. There's no use |
|
180 // in drawing at the incorrect resolution. |
|
181 if (!FuzzyEquals(compositorMetrics.GetZoom().scale, contentMetrics.GetZoom().scale)) { |
|
182 return true; |
|
183 } |
|
184 |
|
185 // Never abort drawing if we can't be sure we've sent a more recent |
|
186 // display-port. If we abort updating when we shouldn't, we can end up |
|
187 // with blank regions on the screen and we open up the risk of entering |
|
188 // an endless updating cycle. |
|
189 if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 && |
|
190 fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 && |
|
191 fabsf(contentMetrics.mDisplayPort.x - compositorMetrics.mDisplayPort.x) <= 2 && |
|
192 fabsf(contentMetrics.mDisplayPort.y - compositorMetrics.mDisplayPort.y) <= 2 && |
|
193 fabsf(contentMetrics.mDisplayPort.width - compositorMetrics.mDisplayPort.width) <= 2 && |
|
194 fabsf(contentMetrics.mDisplayPort.height - compositorMetrics.mDisplayPort.height)) { |
|
195 return false; |
|
196 } |
|
197 |
|
198 // When not a low precision pass and the page is in danger of checker boarding |
|
199 // abort update. |
|
200 if (!aLowPrecision && !mProgressiveUpdateWasInDanger) { |
|
201 if (AboutToCheckerboard(contentMetrics, compositorMetrics)) { |
|
202 mProgressiveUpdateWasInDanger = true; |
|
203 return true; |
|
204 } |
|
205 } |
|
206 |
|
207 // Abort drawing stale low-precision content if there's a more recent |
|
208 // display-port in the pipeline. |
|
209 if (aLowPrecision && !aHasPendingNewThebesContent) { |
|
210 return true; |
|
211 } |
|
212 |
|
213 return false; |
|
214 } |
|
215 |
|
216 void |
|
217 SharedFrameMetricsHelper::FindFallbackContentFrameMetrics(ContainerLayer* aLayer, |
|
218 ParentLayerRect& aCompositionBounds, |
|
219 CSSToParentLayerScale& aZoom) { |
|
220 if (!aLayer) { |
|
221 return; |
|
222 } |
|
223 |
|
224 ContainerLayer* layer = aLayer; |
|
225 const FrameMetrics* contentMetrics = &(layer->GetFrameMetrics()); |
|
226 |
|
227 // Walk up the layer tree until a valid composition bounds is found |
|
228 while (layer && contentMetrics->mCompositionBounds.IsEmpty()) { |
|
229 layer = layer->GetParent(); |
|
230 contentMetrics = layer ? &(layer->GetFrameMetrics()) : contentMetrics; |
|
231 } |
|
232 |
|
233 MOZ_ASSERT(!contentMetrics->mCompositionBounds.IsEmpty()); |
|
234 |
|
235 aCompositionBounds = ParentLayerRect(contentMetrics->mCompositionBounds); |
|
236 aZoom = contentMetrics->GetZoomToParent(); // TODO(botond): double-check this |
|
237 return; |
|
238 } |
|
239 |
|
240 bool |
|
241 SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics, |
|
242 const FrameMetrics& aCompositorMetrics) |
|
243 { |
|
244 return !aContentMetrics.mDisplayPort.Contains(aCompositorMetrics.CalculateCompositedRectInCssPixels() - aCompositorMetrics.GetScrollOffset()); |
|
245 } |
|
246 |
|
247 ClientTiledLayerBuffer::ClientTiledLayerBuffer(ClientTiledThebesLayer* aThebesLayer, |
|
248 CompositableClient* aCompositableClient, |
|
249 ClientLayerManager* aManager, |
|
250 SharedFrameMetricsHelper* aHelper) |
|
251 : mThebesLayer(aThebesLayer) |
|
252 , mCompositableClient(aCompositableClient) |
|
253 , mManager(aManager) |
|
254 , mLastPaintOpaque(false) |
|
255 , mSharedFrameMetricsHelper(aHelper) |
|
256 { |
|
257 } |
|
258 |
|
259 bool |
|
260 ClientTiledLayerBuffer::HasFormatChanged() const |
|
261 { |
|
262 return mThebesLayer->CanUseOpaqueSurface() != mLastPaintOpaque; |
|
263 } |
|
264 |
|
265 |
|
266 gfxContentType |
|
267 ClientTiledLayerBuffer::GetContentType() const |
|
268 { |
|
269 if (mThebesLayer->CanUseOpaqueSurface()) { |
|
270 return gfxContentType::COLOR; |
|
271 } else { |
|
272 return gfxContentType::COLOR_ALPHA; |
|
273 } |
|
274 } |
|
275 |
|
276 gfxMemorySharedReadLock::gfxMemorySharedReadLock() |
|
277 : mReadCount(1) |
|
278 { |
|
279 MOZ_COUNT_CTOR(gfxMemorySharedReadLock); |
|
280 } |
|
281 |
|
282 gfxMemorySharedReadLock::~gfxMemorySharedReadLock() |
|
283 { |
|
284 MOZ_COUNT_DTOR(gfxMemorySharedReadLock); |
|
285 } |
|
286 |
|
287 int32_t |
|
288 gfxMemorySharedReadLock::ReadLock() |
|
289 { |
|
290 NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock); |
|
291 |
|
292 return PR_ATOMIC_INCREMENT(&mReadCount); |
|
293 } |
|
294 |
|
295 int32_t |
|
296 gfxMemorySharedReadLock::ReadUnlock() |
|
297 { |
|
298 int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount); |
|
299 NS_ASSERTION(readCount >= 0, "ReadUnlock called without ReadLock."); |
|
300 |
|
301 return readCount; |
|
302 } |
|
303 |
|
304 int32_t |
|
305 gfxMemorySharedReadLock::GetReadCount() |
|
306 { |
|
307 NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock); |
|
308 return mReadCount; |
|
309 } |
|
310 |
|
311 gfxShmSharedReadLock::gfxShmSharedReadLock(ISurfaceAllocator* aAllocator) |
|
312 : mAllocator(aAllocator) |
|
313 , mAllocSuccess(false) |
|
314 { |
|
315 MOZ_COUNT_CTOR(gfxShmSharedReadLock); |
|
316 MOZ_ASSERT(mAllocator); |
|
317 if (mAllocator) { |
|
318 #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3) |
|
319 if (mAllocator->AllocShmemSection(MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) { |
|
320 ShmReadLockInfo* info = GetShmReadLockInfoPtr(); |
|
321 info->readCount = 1; |
|
322 mAllocSuccess = true; |
|
323 } |
|
324 } |
|
325 } |
|
326 |
|
327 gfxShmSharedReadLock::~gfxShmSharedReadLock() |
|
328 { |
|
329 MOZ_COUNT_DTOR(gfxShmSharedReadLock); |
|
330 } |
|
331 |
|
332 int32_t |
|
333 gfxShmSharedReadLock::ReadLock() { |
|
334 NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock); |
|
335 if (!mAllocSuccess) { |
|
336 return 0; |
|
337 } |
|
338 ShmReadLockInfo* info = GetShmReadLockInfoPtr(); |
|
339 return PR_ATOMIC_INCREMENT(&info->readCount); |
|
340 } |
|
341 |
|
342 int32_t |
|
343 gfxShmSharedReadLock::ReadUnlock() { |
|
344 if (!mAllocSuccess) { |
|
345 return 0; |
|
346 } |
|
347 ShmReadLockInfo* info = GetShmReadLockInfoPtr(); |
|
348 int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount); |
|
349 NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock."); |
|
350 if (readCount <= 0) { |
|
351 mAllocator->FreeShmemSection(mShmemSection); |
|
352 } |
|
353 return readCount; |
|
354 } |
|
355 |
|
356 int32_t |
|
357 gfxShmSharedReadLock::GetReadCount() { |
|
358 NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock); |
|
359 if (!mAllocSuccess) { |
|
360 return 0; |
|
361 } |
|
362 ShmReadLockInfo* info = GetShmReadLockInfoPtr(); |
|
363 return info->readCount; |
|
364 } |
|
365 |
|
366 // Placeholder |
|
367 TileClient::TileClient() |
|
368 : mBackBuffer(nullptr) |
|
369 , mFrontBuffer(nullptr) |
|
370 , mBackLock(nullptr) |
|
371 , mFrontLock(nullptr) |
|
372 { |
|
373 } |
|
374 |
|
375 TileClient::TileClient(const TileClient& o) |
|
376 { |
|
377 mBackBuffer = o.mBackBuffer; |
|
378 mFrontBuffer = o.mFrontBuffer; |
|
379 mBackLock = o.mBackLock; |
|
380 mFrontLock = o.mFrontLock; |
|
381 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY |
|
382 mLastUpdate = o.mLastUpdate; |
|
383 #endif |
|
384 mManager = o.mManager; |
|
385 mInvalidFront = o.mInvalidFront; |
|
386 mInvalidBack = o.mInvalidBack; |
|
387 } |
|
388 |
|
389 TileClient& |
|
390 TileClient::operator=(const TileClient& o) |
|
391 { |
|
392 if (this == &o) return *this; |
|
393 mBackBuffer = o.mBackBuffer; |
|
394 mFrontBuffer = o.mFrontBuffer; |
|
395 mBackLock = o.mBackLock; |
|
396 mFrontLock = o.mFrontLock; |
|
397 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY |
|
398 mLastUpdate = o.mLastUpdate; |
|
399 #endif |
|
400 mManager = o.mManager; |
|
401 mInvalidFront = o.mInvalidFront; |
|
402 mInvalidBack = o.mInvalidBack; |
|
403 return *this; |
|
404 } |
|
405 |
|
406 |
|
407 void |
|
408 TileClient::Flip() |
|
409 { |
|
410 RefPtr<TextureClient> frontBuffer = mFrontBuffer; |
|
411 mFrontBuffer = mBackBuffer; |
|
412 mBackBuffer = frontBuffer; |
|
413 RefPtr<gfxSharedReadLock> frontLock = mFrontLock; |
|
414 mFrontLock = mBackLock; |
|
415 mBackLock = frontLock; |
|
416 nsIntRegion invalidFront = mInvalidFront; |
|
417 mInvalidFront = mInvalidBack; |
|
418 mInvalidBack = invalidFront; |
|
419 } |
|
420 |
|
421 void |
|
422 TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion, |
|
423 bool aCanRerasterizeValidRegion) |
|
424 { |
|
425 if (mBackBuffer && mFrontBuffer) { |
|
426 gfx::IntSize tileSize = mFrontBuffer->GetSize(); |
|
427 const nsIntRect tileRect = nsIntRect(0, 0, tileSize.width, tileSize.height); |
|
428 |
|
429 if (aDirtyRegion.Contains(tileRect)) { |
|
430 // The dirty region means that we no longer need the front buffer, so |
|
431 // discard it. |
|
432 DiscardFrontBuffer(); |
|
433 } else { |
|
434 // Region that needs copying. |
|
435 nsIntRegion regionToCopy = mInvalidBack; |
|
436 |
|
437 regionToCopy.Sub(regionToCopy, aDirtyRegion); |
|
438 |
|
439 if (regionToCopy.IsEmpty() || |
|
440 (aCanRerasterizeValidRegion && |
|
441 regionToCopy.Area() < tileSize.width * tileSize.height * MINIMUM_TILE_COPY_AREA)) { |
|
442 // Just redraw it all. |
|
443 return; |
|
444 } |
|
445 |
|
446 if (!mFrontBuffer->Lock(OPEN_READ)) { |
|
447 NS_WARNING("Failed to lock the tile's front buffer"); |
|
448 return; |
|
449 } |
|
450 TextureClientAutoUnlock autoFront(mFrontBuffer); |
|
451 |
|
452 if (!mBackBuffer->Lock(OPEN_WRITE)) { |
|
453 NS_WARNING("Failed to lock the tile's back buffer"); |
|
454 return; |
|
455 } |
|
456 TextureClientAutoUnlock autoBack(mBackBuffer); |
|
457 |
|
458 // Copy the bounding rect of regionToCopy. As tiles are quite small, it |
|
459 // is unlikely that we'd save much by copying each individual rect of the |
|
460 // region, but we can reevaluate this if it becomes an issue. |
|
461 const nsIntRect rectToCopy = regionToCopy.GetBounds(); |
|
462 gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.width, rectToCopy.height); |
|
463 gfx::IntPoint gfxRectToCopyTopLeft = gfxRectToCopy.TopLeft(); |
|
464 mFrontBuffer->CopyToTextureClient(mBackBuffer, &gfxRectToCopy, &gfxRectToCopyTopLeft); |
|
465 |
|
466 mInvalidBack.SetEmpty(); |
|
467 } |
|
468 } |
|
469 } |
|
470 |
|
471 void |
|
472 TileClient::DiscardFrontBuffer() |
|
473 { |
|
474 if (mFrontBuffer) { |
|
475 MOZ_ASSERT(mFrontLock); |
|
476 mManager->GetTexturePool(mFrontBuffer->GetFormat())->ReturnTextureClientDeferred(mFrontBuffer); |
|
477 mFrontLock->ReadUnlock(); |
|
478 mFrontBuffer = nullptr; |
|
479 mFrontLock = nullptr; |
|
480 } |
|
481 } |
|
482 |
|
483 void |
|
484 TileClient::DiscardBackBuffer() |
|
485 { |
|
486 if (mBackBuffer) { |
|
487 MOZ_ASSERT(mBackLock); |
|
488 if (!mBackBuffer->ImplementsLocking() && mBackLock->GetReadCount() > 1) { |
|
489 // Our current back-buffer is still locked by the compositor. This can occur |
|
490 // when the client is producing faster than the compositor can consume. In |
|
491 // this case we just want to drop it and not return it to the pool. |
|
492 mManager->GetTexturePool(mBackBuffer->GetFormat())->ReportClientLost(); |
|
493 } else { |
|
494 mManager->GetTexturePool(mBackBuffer->GetFormat())->ReturnTextureClient(mBackBuffer); |
|
495 } |
|
496 mBackLock->ReadUnlock(); |
|
497 mBackBuffer = nullptr; |
|
498 mBackLock = nullptr; |
|
499 } |
|
500 } |
|
501 |
|
502 TextureClient* |
|
503 TileClient::GetBackBuffer(const nsIntRegion& aDirtyRegion, TextureClientPool *aPool, bool *aCreatedTextureClient, bool aCanRerasterizeValidRegion) |
|
504 { |
|
505 // Try to re-use the front-buffer if possible |
|
506 if (mFrontBuffer && |
|
507 mFrontBuffer->HasInternalBuffer() && |
|
508 mFrontLock->GetReadCount() == 1) { |
|
509 // If we had a backbuffer we no longer care about it since we'll |
|
510 // re-use the front buffer. |
|
511 DiscardBackBuffer(); |
|
512 Flip(); |
|
513 return mBackBuffer; |
|
514 } |
|
515 |
|
516 if (!mBackBuffer || |
|
517 mBackLock->GetReadCount() > 1) { |
|
518 if (mBackBuffer) { |
|
519 // Our current back-buffer is still locked by the compositor. This can occur |
|
520 // when the client is producing faster than the compositor can consume. In |
|
521 // this case we just want to drop it and not return it to the pool. |
|
522 aPool->ReportClientLost(); |
|
523 } |
|
524 mBackBuffer = aPool->GetTextureClient(); |
|
525 // Create a lock for our newly created back-buffer. |
|
526 if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) { |
|
527 // If our compositor is in the same process, we can save some cycles by not |
|
528 // using shared memory. |
|
529 mBackLock = new gfxMemorySharedReadLock(); |
|
530 } else { |
|
531 mBackLock = new gfxShmSharedReadLock(mManager->AsShadowForwarder()); |
|
532 } |
|
533 |
|
534 MOZ_ASSERT(mBackLock->IsValid()); |
|
535 |
|
536 *aCreatedTextureClient = true; |
|
537 mInvalidBack = nsIntRect(0, 0, mBackBuffer->GetSize().width, mBackBuffer->GetSize().height); |
|
538 } |
|
539 |
|
540 ValidateBackBufferFromFront(aDirtyRegion, aCanRerasterizeValidRegion); |
|
541 |
|
542 return mBackBuffer; |
|
543 } |
|
544 |
|
545 TileDescriptor |
|
546 TileClient::GetTileDescriptor() |
|
547 { |
|
548 if (IsPlaceholderTile()) { |
|
549 return PlaceholderTileDescriptor(); |
|
550 } |
|
551 MOZ_ASSERT(mFrontLock); |
|
552 if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) { |
|
553 // AddRef here and Release when receiving on the host side to make sure the |
|
554 // reference count doesn't go to zero before the host receives the message. |
|
555 // see TiledLayerBufferComposite::TiledLayerBufferComposite |
|
556 mFrontLock->AddRef(); |
|
557 } |
|
558 |
|
559 if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) { |
|
560 return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(), |
|
561 TileLock(uintptr_t(mFrontLock.get()))); |
|
562 } else { |
|
563 gfxShmSharedReadLock *lock = static_cast<gfxShmSharedReadLock*>(mFrontLock.get()); |
|
564 return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(), |
|
565 TileLock(lock->GetShmemSection())); |
|
566 } |
|
567 } |
|
568 |
|
569 void |
|
570 ClientTiledLayerBuffer::ReadUnlock() { |
|
571 for (size_t i = 0; i < mRetainedTiles.Length(); i++) { |
|
572 if (mRetainedTiles[i].IsPlaceholderTile()) continue; |
|
573 mRetainedTiles[i].ReadUnlock(); |
|
574 } |
|
575 } |
|
576 |
|
577 void |
|
578 ClientTiledLayerBuffer::ReadLock() { |
|
579 for (size_t i = 0; i < mRetainedTiles.Length(); i++) { |
|
580 if (mRetainedTiles[i].IsPlaceholderTile()) continue; |
|
581 mRetainedTiles[i].ReadLock(); |
|
582 } |
|
583 } |
|
584 |
|
585 void |
|
586 ClientTiledLayerBuffer::Release() |
|
587 { |
|
588 for (size_t i = 0; i < mRetainedTiles.Length(); i++) { |
|
589 if (mRetainedTiles[i].IsPlaceholderTile()) continue; |
|
590 mRetainedTiles[i].Release(); |
|
591 } |
|
592 } |
|
593 |
|
594 void |
|
595 ClientTiledLayerBuffer::DiscardBackBuffers() |
|
596 { |
|
597 for (size_t i = 0; i < mRetainedTiles.Length(); i++) { |
|
598 if (mRetainedTiles[i].IsPlaceholderTile()) continue; |
|
599 mRetainedTiles[i].DiscardBackBuffer(); |
|
600 } |
|
601 } |
|
602 |
|
603 SurfaceDescriptorTiles |
|
604 ClientTiledLayerBuffer::GetSurfaceDescriptorTiles() |
|
605 { |
|
606 InfallibleTArray<TileDescriptor> tiles; |
|
607 |
|
608 for (size_t i = 0; i < mRetainedTiles.Length(); i++) { |
|
609 TileDescriptor tileDesc; |
|
610 if (mRetainedTiles.SafeElementAt(i, GetPlaceholderTile()) == GetPlaceholderTile()) { |
|
611 tileDesc = PlaceholderTileDescriptor(); |
|
612 } else { |
|
613 tileDesc = mRetainedTiles[i].GetTileDescriptor(); |
|
614 } |
|
615 tiles.AppendElement(tileDesc); |
|
616 } |
|
617 return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion, |
|
618 tiles, mRetainedWidth, mRetainedHeight, |
|
619 mResolution, mFrameResolution.scale); |
|
620 } |
|
621 |
|
622 void |
|
623 ClientTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, |
|
624 const nsIntRegion& aPaintRegion, |
|
625 LayerManager::DrawThebesLayerCallback aCallback, |
|
626 void* aCallbackData) |
|
627 { |
|
628 mCallback = aCallback; |
|
629 mCallbackData = aCallbackData; |
|
630 |
|
631 #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
|
632 long start = PR_IntervalNow(); |
|
633 #endif |
|
634 |
|
635 // If this region is empty XMost() - 1 will give us a negative value. |
|
636 NS_ASSERTION(!aPaintRegion.GetBounds().IsEmpty(), "Empty paint region\n"); |
|
637 |
|
638 bool useSinglePaintBuffer = UseSinglePaintBuffer(); |
|
639 // XXX The single-tile case doesn't work at the moment, see bug 850396 |
|
640 /* |
|
641 if (useSinglePaintBuffer) { |
|
642 // Check if the paint only spans a single tile. If that's |
|
643 // the case there's no point in using a single paint buffer. |
|
644 nsIntRect paintBounds = aPaintRegion.GetBounds(); |
|
645 useSinglePaintBuffer = GetTileStart(paintBounds.x) != |
|
646 GetTileStart(paintBounds.XMost() - 1) || |
|
647 GetTileStart(paintBounds.y) != |
|
648 GetTileStart(paintBounds.YMost() - 1); |
|
649 } |
|
650 */ |
|
651 |
|
652 if (useSinglePaintBuffer) { |
|
653 nsRefPtr<gfxContext> ctxt; |
|
654 |
|
655 const nsIntRect bounds = aPaintRegion.GetBounds(); |
|
656 { |
|
657 PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferAlloc"); |
|
658 gfxImageFormat format = |
|
659 gfxPlatform::GetPlatform()->OptimalFormatForContent( |
|
660 GetContentType()); |
|
661 |
|
662 mSinglePaintDrawTarget = |
|
663 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( |
|
664 gfx::IntSize(ceilf(bounds.width * mResolution), |
|
665 ceilf(bounds.height * mResolution)), |
|
666 gfx::ImageFormatToSurfaceFormat(format)); |
|
667 |
|
668 if (!mSinglePaintDrawTarget) { |
|
669 return; |
|
670 } |
|
671 |
|
672 ctxt = new gfxContext(mSinglePaintDrawTarget); |
|
673 |
|
674 mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y); |
|
675 } |
|
676 ctxt->NewPath(); |
|
677 ctxt->Scale(mResolution, mResolution); |
|
678 ctxt->Translate(gfxPoint(-bounds.x, -bounds.y)); |
|
679 #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
|
680 if (PR_IntervalNow() - start > 3) { |
|
681 printf_stderr("Slow alloc %i\n", PR_IntervalNow() - start); |
|
682 } |
|
683 start = PR_IntervalNow(); |
|
684 #endif |
|
685 PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferDraw"); |
|
686 |
|
687 mCallback(mThebesLayer, ctxt, aPaintRegion, DrawRegionClip::CLIP_NONE, nsIntRegion(), mCallbackData); |
|
688 } |
|
689 |
|
690 #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
|
691 if (PR_IntervalNow() - start > 30) { |
|
692 const nsIntRect bounds = aPaintRegion.GetBounds(); |
|
693 printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height); |
|
694 if (aPaintRegion.IsComplex()) { |
|
695 printf_stderr("Complex region\n"); |
|
696 nsIntRegionRectIterator it(aPaintRegion); |
|
697 for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) { |
|
698 printf_stderr(" rect %i, %i, %i, %i\n", rect->x, rect->y, rect->width, rect->height); |
|
699 } |
|
700 } |
|
701 } |
|
702 start = PR_IntervalNow(); |
|
703 #endif |
|
704 |
|
705 PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesUpdate"); |
|
706 Update(aNewValidRegion, aPaintRegion); |
|
707 |
|
708 #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
|
709 if (PR_IntervalNow() - start > 10) { |
|
710 const nsIntRect bounds = aPaintRegion.GetBounds(); |
|
711 printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height); |
|
712 } |
|
713 #endif |
|
714 |
|
715 mLastPaintOpaque = mThebesLayer->CanUseOpaqueSurface(); |
|
716 mCallback = nullptr; |
|
717 mCallbackData = nullptr; |
|
718 mSinglePaintDrawTarget = nullptr; |
|
719 } |
|
720 |
|
721 TileClient |
|
722 ClientTiledLayerBuffer::ValidateTile(TileClient aTile, |
|
723 const nsIntPoint& aTileOrigin, |
|
724 const nsIntRegion& aDirtyRegion) |
|
725 { |
|
726 PROFILER_LABEL("ClientTiledLayerBuffer", "ValidateTile"); |
|
727 |
|
728 #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
|
729 if (aDirtyRegion.IsComplex()) { |
|
730 printf_stderr("Complex region\n"); |
|
731 } |
|
732 #endif |
|
733 |
|
734 if (aTile.IsPlaceholderTile()) { |
|
735 aTile.SetLayerManager(mManager); |
|
736 } |
|
737 |
|
738 // Discard our front and backbuffers if our contents changed. In this case |
|
739 // the calling code will already have taken care of invalidating the entire |
|
740 // layer. |
|
741 if (HasFormatChanged()) { |
|
742 aTile.DiscardBackBuffer(); |
|
743 aTile.DiscardFrontBuffer(); |
|
744 } |
|
745 |
|
746 bool createdTextureClient = false; |
|
747 nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin); |
|
748 offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution); |
|
749 |
|
750 bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget; |
|
751 RefPtr<TextureClient> backBuffer = |
|
752 aTile.GetBackBuffer(offsetScaledDirtyRegion, |
|
753 mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())), |
|
754 &createdTextureClient, !usingSinglePaintBuffer); |
|
755 |
|
756 if (!backBuffer->Lock(OPEN_READ_WRITE)) { |
|
757 NS_WARNING("Failed to lock tile TextureClient for updating."); |
|
758 aTile.DiscardFrontBuffer(); |
|
759 return aTile; |
|
760 } |
|
761 |
|
762 // We must not keep a reference to the DrawTarget after it has been unlocked, |
|
763 // make sure these are null'd before unlocking as destruction of the context |
|
764 // may cause the target to be flushed. |
|
765 RefPtr<DrawTarget> drawTarget = backBuffer->GetAsDrawTarget(); |
|
766 drawTarget->SetTransform(Matrix()); |
|
767 |
|
768 RefPtr<gfxContext> ctxt = new gfxContext(drawTarget); |
|
769 |
|
770 if (usingSinglePaintBuffer) { |
|
771 // XXX Perhaps we should just copy the bounding rectangle here? |
|
772 RefPtr<gfx::SourceSurface> source = mSinglePaintDrawTarget->Snapshot(); |
|
773 nsIntRegionRectIterator it(aDirtyRegion); |
|
774 for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) { |
|
775 #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
|
776 printf_stderr(" break into subdirtyRect %i, %i, %i, %i\n", |
|
777 dirtyRect->x, dirtyRect->y, dirtyRect->width, dirtyRect->height); |
|
778 #endif |
|
779 gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x, |
|
780 dirtyRect->y - aTileOrigin.y, |
|
781 dirtyRect->width, |
|
782 dirtyRect->height); |
|
783 drawRect.Scale(mResolution); |
|
784 |
|
785 gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution), |
|
786 NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution), |
|
787 drawRect.width, |
|
788 drawRect.height); |
|
789 gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y)); |
|
790 drawTarget->CopySurface(source, copyRect, copyTarget); |
|
791 |
|
792 // Mark the newly updated area as invalid in the front buffer |
|
793 aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height)); |
|
794 } |
|
795 |
|
796 // The new buffer is now validated, remove the dirty region from it. |
|
797 aTile.mInvalidBack.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height), |
|
798 offsetScaledDirtyRegion); |
|
799 } else { |
|
800 // Area of the full tile... |
|
801 nsIntRegion tileRegion = |
|
802 nsIntRect(aTileOrigin.x, aTileOrigin.y, |
|
803 GetScaledTileSize().width, GetScaledTileSize().height); |
|
804 |
|
805 // Intersect this area with the portion that's dirty. |
|
806 tileRegion = tileRegion.Intersect(aDirtyRegion); |
|
807 |
|
808 // Add the resolution scale to store the dirty region. |
|
809 nsIntPoint unscaledTileOrigin = nsIntPoint(aTileOrigin.x * mResolution, |
|
810 aTileOrigin.y * mResolution); |
|
811 nsIntRegion unscaledTileRegion(tileRegion); |
|
812 unscaledTileRegion.ScaleRoundOut(mResolution, mResolution); |
|
813 |
|
814 // Move invalid areas into scaled layer space. |
|
815 aTile.mInvalidFront.MoveBy(unscaledTileOrigin); |
|
816 aTile.mInvalidBack.MoveBy(unscaledTileOrigin); |
|
817 |
|
818 // Add the area that's going to be redrawn to the invalid area of the |
|
819 // front region. |
|
820 aTile.mInvalidFront.Or(aTile.mInvalidFront, unscaledTileRegion); |
|
821 |
|
822 // Add invalid areas of the backbuffer to the area to redraw. |
|
823 tileRegion.Or(tileRegion, aTile.mInvalidBack); |
|
824 |
|
825 // Move invalid areas back into tile space. |
|
826 aTile.mInvalidFront.MoveBy(-unscaledTileOrigin); |
|
827 |
|
828 // This will be validated now. |
|
829 aTile.mInvalidBack.SetEmpty(); |
|
830 |
|
831 nsIntRect bounds = tileRegion.GetBounds(); |
|
832 bounds.MoveBy(-aTileOrigin); |
|
833 |
|
834 if (GetContentType() != gfxContentType::COLOR) { |
|
835 drawTarget->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height)); |
|
836 } |
|
837 |
|
838 ctxt->NewPath(); |
|
839 ctxt->Clip(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)); |
|
840 ctxt->Translate(gfxPoint(-unscaledTileOrigin.x, -unscaledTileOrigin.y)); |
|
841 ctxt->Scale(mResolution, mResolution); |
|
842 mCallback(mThebesLayer, ctxt, |
|
843 tileRegion.GetBounds(), |
|
844 DrawRegionClip::CLIP_NONE, |
|
845 nsIntRegion(), mCallbackData); |
|
846 |
|
847 } |
|
848 |
|
849 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY |
|
850 DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution, |
|
851 aTileOrigin.y * mResolution, GetTileLength(), GetTileLength()); |
|
852 #endif |
|
853 |
|
854 ctxt = nullptr; |
|
855 drawTarget = nullptr; |
|
856 |
|
857 backBuffer->Unlock(); |
|
858 |
|
859 aTile.Flip(); |
|
860 |
|
861 if (createdTextureClient) { |
|
862 if (!mCompositableClient->AddTextureClient(backBuffer)) { |
|
863 NS_WARNING("Failed to add tile TextureClient."); |
|
864 aTile.DiscardFrontBuffer(); |
|
865 aTile.DiscardBackBuffer(); |
|
866 return aTile; |
|
867 } |
|
868 } |
|
869 |
|
870 // Note, we don't call UpdatedTexture. The Updated function is called manually |
|
871 // by the TiledContentHost before composition. |
|
872 |
|
873 if (backBuffer->HasInternalBuffer()) { |
|
874 // If our new buffer has an internal buffer, we don't want to keep another |
|
875 // TextureClient around unnecessarily, so discard the back-buffer. |
|
876 aTile.DiscardBackBuffer(); |
|
877 } |
|
878 |
|
879 return aTile; |
|
880 } |
|
881 |
|
882 static LayoutDeviceRect |
|
883 TransformCompositionBounds(const ParentLayerRect& aCompositionBounds, |
|
884 const CSSToParentLayerScale& aZoom, |
|
885 const ParentLayerPoint& aScrollOffset, |
|
886 const CSSToParentLayerScale& aResolution, |
|
887 const gfx3DMatrix& aTransformParentLayerToLayoutDevice) |
|
888 { |
|
889 // Transform the current composition bounds into ParentLayer coordinates |
|
890 // by compensating for the difference in resolution and subtracting the |
|
891 // old composition bounds origin. |
|
892 ParentLayerRect offsetViewportRect = (aCompositionBounds / aZoom) * aResolution; |
|
893 offsetViewportRect.MoveBy(-aScrollOffset); |
|
894 |
|
895 gfxRect transformedViewport = |
|
896 aTransformParentLayerToLayoutDevice.TransformBounds( |
|
897 gfxRect(offsetViewportRect.x, offsetViewportRect.y, |
|
898 offsetViewportRect.width, offsetViewportRect.height)); |
|
899 |
|
900 return LayoutDeviceRect(transformedViewport.x, |
|
901 transformedViewport.y, |
|
902 transformedViewport.width, |
|
903 transformedViewport.height); |
|
904 } |
|
905 |
|
906 bool |
|
907 ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion, |
|
908 const nsIntRegion& aOldValidRegion, |
|
909 nsIntRegion& aRegionToPaint, |
|
910 BasicTiledLayerPaintData* aPaintData, |
|
911 bool aIsRepeated) |
|
912 { |
|
913 aRegionToPaint = aInvalidRegion; |
|
914 |
|
915 // If the composition bounds rect is empty, we can't make any sensible |
|
916 // decision about how to update coherently. In this case, just update |
|
917 // everything in one transaction. |
|
918 if (aPaintData->mCompositionBounds.IsEmpty()) { |
|
919 aPaintData->mPaintFinished = true; |
|
920 return false; |
|
921 } |
|
922 |
|
923 // If this is a low precision buffer, we force progressive updates. The |
|
924 // assumption is that the contents is less important, so visual coherency |
|
925 // is lower priority than speed. |
|
926 bool drawingLowPrecision = IsLowPrecision(); |
|
927 |
|
928 // Find out if we have any non-stale content to update. |
|
929 nsIntRegion staleRegion; |
|
930 staleRegion.And(aInvalidRegion, aOldValidRegion); |
|
931 |
|
932 // Find out the current view transform to determine which tiles to draw |
|
933 // first, and see if we should just abort this paint. Aborting is usually |
|
934 // caused by there being an incoming, more relevant paint. |
|
935 ParentLayerRect compositionBounds; |
|
936 CSSToParentLayerScale zoom; |
|
937 #if defined(MOZ_WIDGET_ANDROID) |
|
938 bool abortPaint = mManager->ProgressiveUpdateCallback(!staleRegion.Contains(aInvalidRegion), |
|
939 compositionBounds, zoom, |
|
940 !drawingLowPrecision); |
|
941 #else |
|
942 MOZ_ASSERT(mSharedFrameMetricsHelper); |
|
943 |
|
944 ContainerLayer* parent = mThebesLayer->AsLayer()->GetParent(); |
|
945 |
|
946 bool abortPaint = |
|
947 mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics( |
|
948 parent, |
|
949 !staleRegion.Contains(aInvalidRegion), |
|
950 drawingLowPrecision, |
|
951 compositionBounds, |
|
952 zoom); |
|
953 #endif |
|
954 |
|
955 if (abortPaint) { |
|
956 // We ignore if front-end wants to abort if this is the first, |
|
957 // non-low-precision paint, as in that situation, we're about to override |
|
958 // front-end's page/viewport metrics. |
|
959 if (!aPaintData->mFirstPaint || drawingLowPrecision) { |
|
960 PROFILER_LABEL("ContentClient", "Abort painting"); |
|
961 aRegionToPaint.SetEmpty(); |
|
962 return aIsRepeated; |
|
963 } |
|
964 } |
|
965 |
|
966 // Transform the composition bounds, which is in the ParentLayer coordinates |
|
967 // of the nearest ContainerLayer with a valid displayport to LayoutDevice |
|
968 // coordinates relative to this layer. |
|
969 LayoutDeviceRect transformedCompositionBounds = |
|
970 TransformCompositionBounds(compositionBounds, zoom, aPaintData->mScrollOffset, |
|
971 aPaintData->mResolution, aPaintData->mTransformParentLayerToLayoutDevice); |
|
972 |
|
973 // Paint tiles that have stale content or that intersected with the screen |
|
974 // at the time of issuing the draw command in a single transaction first. |
|
975 // This is to avoid rendering glitches on animated page content, and when |
|
976 // layers change size/shape. |
|
977 LayoutDeviceRect typedCoherentUpdateRect = |
|
978 transformedCompositionBounds.Intersect(aPaintData->mCompositionBounds); |
|
979 |
|
980 // Offset by the viewport origin, as the composition bounds are stored in |
|
981 // Layer space and not LayoutDevice space. |
|
982 typedCoherentUpdateRect.MoveBy(aPaintData->mViewport.TopLeft()); |
|
983 |
|
984 // Convert to untyped to intersect with the invalid region. |
|
985 nsIntRect roundedCoherentUpdateRect = |
|
986 LayoutDeviceIntRect::ToUntyped(RoundedOut(typedCoherentUpdateRect)); |
|
987 |
|
988 aRegionToPaint.And(aInvalidRegion, roundedCoherentUpdateRect); |
|
989 aRegionToPaint.Or(aRegionToPaint, staleRegion); |
|
990 bool drawingStale = !aRegionToPaint.IsEmpty(); |
|
991 if (!drawingStale) { |
|
992 aRegionToPaint = aInvalidRegion; |
|
993 } |
|
994 |
|
995 // Prioritise tiles that are currently visible on the screen. |
|
996 bool paintVisible = false; |
|
997 if (aRegionToPaint.Intersects(roundedCoherentUpdateRect)) { |
|
998 aRegionToPaint.And(aRegionToPaint, roundedCoherentUpdateRect); |
|
999 paintVisible = true; |
|
1000 } |
|
1001 |
|
1002 // Paint area that's visible and overlaps previously valid content to avoid |
|
1003 // visible glitches in animated elements, such as gifs. |
|
1004 bool paintInSingleTransaction = paintVisible && (drawingStale || aPaintData->mFirstPaint); |
|
1005 |
|
1006 // The following code decides what order to draw tiles in, based on the |
|
1007 // current scroll direction of the primary scrollable layer. |
|
1008 NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!"); |
|
1009 nsIntRect paintBounds = aRegionToPaint.GetBounds(); |
|
1010 |
|
1011 int startX, incX, startY, incY; |
|
1012 gfx::IntSize scaledTileSize = GetScaledTileSize(); |
|
1013 if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) { |
|
1014 startX = RoundDownToTileEdge(paintBounds.x, scaledTileSize.width); |
|
1015 incX = scaledTileSize.width; |
|
1016 } else { |
|
1017 startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width); |
|
1018 incX = -scaledTileSize.width; |
|
1019 } |
|
1020 |
|
1021 if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) { |
|
1022 startY = RoundDownToTileEdge(paintBounds.y, scaledTileSize.height); |
|
1023 incY = scaledTileSize.height; |
|
1024 } else { |
|
1025 startY = RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height); |
|
1026 incY = -scaledTileSize.height; |
|
1027 } |
|
1028 |
|
1029 // Find a tile to draw. |
|
1030 nsIntRect tileBounds(startX, startY, scaledTileSize.width, scaledTileSize.height); |
|
1031 int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x; |
|
1032 int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y; |
|
1033 // This loop will always terminate, as there is at least one tile area |
|
1034 // along the first/last row/column intersecting with regionToPaint, or its |
|
1035 // bounds would have been smaller. |
|
1036 while (true) { |
|
1037 aRegionToPaint.And(aInvalidRegion, tileBounds); |
|
1038 if (!aRegionToPaint.IsEmpty()) { |
|
1039 break; |
|
1040 } |
|
1041 if (Abs(scrollDiffY) >= Abs(scrollDiffX)) { |
|
1042 tileBounds.x += incX; |
|
1043 } else { |
|
1044 tileBounds.y += incY; |
|
1045 } |
|
1046 } |
|
1047 |
|
1048 if (!aRegionToPaint.Contains(aInvalidRegion)) { |
|
1049 // The region needed to paint is larger then our progressive chunk size |
|
1050 // therefore update what we want to paint and ask for a new paint transaction. |
|
1051 |
|
1052 // If we need to draw more than one tile to maintain coherency, make |
|
1053 // sure it happens in the same transaction by requesting this work be |
|
1054 // repeated immediately. |
|
1055 // If this is unnecessary, the remaining work will be done tile-by-tile in |
|
1056 // subsequent transactions. |
|
1057 if (!drawingLowPrecision && paintInSingleTransaction) { |
|
1058 return true; |
|
1059 } |
|
1060 |
|
1061 mManager->SetRepeatTransaction(); |
|
1062 return false; |
|
1063 } |
|
1064 |
|
1065 // We're not repeating painting and we've not requested a repeat transaction, |
|
1066 // so the paint is finished. If there's still a separate low precision |
|
1067 // paint to do, it will get marked as unfinished later. |
|
1068 aPaintData->mPaintFinished = true; |
|
1069 return false; |
|
1070 } |
|
1071 |
|
1072 bool |
|
1073 ClientTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion, |
|
1074 nsIntRegion& aInvalidRegion, |
|
1075 const nsIntRegion& aOldValidRegion, |
|
1076 BasicTiledLayerPaintData* aPaintData, |
|
1077 LayerManager::DrawThebesLayerCallback aCallback, |
|
1078 void* aCallbackData) |
|
1079 { |
|
1080 bool repeat = false; |
|
1081 bool isBufferChanged = false; |
|
1082 do { |
|
1083 // Compute the region that should be updated. Repeat as many times as |
|
1084 // is required. |
|
1085 nsIntRegion regionToPaint; |
|
1086 repeat = ComputeProgressiveUpdateRegion(aInvalidRegion, |
|
1087 aOldValidRegion, |
|
1088 regionToPaint, |
|
1089 aPaintData, |
|
1090 repeat); |
|
1091 |
|
1092 // There's no further work to be done. |
|
1093 if (regionToPaint.IsEmpty()) { |
|
1094 break; |
|
1095 } |
|
1096 |
|
1097 isBufferChanged = true; |
|
1098 |
|
1099 // Keep track of what we're about to refresh. |
|
1100 aValidRegion.Or(aValidRegion, regionToPaint); |
|
1101 |
|
1102 // aValidRegion may have been altered by InvalidateRegion, but we still |
|
1103 // want to display stale content until it gets progressively updated. |
|
1104 // Create a region that includes stale content. |
|
1105 nsIntRegion validOrStale; |
|
1106 validOrStale.Or(aValidRegion, aOldValidRegion); |
|
1107 |
|
1108 // Paint the computed region and subtract it from the invalid region. |
|
1109 PaintThebes(validOrStale, regionToPaint, aCallback, aCallbackData); |
|
1110 aInvalidRegion.Sub(aInvalidRegion, regionToPaint); |
|
1111 } while (repeat); |
|
1112 |
|
1113 // Return false if nothing has been drawn, or give what has been drawn |
|
1114 // to the shadow layer to upload. |
|
1115 return isBufferChanged; |
|
1116 } |
|
1117 |
|
1118 } |
|
1119 } |