gfx/layers/client/TiledContentClient.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial