Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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/. */
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"
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)
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);
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();
48 // Build tile description
49 std::stringstream ss;
50 ss << x << ", " << y;
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);
58 int textWidth = extents.width + 6;
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();
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();
70 c.NewPath();
71 cairo_move_to(cr, 4, 28);
72 cairo_show_text(cr, ss.str().c_str());
74 }
76 #endif
78 namespace mozilla {
80 using namespace gfx;
82 namespace layers {
85 TiledContentClient::TiledContentClient(ClientTiledThebesLayer* aThebesLayer,
86 ClientLayerManager* aManager)
87 : CompositableClient(aManager->AsShadowForwarder())
88 {
89 MOZ_COUNT_CTOR(TiledContentClient);
91 mTiledBuffer = ClientTiledLayerBuffer(aThebesLayer, this, aManager,
92 &mSharedFrameMetricsHelper);
93 mLowPrecisionTiledBuffer = ClientTiledLayerBuffer(aThebesLayer, this, aManager,
94 &mSharedFrameMetricsHelper);
96 mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution()/1000.f);
97 }
99 void
100 TiledContentClient::ClearCachedResources()
101 {
102 mTiledBuffer.DiscardBackBuffers();
103 mLowPrecisionTiledBuffer.DiscardBackBuffers();
104 }
106 void
107 TiledContentClient::UseTiledLayerBuffer(TiledBufferType aType)
108 {
109 ClientTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
110 ? &mLowPrecisionTiledBuffer
111 : &mTiledBuffer;
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();
118 mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles());
119 buffer->ClearPaintedRegion();
120 }
122 SharedFrameMetricsHelper::SharedFrameMetricsHelper()
123 : mLastProgressiveUpdateWasLowPrecision(false)
124 , mProgressiveUpdateWasInDanger(false)
125 {
126 MOZ_COUNT_CTOR(SharedFrameMetricsHelper);
127 }
129 SharedFrameMetricsHelper::~SharedFrameMetricsHelper()
130 {
131 MOZ_COUNT_DTOR(SharedFrameMetricsHelper);
132 }
134 static inline bool
135 FuzzyEquals(float a, float b) {
136 return (fabsf(a - b) < 1e-6);
137 }
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);
149 CompositorChild* compositor = CompositorChild::Get();
151 if (!compositor) {
152 FindFallbackContentFrameMetrics(aLayer, aCompositionBounds, aZoom);
153 return false;
154 }
156 const FrameMetrics& contentMetrics = aLayer->GetFrameMetrics();
157 FrameMetrics compositorMetrics;
159 if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(),
160 compositorMetrics)) {
161 FindFallbackContentFrameMetrics(aLayer, aCompositionBounds, aZoom);
162 return false;
163 }
165 aCompositionBounds = ParentLayerRect(compositorMetrics.mCompositionBounds);
166 aZoom = compositorMetrics.GetZoomToParent();
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;
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 }
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 }
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 }
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 }
213 return false;
214 }
216 void
217 SharedFrameMetricsHelper::FindFallbackContentFrameMetrics(ContainerLayer* aLayer,
218 ParentLayerRect& aCompositionBounds,
219 CSSToParentLayerScale& aZoom) {
220 if (!aLayer) {
221 return;
222 }
224 ContainerLayer* layer = aLayer;
225 const FrameMetrics* contentMetrics = &(layer->GetFrameMetrics());
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 }
233 MOZ_ASSERT(!contentMetrics->mCompositionBounds.IsEmpty());
235 aCompositionBounds = ParentLayerRect(contentMetrics->mCompositionBounds);
236 aZoom = contentMetrics->GetZoomToParent(); // TODO(botond): double-check this
237 return;
238 }
240 bool
241 SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
242 const FrameMetrics& aCompositorMetrics)
243 {
244 return !aContentMetrics.mDisplayPort.Contains(aCompositorMetrics.CalculateCompositedRectInCssPixels() - aCompositorMetrics.GetScrollOffset());
245 }
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 }
259 bool
260 ClientTiledLayerBuffer::HasFormatChanged() const
261 {
262 return mThebesLayer->CanUseOpaqueSurface() != mLastPaintOpaque;
263 }
266 gfxContentType
267 ClientTiledLayerBuffer::GetContentType() const
268 {
269 if (mThebesLayer->CanUseOpaqueSurface()) {
270 return gfxContentType::COLOR;
271 } else {
272 return gfxContentType::COLOR_ALPHA;
273 }
274 }
276 gfxMemorySharedReadLock::gfxMemorySharedReadLock()
277 : mReadCount(1)
278 {
279 MOZ_COUNT_CTOR(gfxMemorySharedReadLock);
280 }
282 gfxMemorySharedReadLock::~gfxMemorySharedReadLock()
283 {
284 MOZ_COUNT_DTOR(gfxMemorySharedReadLock);
285 }
287 int32_t
288 gfxMemorySharedReadLock::ReadLock()
289 {
290 NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock);
292 return PR_ATOMIC_INCREMENT(&mReadCount);
293 }
295 int32_t
296 gfxMemorySharedReadLock::ReadUnlock()
297 {
298 int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount);
299 NS_ASSERTION(readCount >= 0, "ReadUnlock called without ReadLock.");
301 return readCount;
302 }
304 int32_t
305 gfxMemorySharedReadLock::GetReadCount()
306 {
307 NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock);
308 return mReadCount;
309 }
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 }
327 gfxShmSharedReadLock::~gfxShmSharedReadLock()
328 {
329 MOZ_COUNT_DTOR(gfxShmSharedReadLock);
330 }
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 }
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 }
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 }
366 // Placeholder
367 TileClient::TileClient()
368 : mBackBuffer(nullptr)
369 , mFrontBuffer(nullptr)
370 , mBackLock(nullptr)
371 , mFrontLock(nullptr)
372 {
373 }
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 }
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 }
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 }
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);
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;
437 regionToCopy.Sub(regionToCopy, aDirtyRegion);
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 }
446 if (!mFrontBuffer->Lock(OPEN_READ)) {
447 NS_WARNING("Failed to lock the tile's front buffer");
448 return;
449 }
450 TextureClientAutoUnlock autoFront(mFrontBuffer);
452 if (!mBackBuffer->Lock(OPEN_WRITE)) {
453 NS_WARNING("Failed to lock the tile's back buffer");
454 return;
455 }
456 TextureClientAutoUnlock autoBack(mBackBuffer);
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);
466 mInvalidBack.SetEmpty();
467 }
468 }
469 }
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 }
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 }
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 }
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 }
534 MOZ_ASSERT(mBackLock->IsValid());
536 *aCreatedTextureClient = true;
537 mInvalidBack = nsIntRect(0, 0, mBackBuffer->GetSize().width, mBackBuffer->GetSize().height);
538 }
540 ValidateBackBufferFromFront(aDirtyRegion, aCanRerasterizeValidRegion);
542 return mBackBuffer;
543 }
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 }
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 }
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 }
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 }
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 }
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 }
603 SurfaceDescriptorTiles
604 ClientTiledLayerBuffer::GetSurfaceDescriptorTiles()
605 {
606 InfallibleTArray<TileDescriptor> tiles;
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 }
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;
631 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
632 long start = PR_IntervalNow();
633 #endif
635 // If this region is empty XMost() - 1 will give us a negative value.
636 NS_ASSERTION(!aPaintRegion.GetBounds().IsEmpty(), "Empty paint region\n");
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 */
652 if (useSinglePaintBuffer) {
653 nsRefPtr<gfxContext> ctxt;
655 const nsIntRect bounds = aPaintRegion.GetBounds();
656 {
657 PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferAlloc");
658 gfxImageFormat format =
659 gfxPlatform::GetPlatform()->OptimalFormatForContent(
660 GetContentType());
662 mSinglePaintDrawTarget =
663 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
664 gfx::IntSize(ceilf(bounds.width * mResolution),
665 ceilf(bounds.height * mResolution)),
666 gfx::ImageFormatToSurfaceFormat(format));
668 if (!mSinglePaintDrawTarget) {
669 return;
670 }
672 ctxt = new gfxContext(mSinglePaintDrawTarget);
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");
687 mCallback(mThebesLayer, ctxt, aPaintRegion, DrawRegionClip::CLIP_NONE, nsIntRegion(), mCallbackData);
688 }
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
705 PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesUpdate");
706 Update(aNewValidRegion, aPaintRegion);
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
715 mLastPaintOpaque = mThebesLayer->CanUseOpaqueSurface();
716 mCallback = nullptr;
717 mCallbackData = nullptr;
718 mSinglePaintDrawTarget = nullptr;
719 }
721 TileClient
722 ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
723 const nsIntPoint& aTileOrigin,
724 const nsIntRegion& aDirtyRegion)
725 {
726 PROFILER_LABEL("ClientTiledLayerBuffer", "ValidateTile");
728 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
729 if (aDirtyRegion.IsComplex()) {
730 printf_stderr("Complex region\n");
731 }
732 #endif
734 if (aTile.IsPlaceholderTile()) {
735 aTile.SetLayerManager(mManager);
736 }
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 }
746 bool createdTextureClient = false;
747 nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
748 offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
750 bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget;
751 RefPtr<TextureClient> backBuffer =
752 aTile.GetBackBuffer(offsetScaledDirtyRegion,
753 mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())),
754 &createdTextureClient, !usingSinglePaintBuffer);
756 if (!backBuffer->Lock(OPEN_READ_WRITE)) {
757 NS_WARNING("Failed to lock tile TextureClient for updating.");
758 aTile.DiscardFrontBuffer();
759 return aTile;
760 }
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());
768 RefPtr<gfxContext> ctxt = new gfxContext(drawTarget);
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);
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);
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 }
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);
805 // Intersect this area with the portion that's dirty.
806 tileRegion = tileRegion.Intersect(aDirtyRegion);
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);
814 // Move invalid areas into scaled layer space.
815 aTile.mInvalidFront.MoveBy(unscaledTileOrigin);
816 aTile.mInvalidBack.MoveBy(unscaledTileOrigin);
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);
822 // Add invalid areas of the backbuffer to the area to redraw.
823 tileRegion.Or(tileRegion, aTile.mInvalidBack);
825 // Move invalid areas back into tile space.
826 aTile.mInvalidFront.MoveBy(-unscaledTileOrigin);
828 // This will be validated now.
829 aTile.mInvalidBack.SetEmpty();
831 nsIntRect bounds = tileRegion.GetBounds();
832 bounds.MoveBy(-aTileOrigin);
834 if (GetContentType() != gfxContentType::COLOR) {
835 drawTarget->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height));
836 }
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);
847 }
849 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
850 DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution,
851 aTileOrigin.y * mResolution, GetTileLength(), GetTileLength());
852 #endif
854 ctxt = nullptr;
855 drawTarget = nullptr;
857 backBuffer->Unlock();
859 aTile.Flip();
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 }
870 // Note, we don't call UpdatedTexture. The Updated function is called manually
871 // by the TiledContentHost before composition.
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 }
879 return aTile;
880 }
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);
895 gfxRect transformedViewport =
896 aTransformParentLayerToLayoutDevice.TransformBounds(
897 gfxRect(offsetViewportRect.x, offsetViewportRect.y,
898 offsetViewportRect.width, offsetViewportRect.height));
900 return LayoutDeviceRect(transformedViewport.x,
901 transformedViewport.y,
902 transformedViewport.width,
903 transformedViewport.height);
904 }
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;
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 }
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();
928 // Find out if we have any non-stale content to update.
929 nsIntRegion staleRegion;
930 staleRegion.And(aInvalidRegion, aOldValidRegion);
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);
944 ContainerLayer* parent = mThebesLayer->AsLayer()->GetParent();
946 bool abortPaint =
947 mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics(
948 parent,
949 !staleRegion.Contains(aInvalidRegion),
950 drawingLowPrecision,
951 compositionBounds,
952 zoom);
953 #endif
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 }
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);
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);
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());
984 // Convert to untyped to intersect with the invalid region.
985 nsIntRect roundedCoherentUpdateRect =
986 LayoutDeviceIntRect::ToUntyped(RoundedOut(typedCoherentUpdateRect));
988 aRegionToPaint.And(aInvalidRegion, roundedCoherentUpdateRect);
989 aRegionToPaint.Or(aRegionToPaint, staleRegion);
990 bool drawingStale = !aRegionToPaint.IsEmpty();
991 if (!drawingStale) {
992 aRegionToPaint = aInvalidRegion;
993 }
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 }
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);
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();
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 }
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 }
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 }
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.
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 }
1061 mManager->SetRepeatTransaction();
1062 return false;
1063 }
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 }
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);
1092 // There's no further work to be done.
1093 if (regionToPaint.IsEmpty()) {
1094 break;
1095 }
1097 isBufferChanged = true;
1099 // Keep track of what we're about to refresh.
1100 aValidRegion.Or(aValidRegion, regionToPaint);
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);
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);
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 }
1118 }
1119 }