gfx/layers/client/TiledContentClient.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial