Line data Source code
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 <algorithm>
9 : #include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
10 : #include "GeckoProfiler.h" // for AUTO_PROFILER_LABEL
11 : #include "ClientLayerManager.h" // for ClientLayerManager
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/gfx/Tools.h" // for BytesPerPixel
20 : #include "mozilla/layers/CompositableForwarder.h"
21 : #include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
22 : #include "mozilla/layers/LayerMetricsWrapper.h"
23 : #include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
24 : #include "TextureClientPool.h"
25 : #include "nsDebug.h" // for NS_ASSERTION
26 : #include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
27 : #include "nsExpirationTracker.h" // for nsExpirationTracker
28 : #include "nsMathUtils.h" // for NS_lroundf
29 : #include "LayersLogging.h"
30 : #include "UnitTransforms.h" // for TransformTo
31 : #include "mozilla/UniquePtr.h"
32 :
33 : // This is the minimum area that we deem reasonable to copy from the front buffer to the
34 : // back buffer on tile updates. If the valid region is smaller than this, we just
35 : // redraw it and save on the copy (and requisite surface-locking involved).
36 : #define MINIMUM_TILE_COPY_AREA (1.f/16.f)
37 :
38 : #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
39 : #include "cairo.h"
40 : #include <sstream>
41 : using mozilla::layers::Layer;
42 : static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y, int width, int height)
43 : {
44 : gfxContext c(dt);
45 :
46 : // Draw border
47 : c.NewPath();
48 : c.SetDeviceColor(Color(0.f, 0.f, 0.f));
49 : c.Rectangle(gfxRect(0, 0, width, height));
50 : c.Stroke();
51 :
52 : // Build tile description
53 : std::stringstream ss;
54 : ss << x << ", " << y;
55 :
56 : // Draw text using cairo toy text API
57 : // XXX: this drawing will silently fail if |dt| doesn't have a Cairo backend
58 : cairo_t* cr = gfxFont::RefCairo(dt);
59 : cairo_set_font_size(cr, 25);
60 : cairo_text_extents_t extents;
61 : cairo_text_extents(cr, ss.str().c_str(), &extents);
62 :
63 : int textWidth = extents.width + 6;
64 :
65 : c.NewPath();
66 : c.SetDeviceColor(Color(0.f, 0.f, 0.f));
67 : c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
68 : c.Fill();
69 :
70 : c.NewPath();
71 : c.SetDeviceColor(Color(1.0, 0.0, 0.0));
72 : c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
73 : c.Stroke();
74 :
75 : c.NewPath();
76 : cairo_move_to(cr, 4, 28);
77 : cairo_show_text(cr, ss.str().c_str());
78 :
79 : }
80 :
81 : #endif
82 :
83 : namespace mozilla {
84 :
85 : using namespace gfx;
86 :
87 : namespace layers {
88 :
89 :
90 0 : MultiTiledContentClient::MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
91 0 : ClientLayerManager* aManager)
92 : : TiledContentClient(aManager, "Multi")
93 : , mTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper)
94 0 : , mLowPrecisionTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper)
95 : {
96 0 : MOZ_COUNT_CTOR(MultiTiledContentClient);
97 0 : mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution());
98 0 : mHasLowPrecision = gfxPrefs::UseLowPrecisionBuffer();
99 0 : }
100 :
101 : void
102 0 : MultiTiledContentClient::ClearCachedResources()
103 : {
104 0 : CompositableClient::ClearCachedResources();
105 0 : mTiledBuffer.DiscardBuffers();
106 0 : mLowPrecisionTiledBuffer.DiscardBuffers();
107 0 : }
108 :
109 : void
110 0 : MultiTiledContentClient::UpdatedBuffer(TiledBufferType aType)
111 : {
112 : ClientMultiTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
113 0 : ? &mLowPrecisionTiledBuffer
114 0 : : &mTiledBuffer;
115 :
116 0 : MOZ_ASSERT(aType != LOW_PRECISION_TILED_BUFFER || mHasLowPrecision);
117 :
118 0 : mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles());
119 0 : buffer->ClearPaintedRegion();
120 0 : }
121 :
122 0 : SharedFrameMetricsHelper::SharedFrameMetricsHelper()
123 : : mLastProgressiveUpdateWasLowPrecision(false)
124 0 : , mProgressiveUpdateWasInDanger(false)
125 : {
126 0 : MOZ_COUNT_CTOR(SharedFrameMetricsHelper);
127 0 : }
128 :
129 0 : SharedFrameMetricsHelper::~SharedFrameMetricsHelper()
130 : {
131 0 : MOZ_COUNT_DTOR(SharedFrameMetricsHelper);
132 0 : }
133 :
134 : static inline bool
135 0 : FuzzyEquals(float a, float b) {
136 0 : return (fabsf(a - b) < 1e-6);
137 : }
138 :
139 : static AsyncTransform
140 0 : ComputeViewTransform(const FrameMetrics& aContentMetrics, const FrameMetrics& aCompositorMetrics)
141 : {
142 : // This is basically the same code as AsyncPanZoomController::GetCurrentAsyncTransform
143 : // but with aContentMetrics used in place of mLastContentPaintMetrics, because they
144 : // should be equivalent, modulo race conditions while transactions are inflight.
145 :
146 0 : ParentLayerPoint translation = (aCompositorMetrics.GetScrollOffset() - aContentMetrics.GetScrollOffset())
147 0 : * aCompositorMetrics.GetZoom();
148 0 : return AsyncTransform(aCompositorMetrics.GetAsyncZoom(), -translation);
149 : }
150 :
151 : bool
152 0 : SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
153 : const LayerMetricsWrapper& aLayer,
154 : bool aHasPendingNewThebesContent,
155 : bool aLowPrecision,
156 : AsyncTransform& aViewTransform)
157 : {
158 0 : MOZ_ASSERT(aLayer);
159 :
160 0 : CompositorBridgeChild* compositor = nullptr;
161 0 : if (aLayer.Manager() &&
162 0 : aLayer.Manager()->AsClientLayerManager()) {
163 0 : compositor = aLayer.Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
164 : }
165 :
166 0 : if (!compositor) {
167 0 : return false;
168 : }
169 :
170 0 : const FrameMetrics& contentMetrics = aLayer.Metrics();
171 0 : FrameMetrics compositorMetrics;
172 :
173 0 : if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(),
174 : compositorMetrics)) {
175 0 : return false;
176 : }
177 :
178 0 : aViewTransform = ComputeViewTransform(contentMetrics, compositorMetrics);
179 :
180 : // Reset the checkerboard risk flag when switching to low precision
181 : // rendering.
182 0 : if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
183 : // Skip low precision rendering until we're at risk of checkerboarding.
184 0 : if (!mProgressiveUpdateWasInDanger) {
185 : TILING_LOG("TILING: Aborting low-precision rendering because not at risk of checkerboarding\n");
186 0 : return true;
187 : }
188 0 : mProgressiveUpdateWasInDanger = false;
189 : }
190 0 : mLastProgressiveUpdateWasLowPrecision = aLowPrecision;
191 :
192 : // Always abort updates if the resolution has changed. There's no use
193 : // in drawing at the incorrect resolution.
194 0 : if (!FuzzyEquals(compositorMetrics.GetZoom().xScale, contentMetrics.GetZoom().xScale) ||
195 0 : !FuzzyEquals(compositorMetrics.GetZoom().yScale, contentMetrics.GetZoom().yScale)) {
196 : TILING_LOG("TILING: Aborting because resolution changed from %s to %s\n",
197 : ToString(contentMetrics.GetZoom()).c_str(), ToString(compositorMetrics.GetZoom()).c_str());
198 0 : return true;
199 : }
200 :
201 : // Never abort drawing if we can't be sure we've sent a more recent
202 : // display-port. If we abort updating when we shouldn't, we can end up
203 : // with blank regions on the screen and we open up the risk of entering
204 : // an endless updating cycle.
205 0 : if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 &&
206 0 : fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 &&
207 0 : fabsf(contentMetrics.GetDisplayPort().x - compositorMetrics.GetDisplayPort().x) <= 2 &&
208 0 : fabsf(contentMetrics.GetDisplayPort().y - compositorMetrics.GetDisplayPort().y) <= 2 &&
209 0 : fabsf(contentMetrics.GetDisplayPort().width - compositorMetrics.GetDisplayPort().width) <= 2 &&
210 0 : fabsf(contentMetrics.GetDisplayPort().height - compositorMetrics.GetDisplayPort().height) <= 2) {
211 0 : return false;
212 : }
213 :
214 : // When not a low precision pass and the page is in danger of checker boarding
215 : // abort update.
216 0 : if (!aLowPrecision && !mProgressiveUpdateWasInDanger) {
217 0 : bool scrollUpdatePending = contentMetrics.GetScrollOffsetUpdated() &&
218 0 : contentMetrics.GetScrollGeneration() != compositorMetrics.GetScrollGeneration();
219 : // If scrollUpdatePending is true, then that means the content-side
220 : // metrics has a new scroll offset that is going to be forced into the
221 : // compositor but it hasn't gotten there yet.
222 : // Even though right now comparing the metrics might indicate we're
223 : // about to checkerboard (and that's true), the checkerboarding will
224 : // disappear as soon as the new scroll offset update is processed
225 : // on the compositor side. To avoid leaving things in a low-precision
226 : // paint, we need to detect and handle this case (bug 1026756).
227 0 : if (!scrollUpdatePending && AboutToCheckerboard(contentMetrics, compositorMetrics)) {
228 0 : mProgressiveUpdateWasInDanger = true;
229 0 : return true;
230 : }
231 : }
232 :
233 : // Abort drawing stale low-precision content if there's a more recent
234 : // display-port in the pipeline.
235 0 : if (aLowPrecision && !aHasPendingNewThebesContent) {
236 : TILING_LOG("TILING: Aborting low-precision because of new pending content\n");
237 0 : return true;
238 : }
239 :
240 0 : return false;
241 : }
242 :
243 : bool
244 0 : SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
245 : const FrameMetrics& aCompositorMetrics)
246 : {
247 : // The size of the painted area is originally computed in layer pixels in layout, but then
248 : // converted to app units and then back to CSS pixels before being put in the FrameMetrics.
249 : // This process can introduce some rounding error, so we inflate the rect by one app unit
250 : // to account for that.
251 0 : CSSRect painted = (aContentMetrics.GetCriticalDisplayPort().IsEmpty()
252 0 : ? aContentMetrics.GetDisplayPort()
253 : : aContentMetrics.GetCriticalDisplayPort())
254 0 : + aContentMetrics.GetScrollOffset();
255 0 : painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
256 :
257 : // Inflate the rect by the danger zone. See the description of the danger zone prefs
258 : // in AsyncPanZoomController.cpp for an explanation of this.
259 0 : CSSRect showing = CSSRect(aCompositorMetrics.GetScrollOffset(),
260 0 : aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
261 0 : showing.Inflate(LayerSize(gfxPrefs::APZDangerZoneX(), gfxPrefs::APZDangerZoneY())
262 0 : / aCompositorMetrics.LayersPixelsPerCSSPixel());
263 :
264 : // Clamp both rects to the scrollable rect, because having either of those
265 : // exceed the scrollable rect doesn't make sense, and could lead to false
266 : // positives.
267 0 : painted = painted.Intersect(aContentMetrics.GetScrollableRect());
268 0 : showing = showing.Intersect(aContentMetrics.GetScrollableRect());
269 :
270 0 : if (!painted.Contains(showing)) {
271 : TILING_LOG("TILING: About to checkerboard; content %s\n", Stringify(aContentMetrics).c_str());
272 : TILING_LOG("TILING: About to checkerboard; painted %s\n", Stringify(painted).c_str());
273 : TILING_LOG("TILING: About to checkerboard; compositor %s\n", Stringify(aCompositorMetrics).c_str());
274 : TILING_LOG("TILING: About to checkerboard; showing %s\n", Stringify(showing).c_str());
275 0 : return true;
276 : }
277 0 : return false;
278 : }
279 :
280 0 : ClientMultiTiledLayerBuffer::ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
281 : CompositableClient& aCompositableClient,
282 : ClientLayerManager* aManager,
283 0 : SharedFrameMetricsHelper* aHelper)
284 : : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient)
285 : , mManager(aManager)
286 : , mCallback(nullptr)
287 : , mCallbackData(nullptr)
288 0 : , mSharedFrameMetricsHelper(aHelper)
289 : {
290 0 : }
291 :
292 : bool
293 0 : ClientTiledLayerBuffer::HasFormatChanged() const
294 : {
295 : SurfaceMode mode;
296 0 : gfxContentType content = GetContentType(&mode);
297 0 : return content != mLastPaintContentType ||
298 0 : mode != mLastPaintSurfaceMode;
299 : }
300 :
301 :
302 : gfxContentType
303 0 : ClientTiledLayerBuffer::GetContentType(SurfaceMode* aMode) const
304 : {
305 : gfxContentType content =
306 0 : mPaintedLayer.CanUseOpaqueSurface() ? gfxContentType::COLOR :
307 0 : gfxContentType::COLOR_ALPHA;
308 0 : SurfaceMode mode = mPaintedLayer.GetSurfaceMode();
309 :
310 0 : if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
311 : #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
312 : mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
313 : #else
314 0 : if (!mPaintedLayer.GetParent() ||
315 0 : !mPaintedLayer.GetParent()->SupportsComponentAlphaChildren()) {
316 0 : mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
317 : } else {
318 0 : content = gfxContentType::COLOR;
319 : }
320 : #endif
321 0 : } else if (mode == SurfaceMode::SURFACE_OPAQUE) {
322 : #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
323 : if (IsLowPrecision()) {
324 : // If we're in low-res mode, drawing can sample from outside the visible
325 : // region. Make sure that we only sample transparency if that happens.
326 : mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
327 : content = gfxContentType::COLOR_ALPHA;
328 : }
329 : #else
330 0 : if (mPaintedLayer.MayResample()) {
331 0 : mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
332 0 : content = gfxContentType::COLOR_ALPHA;
333 : }
334 : #endif
335 : }
336 :
337 0 : if (aMode) {
338 0 : *aMode = mode;
339 : }
340 0 : return content;
341 : }
342 :
343 0 : class TileExpiry final : public nsExpirationTracker<TileClient, 3>
344 : {
345 : public:
346 0 : TileExpiry() : nsExpirationTracker<TileClient, 3>(1000, "TileExpiry") {}
347 :
348 0 : static void AddTile(TileClient* aTile)
349 : {
350 0 : if (!sTileExpiry) {
351 0 : sTileExpiry = MakeUnique<TileExpiry>();
352 : }
353 :
354 0 : sTileExpiry->AddObject(aTile);
355 0 : }
356 :
357 0 : static void RemoveTile(TileClient* aTile)
358 : {
359 0 : MOZ_ASSERT(sTileExpiry);
360 0 : sTileExpiry->RemoveObject(aTile);
361 0 : }
362 :
363 0 : static void Shutdown() {
364 0 : sTileExpiry = nullptr;
365 0 : }
366 : private:
367 0 : virtual void NotifyExpired(TileClient* aTile) override
368 : {
369 0 : aTile->DiscardBackBuffer();
370 0 : }
371 :
372 : static UniquePtr<TileExpiry> sTileExpiry;
373 : };
374 3 : UniquePtr<TileExpiry> TileExpiry::sTileExpiry;
375 :
376 0 : void ShutdownTileCache()
377 : {
378 0 : TileExpiry::Shutdown();
379 0 : }
380 :
381 : void
382 0 : TileClient::PrivateProtector::Set(TileClient * const aContainer, RefPtr<TextureClient> aNewValue)
383 : {
384 0 : if (mBuffer) {
385 0 : TileExpiry::RemoveTile(aContainer);
386 : }
387 0 : mBuffer = aNewValue;
388 0 : if (mBuffer) {
389 0 : TileExpiry::AddTile(aContainer);
390 : }
391 0 : }
392 :
393 : void
394 0 : TileClient::PrivateProtector::Set(TileClient * const aContainer, TextureClient* aNewValue)
395 : {
396 0 : Set(aContainer, RefPtr<TextureClient>(aNewValue));
397 0 : }
398 :
399 : // Placeholder
400 0 : TileClient::TileClient()
401 0 : : mWasPlaceholder(false)
402 : {
403 0 : }
404 :
405 0 : TileClient::~TileClient()
406 : {
407 0 : if (mExpirationState.IsTracked()) {
408 0 : MOZ_ASSERT(mBackBuffer);
409 0 : TileExpiry::RemoveTile(this);
410 : }
411 0 : }
412 :
413 0 : TileClient::TileClient(const TileClient& o)
414 : {
415 0 : mBackBuffer.Set(this, o.mBackBuffer);
416 0 : mBackBufferOnWhite = o.mBackBufferOnWhite;
417 0 : mFrontBuffer = o.mFrontBuffer;
418 0 : mFrontBufferOnWhite = o.mFrontBufferOnWhite;
419 0 : mWasPlaceholder = o.mWasPlaceholder;
420 0 : mUpdateRect = o.mUpdateRect;
421 : #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
422 : mLastUpdate = o.mLastUpdate;
423 : #endif
424 0 : mAllocator = o.mAllocator;
425 0 : mInvalidFront = o.mInvalidFront;
426 0 : mInvalidBack = o.mInvalidBack;
427 0 : }
428 :
429 : TileClient&
430 0 : TileClient::operator=(const TileClient& o)
431 : {
432 0 : if (this == &o) return *this;
433 0 : mBackBuffer.Set(this, o.mBackBuffer);
434 0 : mBackBufferOnWhite = o.mBackBufferOnWhite;
435 0 : mFrontBuffer = o.mFrontBuffer;
436 0 : mFrontBufferOnWhite = o.mFrontBufferOnWhite;
437 0 : mWasPlaceholder = o.mWasPlaceholder;
438 0 : mUpdateRect = o.mUpdateRect;
439 : #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
440 : mLastUpdate = o.mLastUpdate;
441 : #endif
442 0 : mAllocator = o.mAllocator;
443 0 : mInvalidFront = o.mInvalidFront;
444 0 : mInvalidBack = o.mInvalidBack;
445 0 : return *this;
446 : }
447 :
448 : void
449 0 : TileClient::Dump(std::stringstream& aStream)
450 : {
451 0 : aStream << "TileClient(bb=" << (TextureClient*)mBackBuffer << " fb=" << mFrontBuffer.get();
452 0 : if (mBackBufferOnWhite) {
453 0 : aStream << " bbow=" << mBackBufferOnWhite.get();
454 : }
455 0 : if (mFrontBufferOnWhite) {
456 0 : aStream << " fbow=" << mFrontBufferOnWhite.get();
457 : }
458 0 : aStream << ")";
459 0 : }
460 :
461 : void
462 0 : TileClient::Flip()
463 : {
464 0 : RefPtr<TextureClient> frontBuffer = mFrontBuffer;
465 0 : RefPtr<TextureClient> frontBufferOnWhite = mFrontBufferOnWhite;
466 0 : mFrontBuffer = mBackBuffer;
467 0 : mFrontBufferOnWhite = mBackBufferOnWhite;
468 0 : mBackBuffer.Set(this, frontBuffer);
469 0 : mBackBufferOnWhite = frontBufferOnWhite;
470 0 : nsIntRegion invalidFront = mInvalidFront;
471 0 : mInvalidFront = mInvalidBack;
472 0 : mInvalidBack = invalidFront;
473 0 : }
474 :
475 : static bool
476 0 : CopyFrontToBack(TextureClient* aFront,
477 : TextureClient* aBack,
478 : const gfx::IntRect& aRectToCopy)
479 : {
480 0 : TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ);
481 0 : if (!frontLock.Succeeded()) {
482 0 : gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's front buffer";
483 0 : return false;
484 : }
485 :
486 0 : if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
487 0 : gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
488 0 : return false;
489 : }
490 :
491 0 : gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
492 0 : aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
493 0 : return true;
494 : }
495 :
496 : void
497 0 : TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
498 : nsIntRegion& aAddPaintedRegion)
499 : {
500 0 : if (mBackBuffer && mFrontBuffer) {
501 0 : gfx::IntSize tileSize = mFrontBuffer->GetSize();
502 0 : const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
503 :
504 0 : if (aDirtyRegion.Contains(tileRect)) {
505 : // The dirty region means that we no longer need the front buffer, so
506 : // discard it.
507 0 : DiscardFrontBuffer();
508 : } else {
509 : // Region that needs copying.
510 0 : nsIntRegion regionToCopy = mInvalidBack;
511 :
512 0 : regionToCopy.Sub(regionToCopy, aDirtyRegion);
513 :
514 0 : aAddPaintedRegion = regionToCopy;
515 :
516 0 : if (regionToCopy.IsEmpty()) {
517 : // Just redraw it all.
518 0 : return;
519 : }
520 :
521 : // Copy the bounding rect of regionToCopy. As tiles are quite small, it
522 : // is unlikely that we'd save much by copying each individual rect of the
523 : // region, but we can reevaluate this if it becomes an issue.
524 0 : const IntRect rectToCopy = regionToCopy.GetBounds();
525 0 : gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.width, rectToCopy.height);
526 0 : CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy);
527 :
528 0 : if (mBackBufferOnWhite) {
529 0 : MOZ_ASSERT(mFrontBufferOnWhite);
530 0 : CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy);
531 : }
532 :
533 0 : mInvalidBack.SetEmpty();
534 : }
535 : }
536 : }
537 :
538 : void
539 0 : TileClient::DiscardFrontBuffer()
540 : {
541 0 : if (mFrontBuffer) {
542 0 : MOZ_ASSERT(mFrontBuffer->GetReadLock());
543 :
544 0 : MOZ_ASSERT(mAllocator);
545 0 : if (mAllocator) {
546 0 : mAllocator->ReturnTextureClientDeferred(mFrontBuffer);
547 0 : if (mFrontBufferOnWhite) {
548 0 : mAllocator->ReturnTextureClientDeferred(mFrontBufferOnWhite);
549 : }
550 : }
551 :
552 0 : if (mFrontBuffer->IsLocked()) {
553 0 : mFrontBuffer->Unlock();
554 : }
555 0 : if (mFrontBufferOnWhite && mFrontBufferOnWhite->IsLocked()) {
556 0 : mFrontBufferOnWhite->Unlock();
557 : }
558 0 : mFrontBuffer = nullptr;
559 0 : mFrontBufferOnWhite = nullptr;
560 : }
561 0 : }
562 :
563 : static void
564 0 : DiscardTexture(TextureClient* aTexture, TextureClientAllocator* aAllocator)
565 : {
566 0 : MOZ_ASSERT(aAllocator);
567 0 : if (aTexture && aAllocator) {
568 0 : MOZ_ASSERT(aTexture->GetReadLock());
569 0 : if (!aTexture->HasSynchronization() && aTexture->IsReadLocked()) {
570 : // Our current back-buffer is still locked by the compositor. This can occur
571 : // when the client is producing faster than the compositor can consume. In
572 : // this case we just want to drop it and not return it to the pool.
573 0 : aAllocator->ReportClientLost();
574 : } else {
575 0 : aAllocator->ReturnTextureClientDeferred(aTexture);
576 : }
577 0 : if (aTexture->IsLocked()) {
578 0 : aTexture->Unlock();
579 : }
580 : }
581 0 : }
582 :
583 : void
584 0 : TileClient::DiscardBackBuffer()
585 : {
586 0 : if (mBackBuffer) {
587 0 : DiscardTexture(mBackBuffer, mAllocator);
588 0 : mBackBuffer.Set(this, nullptr);
589 0 : DiscardTexture(mBackBufferOnWhite, mAllocator);
590 0 : mBackBufferOnWhite = nullptr;
591 : }
592 0 : }
593 :
594 : static already_AddRefed<TextureClient>
595 0 : CreateBackBufferTexture(TextureClient* aCurrentTexture,
596 : CompositableClient& aCompositable,
597 : TextureClientAllocator* aAllocator)
598 : {
599 0 : if (aCurrentTexture) {
600 : // Our current back-buffer is still locked by the compositor. This can occur
601 : // when the client is producing faster than the compositor can consume. In
602 : // this case we just want to drop it and not return it to the pool.
603 0 : aAllocator->ReportClientLost();
604 : }
605 :
606 0 : RefPtr<TextureClient> texture = aAllocator->GetTextureClient();
607 :
608 0 : if (!texture) {
609 0 : gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
610 0 : return nullptr;
611 : }
612 :
613 0 : texture->EnableReadLock();
614 :
615 0 : if (!aCompositable.AddTextureClient(texture)) {
616 0 : gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
617 0 : return nullptr;
618 : }
619 :
620 0 : return texture.forget();
621 : }
622 :
623 : TextureClient*
624 0 : TileClient::GetBackBuffer(CompositableClient& aCompositable,
625 : const nsIntRegion& aDirtyRegion,
626 : gfxContentType aContent,
627 : SurfaceMode aMode,
628 : nsIntRegion& aAddPaintedRegion,
629 : RefPtr<TextureClient>* aBackBufferOnWhite)
630 : {
631 0 : if (!mAllocator) {
632 0 : gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
633 0 : return nullptr;
634 : }
635 0 : if (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA) {
636 : // It can happen that a component-alpha layer stops being on component alpha
637 : // on the next frame, just drop the buffers on white if that happens.
638 0 : if (mBackBufferOnWhite) {
639 0 : mAllocator->ReportClientLost();
640 0 : mBackBufferOnWhite = nullptr;
641 : }
642 0 : if (mFrontBufferOnWhite) {
643 0 : mAllocator->ReportClientLost();
644 0 : mFrontBufferOnWhite = nullptr;
645 : }
646 : }
647 :
648 : // Try to re-use the front-buffer if possible
649 0 : if (mFrontBuffer &&
650 0 : mFrontBuffer->HasIntermediateBuffer() &&
651 0 : !mFrontBuffer->IsReadLocked() &&
652 0 : (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA || (
653 0 : mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked()))) {
654 : // If we had a backbuffer we no longer care about it since we'll
655 : // re-use the front buffer.
656 0 : DiscardBackBuffer();
657 0 : Flip();
658 : } else {
659 0 : if (!mBackBuffer || mBackBuffer->IsReadLocked()) {
660 0 : mBackBuffer.Set(this,
661 0 : CreateBackBufferTexture(mBackBuffer, aCompositable, mAllocator)
662 0 : );
663 0 : if (!mBackBuffer) {
664 0 : DiscardBackBuffer();
665 0 : DiscardFrontBuffer();
666 0 : return nullptr;
667 : }
668 0 : mInvalidBack = IntRect(IntPoint(), mBackBuffer->GetSize());
669 : }
670 :
671 0 : if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA
672 0 : && (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked())) {
673 0 : mBackBufferOnWhite = CreateBackBufferTexture(
674 : mBackBufferOnWhite, aCompositable, mAllocator
675 0 : );
676 0 : if (!mBackBufferOnWhite) {
677 0 : DiscardBackBuffer();
678 0 : DiscardFrontBuffer();
679 0 : return nullptr;
680 : }
681 0 : mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
682 : }
683 :
684 0 : ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion);
685 : }
686 :
687 0 : if (!mBackBuffer->IsLocked()) {
688 0 : if (!mBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
689 0 : gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
690 0 : DiscardBackBuffer();
691 0 : DiscardFrontBuffer();
692 0 : return nullptr;
693 : }
694 : }
695 :
696 0 : if (mBackBufferOnWhite && !mBackBufferOnWhite->IsLocked()) {
697 0 : if (!mBackBufferOnWhite->Lock(OpenMode::OPEN_READ_WRITE)) {
698 0 : gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
699 0 : DiscardBackBuffer();
700 0 : DiscardFrontBuffer();
701 0 : return nullptr;
702 : }
703 : }
704 :
705 0 : *aBackBufferOnWhite = mBackBufferOnWhite;
706 0 : return mBackBuffer;
707 : }
708 :
709 : TileDescriptor
710 0 : TileClient::GetTileDescriptor()
711 : {
712 0 : if (IsPlaceholderTile()) {
713 0 : mWasPlaceholder = true;
714 0 : return PlaceholderTileDescriptor();
715 : }
716 0 : bool wasPlaceholder = mWasPlaceholder;
717 0 : mWasPlaceholder = false;
718 :
719 0 : ReadLockDescriptor lock;
720 0 : mFrontBuffer->SerializeReadLock(lock);
721 :
722 0 : ReadLockDescriptor lockOnWhite = null_t();
723 0 : if (mFrontBufferOnWhite) {
724 0 : mFrontBufferOnWhite->SerializeReadLock(lockOnWhite);
725 : }
726 :
727 0 : return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
728 0 : mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
729 : mUpdateRect,
730 : lock, lockOnWhite,
731 0 : wasPlaceholder);
732 : }
733 :
734 : void
735 0 : ClientMultiTiledLayerBuffer::DiscardBuffers()
736 : {
737 0 : for (TileClient& tile : mRetainedTiles) {
738 0 : tile.DiscardBuffers();
739 : }
740 0 : }
741 :
742 : SurfaceDescriptorTiles
743 0 : ClientMultiTiledLayerBuffer::GetSurfaceDescriptorTiles()
744 : {
745 0 : InfallibleTArray<TileDescriptor> tiles;
746 :
747 0 : for (TileClient& tile : mRetainedTiles) {
748 0 : TileDescriptor tileDesc = tile.GetTileDescriptor();
749 0 : tiles.AppendElement(tileDesc);
750 : // Reset the update rect
751 0 : tile.mUpdateRect = IntRect();
752 : }
753 : return SurfaceDescriptorTiles(mValidRegion,
754 : tiles,
755 : mTileOrigin, mTileSize,
756 : mTiles.mFirst.x, mTiles.mFirst.y,
757 : mTiles.mSize.width, mTiles.mSize.height,
758 : mResolution, mFrameResolution.xScale,
759 : mFrameResolution.yScale,
760 0 : mWasLastPaintProgressive);
761 : }
762 :
763 : void
764 0 : ClientMultiTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
765 : const nsIntRegion& aPaintRegion,
766 : const nsIntRegion& aDirtyRegion,
767 : LayerManager::DrawPaintedLayerCallback aCallback,
768 : void* aCallbackData,
769 : bool aIsProgressive)
770 : {
771 : TILING_LOG("TILING %p: PaintThebes painting region %s\n", &mPaintedLayer, Stringify(aPaintRegion).c_str());
772 : TILING_LOG("TILING %p: PaintThebes new valid region %s\n", &mPaintedLayer, Stringify(aNewValidRegion).c_str());
773 :
774 0 : mCallback = aCallback;
775 0 : mCallbackData = aCallbackData;
776 0 : mWasLastPaintProgressive = aIsProgressive;
777 :
778 : #ifdef GFX_TILEDLAYER_PREF_WARNINGS
779 : long start = PR_IntervalNow();
780 : #endif
781 :
782 : #ifdef GFX_TILEDLAYER_PREF_WARNINGS
783 : if (PR_IntervalNow() - start > 30) {
784 : const IntRect bounds = aPaintRegion.GetBounds();
785 : printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
786 : if (aPaintRegion.IsComplex()) {
787 : printf_stderr("Complex region\n");
788 : for (auto iter = aPaintRegion.RectIter(); !iter.Done(); iter.Next()) {
789 : const IntRect& rect = iter.Get();
790 : printf_stderr(" rect %i, %i, %i, %i\n",
791 : rect.x, rect.y, rect.width, rect.height);
792 : }
793 : }
794 : }
795 : start = PR_IntervalNow();
796 : #endif
797 :
798 0 : AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::PaintThebes", GRAPHICS);
799 :
800 0 : mNewValidRegion = aNewValidRegion;
801 0 : Update(aNewValidRegion, aPaintRegion, aDirtyRegion);
802 :
803 : #ifdef GFX_TILEDLAYER_PREF_WARNINGS
804 : if (PR_IntervalNow() - start > 10) {
805 : const IntRect bounds = aPaintRegion.GetBounds();
806 : printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
807 : }
808 : #endif
809 :
810 0 : mLastPaintContentType = GetContentType(&mLastPaintSurfaceMode);
811 0 : mCallback = nullptr;
812 0 : mCallbackData = nullptr;
813 0 : }
814 :
815 0 : void PadDrawTargetOutFromRegion(RefPtr<DrawTarget> drawTarget, nsIntRegion ®ion)
816 : {
817 0 : struct LockedBits {
818 : uint8_t *data;
819 : IntSize size;
820 : int32_t stride;
821 : SurfaceFormat format;
822 0 : static int clamp(int x, int min, int max)
823 : {
824 0 : if (x < min)
825 0 : x = min;
826 0 : if (x > max)
827 0 : x = max;
828 0 : return x;
829 : }
830 :
831 0 : static void ensure_memcpy(uint8_t *dst, uint8_t *src, size_t n, uint8_t *bitmap, int stride, int height)
832 : {
833 0 : if (src + n > bitmap + stride*height) {
834 0 : MOZ_CRASH("GFX: long src memcpy");
835 : }
836 0 : if (src < bitmap) {
837 0 : MOZ_CRASH("GFX: short src memcpy");
838 : }
839 0 : if (dst + n > bitmap + stride*height) {
840 0 : MOZ_CRASH("GFX: long dst mempcy");
841 : }
842 0 : if (dst < bitmap) {
843 0 : MOZ_CRASH("GFX: short dst mempcy");
844 : }
845 0 : }
846 :
847 0 : static void visitor(void *closure, VisitSide side, int x1, int y1, int x2, int y2) {
848 0 : LockedBits *lb = static_cast<LockedBits*>(closure);
849 0 : uint8_t *bitmap = lb->data;
850 0 : const int bpp = gfx::BytesPerPixel(lb->format);
851 0 : const int stride = lb->stride;
852 0 : const int width = lb->size.width;
853 0 : const int height = lb->size.height;
854 :
855 0 : if (side == VisitSide::TOP) {
856 0 : if (y1 > 0) {
857 0 : x1 = clamp(x1, 0, width - 1);
858 0 : x2 = clamp(x2, 0, width - 1);
859 0 : ensure_memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp, bitmap, stride, height);
860 0 : memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp);
861 : }
862 0 : } else if (side == VisitSide::BOTTOM) {
863 0 : if (y1 < height) {
864 0 : x1 = clamp(x1, 0, width - 1);
865 0 : x2 = clamp(x2, 0, width - 1);
866 0 : ensure_memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp, bitmap, stride, height);
867 0 : memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp);
868 : }
869 0 : } else if (side == VisitSide::LEFT) {
870 0 : if (x1 > 0) {
871 0 : while (y1 != y2) {
872 0 : memcpy(&bitmap[(x1-1)*bpp + y1 * stride], &bitmap[x1*bpp + y1*stride], bpp);
873 0 : y1++;
874 : }
875 : }
876 0 : } else if (side == VisitSide::RIGHT) {
877 0 : if (x1 < width) {
878 0 : while (y1 != y2) {
879 0 : memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[(x1-1)*bpp + y1*stride], bpp);
880 0 : y1++;
881 : }
882 : }
883 : }
884 :
885 0 : }
886 0 : } lb;
887 :
888 0 : if (drawTarget->LockBits(&lb.data, &lb.size, &lb.stride, &lb.format)) {
889 : // we can only pad software targets so if we can't lock the bits don't pad
890 0 : region.VisitEdges(lb.visitor, &lb);
891 0 : drawTarget->ReleaseBits(lb.data);
892 : }
893 0 : }
894 :
895 : void
896 0 : ClientTiledLayerBuffer::UnlockTile(TileClient& aTile)
897 : {
898 : // We locked the back buffer, and flipped so we now need to unlock the front
899 0 : if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
900 0 : aTile.mFrontBuffer->Unlock();
901 0 : aTile.mFrontBuffer->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
902 : }
903 0 : if (aTile.mFrontBufferOnWhite && aTile.mFrontBufferOnWhite->IsLocked()) {
904 0 : aTile.mFrontBufferOnWhite->Unlock();
905 0 : aTile.mFrontBufferOnWhite->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
906 : }
907 0 : if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
908 0 : aTile.mBackBuffer->Unlock();
909 : }
910 0 : if (aTile.mBackBufferOnWhite && aTile.mBackBufferOnWhite->IsLocked()) {
911 0 : aTile.mBackBufferOnWhite->Unlock();
912 : }
913 0 : }
914 :
915 0 : void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
916 : const nsIntRegion& aPaintRegion,
917 : const nsIntRegion& aDirtyRegion)
918 : {
919 0 : const IntSize scaledTileSize = GetScaledTileSize();
920 0 : const gfx::IntRect newBounds = newValidRegion.GetBounds();
921 :
922 0 : const TilesPlacement oldTiles = mTiles;
923 0 : const TilesPlacement newTiles(floor_div(newBounds.x, scaledTileSize.width),
924 0 : floor_div(newBounds.y, scaledTileSize.height),
925 0 : floor_div(GetTileStart(newBounds.x, scaledTileSize.width)
926 0 : + newBounds.width, scaledTileSize.width) + 1,
927 0 : floor_div(GetTileStart(newBounds.y, scaledTileSize.height)
928 0 : + newBounds.height, scaledTileSize.height) + 1);
929 :
930 0 : const size_t oldTileCount = mRetainedTiles.Length();
931 0 : const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height;
932 :
933 0 : nsTArray<TileClient> oldRetainedTiles;
934 0 : mRetainedTiles.SwapElements(oldRetainedTiles);
935 0 : mRetainedTiles.SetLength(newTileCount);
936 :
937 0 : for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) {
938 0 : const TileIntPoint tilePosition = oldTiles.TilePosition(oldIndex);
939 0 : const size_t newIndex = newTiles.TileIndex(tilePosition);
940 : // First, get the already existing tiles to the right place in the new array.
941 : // Leave placeholders (default constructor) where there was no tile.
942 0 : if (newTiles.HasTile(tilePosition)) {
943 0 : mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex];
944 : } else {
945 : // release tiles that we are not going to reuse before allocating new ones
946 : // to avoid allocating unnecessarily.
947 0 : oldRetainedTiles[oldIndex].DiscardBuffers();
948 : }
949 : }
950 :
951 0 : oldRetainedTiles.Clear();
952 :
953 0 : if (!aPaintRegion.IsEmpty()) {
954 0 : for (size_t i = 0; i < newTileCount; ++i) {
955 0 : const TileIntPoint tilePosition = newTiles.TilePosition(i);
956 :
957 0 : IntPoint tileOffset = GetTileOffset(tilePosition);
958 0 : nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
959 0 : tileDrawRegion.AndWith(aPaintRegion);
960 :
961 0 : if (tileDrawRegion.IsEmpty()) {
962 0 : continue;
963 : }
964 :
965 0 : TileClient& tile = mRetainedTiles[i];
966 0 : if (!ValidateTile(tile, GetTileOffset(tilePosition), tileDrawRegion)) {
967 0 : gfxCriticalError() << "ValidateTile failed";
968 : }
969 : }
970 :
971 0 : if (mMoz2DTiles.size() > 0) {
972 : gfx::TileSet tileset;
973 0 : for (size_t i = 0; i < mMoz2DTiles.size(); ++i) {
974 0 : mMoz2DTiles[i].mTileOrigin -= mTilingOrigin;
975 : }
976 0 : tileset.mTiles = &mMoz2DTiles[0];
977 0 : tileset.mTileCount = mMoz2DTiles.size();
978 0 : RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
979 0 : if (!drawTarget || !drawTarget->IsValid()) {
980 0 : gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
981 0 : return;
982 : }
983 0 : drawTarget->SetTransform(Matrix());
984 :
985 0 : RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
986 0 : MOZ_ASSERT(ctx); // already checked the draw target above
987 0 : ctx->SetMatrix(
988 0 : ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(ThebesPoint(-mTilingOrigin)));
989 :
990 0 : mCallback(&mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
991 0 : DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
992 0 : mMoz2DTiles.clear();
993 : // Reset:
994 0 : mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
995 0 : std::numeric_limits<int32_t>::max());
996 : }
997 :
998 0 : bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
999 :
1000 0 : for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
1001 0 : TileClient& tile = mRetainedTiles[i];
1002 :
1003 : // Only worry about padding when not doing low-res because it simplifies
1004 : // the math and the artifacts won't be noticable
1005 : // Edge padding prevents sampling artifacts when compositing.
1006 0 : if (edgePaddingEnabled && mResolution == 1 &&
1007 0 : tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) {
1008 :
1009 0 : const TileIntPoint tilePosition = newTiles.TilePosition(i);
1010 0 : IntPoint tileOffset = GetTileOffset(tilePosition);
1011 : // Strictly speakig we want the unscaled rect here, but it doesn't matter
1012 : // because we only run this code when the resolution is equal to 1.
1013 : IntRect tileRect = IntRect(tileOffset.x, tileOffset.y,
1014 0 : GetTileSize().width, GetTileSize().height);
1015 :
1016 0 : nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
1017 0 : tileDrawRegion.AndWith(aPaintRegion);
1018 :
1019 0 : nsIntRegion tileValidRegion = mValidRegion;
1020 0 : tileValidRegion.OrWith(tileDrawRegion);
1021 :
1022 : // We only need to pad out if the tile has area that's not valid
1023 0 : if (!tileValidRegion.Contains(tileRect)) {
1024 0 : tileValidRegion = tileValidRegion.Intersect(tileRect);
1025 : // translate the region into tile space and pad
1026 0 : tileValidRegion.MoveBy(-IntPoint(tileOffset.x, tileOffset.y));
1027 0 : RefPtr<DrawTarget> drawTarget = tile.mFrontBuffer->BorrowDrawTarget();
1028 0 : PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
1029 : }
1030 : }
1031 0 : UnlockTile(tile);
1032 : }
1033 : }
1034 :
1035 0 : mTiles = newTiles;
1036 0 : mValidRegion = newValidRegion;
1037 0 : mPaintedRegion.OrWith(aPaintRegion);
1038 : }
1039 :
1040 : bool
1041 0 : ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
1042 : const nsIntPoint& aTileOrigin,
1043 : const nsIntRegion& aDirtyRegion)
1044 : {
1045 0 : AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::ValidateTile", GRAPHICS);
1046 :
1047 : #ifdef GFX_TILEDLAYER_PREF_WARNINGS
1048 : if (aDirtyRegion.IsComplex()) {
1049 : printf_stderr("Complex region\n");
1050 : }
1051 : #endif
1052 :
1053 : SurfaceMode mode;
1054 0 : gfxContentType content = GetContentType(&mode);
1055 :
1056 0 : if (!aTile.mAllocator) {
1057 0 : aTile.SetTextureAllocator(mManager->GetCompositorBridgeChild()->GetTexturePool(
1058 0 : mManager->AsShadowForwarder(),
1059 0 : gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content),
1060 0 : TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD));
1061 0 : MOZ_ASSERT(aTile.mAllocator);
1062 : }
1063 :
1064 0 : nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
1065 0 : offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
1066 :
1067 0 : nsIntRegion extraPainted;
1068 0 : RefPtr<TextureClient> backBufferOnWhite;
1069 : RefPtr<TextureClient> backBuffer =
1070 : aTile.GetBackBuffer(mCompositableClient,
1071 : offsetScaledDirtyRegion,
1072 : content, mode,
1073 : extraPainted,
1074 0 : &backBufferOnWhite);
1075 :
1076 0 : aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
1077 :
1078 0 : extraPainted.MoveBy(aTileOrigin);
1079 0 : extraPainted.And(extraPainted, mNewValidRegion);
1080 0 : mPaintedRegion.Or(mPaintedRegion, extraPainted);
1081 :
1082 0 : if (!backBuffer) {
1083 0 : return false;
1084 : }
1085 :
1086 0 : gfx::Tile moz2DTile;
1087 0 : RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
1088 0 : RefPtr<DrawTarget> dtOnWhite;
1089 0 : if (backBufferOnWhite) {
1090 0 : dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
1091 0 : moz2DTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
1092 : } else {
1093 0 : moz2DTile.mDrawTarget = dt;
1094 : }
1095 0 : moz2DTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
1096 0 : if (!dt || (backBufferOnWhite && !dtOnWhite)) {
1097 0 : aTile.DiscardBuffers();
1098 0 : return false;
1099 : }
1100 :
1101 0 : mMoz2DTiles.push_back(moz2DTile);
1102 0 : mTilingOrigin.x = std::min(mTilingOrigin.x, moz2DTile.mTileOrigin.x);
1103 0 : mTilingOrigin.y = std::min(mTilingOrigin.y, moz2DTile.mTileOrigin.y);
1104 :
1105 0 : for (auto iter = aDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
1106 0 : const IntRect& dirtyRect = iter.Get();
1107 0 : gfx::Rect drawRect(dirtyRect.x - aTileOrigin.x,
1108 0 : dirtyRect.y - aTileOrigin.y,
1109 0 : dirtyRect.width,
1110 0 : dirtyRect.height);
1111 0 : drawRect.Scale(mResolution);
1112 :
1113 : // Mark the newly updated area as invalid in the front buffer
1114 0 : aTile.mInvalidFront.Or(aTile.mInvalidFront, IntRect::RoundOut(drawRect));
1115 :
1116 0 : if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
1117 0 : dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
1118 0 : dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
1119 0 : } else if (content == gfxContentType::COLOR_ALPHA) {
1120 0 : dt->ClearRect(drawRect);
1121 : }
1122 : }
1123 :
1124 : // The new buffer is now validated, remove the dirty region from it.
1125 0 : aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);
1126 :
1127 0 : aTile.Flip();
1128 :
1129 0 : return true;
1130 : }
1131 :
1132 : /**
1133 : * This function takes the transform stored in aTransformToCompBounds
1134 : * (which was generated in GetTransformToAncestorsParentLayer), and
1135 : * modifies it with the ViewTransform from the compositor side so that
1136 : * it reflects what the compositor is actually rendering. This operation
1137 : * basically adds in the layer's async transform.
1138 : * This function then returns the scroll ancestor's composition bounds,
1139 : * transformed into the painted layer's LayerPixel coordinates, accounting
1140 : * for the compositor state.
1141 : */
1142 : static Maybe<LayerRect>
1143 0 : GetCompositorSideCompositionBounds(const LayerMetricsWrapper& aScrollAncestor,
1144 : const LayerToParentLayerMatrix4x4& aTransformToCompBounds,
1145 : const AsyncTransform& aAPZTransform,
1146 : const LayerRect& aClip)
1147 : {
1148 : LayerToParentLayerMatrix4x4 transform = aTransformToCompBounds *
1149 0 : AsyncTransformComponentMatrix(aAPZTransform);
1150 :
1151 0 : return UntransformBy(transform.Inverse(),
1152 0 : aScrollAncestor.Metrics().GetCompositionBounds(), aClip);
1153 : }
1154 :
1155 : bool
1156 0 : ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
1157 : const nsIntRegion& aOldValidRegion,
1158 : nsIntRegion& aRegionToPaint,
1159 : BasicTiledLayerPaintData* aPaintData,
1160 : bool aIsRepeated)
1161 : {
1162 0 : aRegionToPaint = aInvalidRegion;
1163 :
1164 : // If the composition bounds rect is empty, we can't make any sensible
1165 : // decision about how to update coherently. In this case, just update
1166 : // everything in one transaction.
1167 0 : if (aPaintData->mCompositionBounds.IsEmpty()) {
1168 0 : aPaintData->mPaintFinished = true;
1169 0 : return false;
1170 : }
1171 :
1172 : // If this is a low precision buffer, we force progressive updates. The
1173 : // assumption is that the contents is less important, so visual coherency
1174 : // is lower priority than speed.
1175 0 : bool drawingLowPrecision = IsLowPrecision();
1176 :
1177 : // Find out if we have any non-stale content to update.
1178 0 : nsIntRegion staleRegion;
1179 0 : staleRegion.And(aInvalidRegion, aOldValidRegion);
1180 :
1181 : TILING_LOG("TILING %p: Progressive update stale region %s\n", &mPaintedLayer, Stringify(staleRegion).c_str());
1182 :
1183 0 : LayerMetricsWrapper scrollAncestor;
1184 0 : mPaintedLayer.GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
1185 :
1186 : // Find out the current view transform to determine which tiles to draw
1187 : // first, and see if we should just abort this paint. Aborting is usually
1188 : // caused by there being an incoming, more relevant paint.
1189 0 : AsyncTransform viewTransform;
1190 0 : MOZ_ASSERT(mSharedFrameMetricsHelper);
1191 :
1192 : bool abortPaint =
1193 0 : mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics(
1194 : scrollAncestor,
1195 0 : !staleRegion.Contains(aInvalidRegion),
1196 : drawingLowPrecision,
1197 0 : viewTransform);
1198 :
1199 : TILING_LOG("TILING %p: Progressive update view transform %s zoom %f abort %d\n",
1200 : &mPaintedLayer, ToString(viewTransform.mTranslation).c_str(), viewTransform.mScale.scale, abortPaint);
1201 :
1202 0 : if (abortPaint) {
1203 : // We ignore if front-end wants to abort if this is the first,
1204 : // non-low-precision paint, as in that situation, we're about to override
1205 : // front-end's page/viewport metrics.
1206 0 : if (!aPaintData->mFirstPaint || drawingLowPrecision) {
1207 0 : AUTO_PROFILER_LABEL(
1208 : "ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion",
1209 : GRAPHICS);
1210 :
1211 0 : aRegionToPaint.SetEmpty();
1212 0 : return aIsRepeated;
1213 : }
1214 : }
1215 :
1216 : Maybe<LayerRect> transformedCompositionBounds =
1217 : GetCompositorSideCompositionBounds(scrollAncestor,
1218 : aPaintData->mTransformToCompBounds,
1219 : viewTransform,
1220 0 : LayerRect(mPaintedLayer.GetVisibleRegion().GetBounds()));
1221 :
1222 0 : if (!transformedCompositionBounds) {
1223 0 : aPaintData->mPaintFinished = true;
1224 0 : return false;
1225 : }
1226 :
1227 : TILING_LOG("TILING %p: Progressive update transformed compositor bounds %s\n", &mPaintedLayer, Stringify(*transformedCompositionBounds).c_str());
1228 :
1229 : // Compute a "coherent update rect" that we should paint all at once in a
1230 : // single transaction. This is to avoid rendering glitches on animated
1231 : // page content, and when layers change size/shape.
1232 : // On Fennec uploads are more expensive because we're not using gralloc, so
1233 : // we use a coherent update rect that is intersected with the screen at the
1234 : // time of issuing the draw command. This will paint faster but also potentially
1235 : // make the progressive paint more visible to the user while scrolling.
1236 0 : IntRect coherentUpdateRect(RoundedOut(
1237 : #ifdef MOZ_WIDGET_ANDROID
1238 : transformedCompositionBounds->Intersect(aPaintData->mCompositionBounds)
1239 : #else
1240 0 : *transformedCompositionBounds
1241 : #endif
1242 0 : ).ToUnknownRect());
1243 :
1244 : TILING_LOG("TILING %p: Progressive update final coherency rect %s\n", &mPaintedLayer, Stringify(coherentUpdateRect).c_str());
1245 :
1246 0 : aRegionToPaint.And(aInvalidRegion, coherentUpdateRect);
1247 0 : aRegionToPaint.Or(aRegionToPaint, staleRegion);
1248 0 : bool drawingStale = !aRegionToPaint.IsEmpty();
1249 0 : if (!drawingStale) {
1250 0 : aRegionToPaint = aInvalidRegion;
1251 : }
1252 :
1253 : // Prioritise tiles that are currently visible on the screen.
1254 0 : bool paintingVisible = false;
1255 0 : if (aRegionToPaint.Intersects(coherentUpdateRect)) {
1256 0 : aRegionToPaint.And(aRegionToPaint, coherentUpdateRect);
1257 0 : paintingVisible = true;
1258 : }
1259 :
1260 : TILING_LOG("TILING %p: Progressive update final paint region %s\n", &mPaintedLayer, Stringify(aRegionToPaint).c_str());
1261 :
1262 : // Paint area that's visible and overlaps previously valid content to avoid
1263 : // visible glitches in animated elements, such as gifs.
1264 0 : bool paintInSingleTransaction = paintingVisible && (drawingStale || aPaintData->mFirstPaint);
1265 :
1266 : TILING_LOG("TILING %p: paintingVisible %d drawingStale %d firstPaint %d singleTransaction %d\n",
1267 : &mPaintedLayer, paintingVisible, drawingStale, aPaintData->mFirstPaint, paintInSingleTransaction);
1268 :
1269 : // The following code decides what order to draw tiles in, based on the
1270 : // current scroll direction of the primary scrollable layer.
1271 0 : NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!");
1272 0 : IntRect paintBounds = aRegionToPaint.GetBounds();
1273 :
1274 : int startX, incX, startY, incY;
1275 0 : gfx::IntSize scaledTileSize = GetScaledTileSize();
1276 0 : if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) {
1277 0 : startX = RoundDownToTileEdge(paintBounds.x, scaledTileSize.width);
1278 0 : incX = scaledTileSize.width;
1279 : } else {
1280 0 : startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width);
1281 0 : incX = -scaledTileSize.width;
1282 : }
1283 :
1284 0 : if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) {
1285 0 : startY = RoundDownToTileEdge(paintBounds.y, scaledTileSize.height);
1286 0 : incY = scaledTileSize.height;
1287 : } else {
1288 0 : startY = RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height);
1289 0 : incY = -scaledTileSize.height;
1290 : }
1291 :
1292 : // Find a tile to draw.
1293 0 : IntRect tileBounds(startX, startY, scaledTileSize.width, scaledTileSize.height);
1294 0 : int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x;
1295 0 : int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y;
1296 : // This loop will always terminate, as there is at least one tile area
1297 : // along the first/last row/column intersecting with regionToPaint, or its
1298 : // bounds would have been smaller.
1299 : while (true) {
1300 0 : aRegionToPaint.And(aInvalidRegion, tileBounds);
1301 0 : if (!aRegionToPaint.IsEmpty()) {
1302 0 : if (mResolution != 1) {
1303 : // Paint the entire tile for low-res. This is aimed to fixing low-res resampling
1304 : // and to avoid doing costly region accurate painting for a small area.
1305 0 : aRegionToPaint = tileBounds;
1306 : }
1307 0 : break;
1308 : }
1309 0 : if (Abs(scrollDiffY) >= Abs(scrollDiffX)) {
1310 0 : tileBounds.x += incX;
1311 : } else {
1312 0 : tileBounds.y += incY;
1313 : }
1314 : }
1315 :
1316 0 : if (!aRegionToPaint.Contains(aInvalidRegion)) {
1317 : // The region needed to paint is larger then our progressive chunk size
1318 : // therefore update what we want to paint and ask for a new paint transaction.
1319 :
1320 : // If we need to draw more than one tile to maintain coherency, make
1321 : // sure it happens in the same transaction by requesting this work be
1322 : // repeated immediately.
1323 : // If this is unnecessary, the remaining work will be done tile-by-tile in
1324 : // subsequent transactions. The caller code is responsible for scheduling
1325 : // the subsequent transactions as long as we don't set the mPaintFinished
1326 : // flag to true.
1327 0 : return (!drawingLowPrecision && paintInSingleTransaction);
1328 : }
1329 :
1330 : // We're not repeating painting and we've not requested a repeat transaction,
1331 : // so the paint is finished. If there's still a separate low precision
1332 : // paint to do, it will get marked as unfinished later.
1333 0 : aPaintData->mPaintFinished = true;
1334 0 : return false;
1335 : }
1336 :
1337 : bool
1338 0 : ClientMultiTiledLayerBuffer::ProgressiveUpdate(const nsIntRegion& aValidRegion,
1339 : const nsIntRegion& aInvalidRegion,
1340 : const nsIntRegion& aOldValidRegion,
1341 : nsIntRegion& aOutDrawnRegion,
1342 : BasicTiledLayerPaintData* aPaintData,
1343 : LayerManager::DrawPaintedLayerCallback aCallback,
1344 : void* aCallbackData)
1345 : {
1346 : TILING_LOG("TILING %p: Progressive update valid region %s\n", &mPaintedLayer, Stringify(aValidRegion).c_str());
1347 : TILING_LOG("TILING %p: Progressive update invalid region %s\n", &mPaintedLayer, Stringify(aInvalidRegion).c_str());
1348 : TILING_LOG("TILING %p: Progressive update old valid region %s\n", &mPaintedLayer, Stringify(aOldValidRegion).c_str());
1349 :
1350 0 : bool repeat = false;
1351 0 : bool isBufferChanged = false;
1352 0 : nsIntRegion remainingInvalidRegion = aInvalidRegion;
1353 0 : nsIntRegion updatedValidRegion = aValidRegion;
1354 0 : do {
1355 : // Compute the region that should be updated. Repeat as many times as
1356 : // is required.
1357 0 : nsIntRegion regionToPaint;
1358 0 : repeat = ComputeProgressiveUpdateRegion(remainingInvalidRegion,
1359 : aOldValidRegion,
1360 : regionToPaint,
1361 : aPaintData,
1362 0 : repeat);
1363 :
1364 : TILING_LOG("TILING %p: Progressive update computed paint region %s repeat %d\n", &mPaintedLayer, Stringify(regionToPaint).c_str(), repeat);
1365 :
1366 : // There's no further work to be done.
1367 0 : if (regionToPaint.IsEmpty()) {
1368 0 : break;
1369 : }
1370 :
1371 0 : isBufferChanged = true;
1372 :
1373 : // Keep track of what we're about to refresh.
1374 0 : aOutDrawnRegion.OrWith(regionToPaint);
1375 0 : updatedValidRegion.OrWith(regionToPaint);
1376 :
1377 : // aValidRegion may have been altered by InvalidateRegion, but we still
1378 : // want to display stale content until it gets progressively updated.
1379 : // Create a region that includes stale content.
1380 0 : nsIntRegion validOrStale;
1381 0 : validOrStale.Or(updatedValidRegion, aOldValidRegion);
1382 :
1383 : // Paint the computed region and subtract it from the invalid region.
1384 : PaintThebes(validOrStale, regionToPaint, remainingInvalidRegion,
1385 0 : aCallback, aCallbackData, true);
1386 0 : remainingInvalidRegion.SubOut(regionToPaint);
1387 : } while (repeat);
1388 :
1389 : TILING_LOG("TILING %p: Progressive update final valid region %s buffer changed %d\n", &mPaintedLayer, Stringify(updatedValidRegion).c_str(), isBufferChanged);
1390 : TILING_LOG("TILING %p: Progressive update final invalid region %s\n", &mPaintedLayer, Stringify(remainingInvalidRegion).c_str());
1391 :
1392 : // Return false if nothing has been drawn, or give what has been drawn
1393 : // to the shadow layer to upload.
1394 0 : return isBufferChanged;
1395 : }
1396 :
1397 : void
1398 0 : TiledContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
1399 : {
1400 0 : aStream << aPrefix;
1401 0 : aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
1402 :
1403 0 : if (profiler_feature_active(ProfilerFeature::DisplayListDump)) {
1404 0 : nsAutoCString pfx(aPrefix);
1405 0 : pfx += " ";
1406 :
1407 0 : Dump(aStream, pfx.get(), false);
1408 : }
1409 0 : }
1410 :
1411 : void
1412 0 : TiledContentClient::Dump(std::stringstream& aStream,
1413 : const char* aPrefix,
1414 : bool aDumpHtml,
1415 : TextureDumpMode aCompress)
1416 : {
1417 0 : GetTiledBuffer()->Dump(aStream, aPrefix, aDumpHtml, aCompress);
1418 0 : }
1419 :
1420 : void
1421 0 : BasicTiledLayerPaintData::ResetPaintData()
1422 : {
1423 0 : mLowPrecisionPaintCount = 0;
1424 0 : mPaintFinished = false;
1425 0 : mHasTransformAnimation = false;
1426 0 : mCompositionBounds.SetEmpty();
1427 0 : mCriticalDisplayPort = Nothing();
1428 0 : }
1429 :
1430 : } // namespace layers
1431 : } // namespace mozilla
|