gfx/layers/client/ClientTiledThebesLayer.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.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "ClientTiledThebesLayer.h"
     6 #include "FrameMetrics.h"               // for FrameMetrics
     7 #include "Units.h"                      // for ScreenIntRect, CSSPoint, etc
     8 #include "UnitTransforms.h"             // for TransformTo
     9 #include "ClientLayerManager.h"         // for ClientLayerManager, etc
    10 #include "gfx3DMatrix.h"                // for gfx3DMatrix
    11 #include "gfxPlatform.h"                // for gfxPlatform
    12 #include "gfxPrefs.h"                   // for gfxPrefs
    13 #include "gfxRect.h"                    // for gfxRect
    14 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
    15 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
    16 #include "mozilla/gfx/Rect.h"           // for Rect, RectTyped
    17 #include "mozilla/layers/LayersMessages.h"
    18 #include "mozilla/mozalloc.h"           // for operator delete, etc
    19 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
    20 #include "nsRect.h"                     // for nsIntRect
    22 namespace mozilla {
    23 namespace layers {
    26 ClientTiledThebesLayer::ClientTiledThebesLayer(ClientLayerManager* const aManager)
    27   : ThebesLayer(aManager,
    28                 static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST()))
    29   , mContentClient()
    30 {
    31   MOZ_COUNT_CTOR(ClientTiledThebesLayer);
    32   mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0);
    33   mPaintData.mFirstPaint = true;
    34 }
    36 ClientTiledThebesLayer::~ClientTiledThebesLayer()
    37 {
    38   MOZ_COUNT_DTOR(ClientTiledThebesLayer);
    39 }
    41 void
    42 ClientTiledThebesLayer::ClearCachedResources()
    43 {
    44   if (mContentClient) {
    45     mContentClient->ClearCachedResources();
    46   }
    47 }
    49 void
    50 ClientTiledThebesLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
    51 {
    52   aAttrs = ThebesLayerAttributes(GetValidRegion());
    53 }
    55 static LayoutDeviceRect
    56 ApplyParentLayerToLayoutTransform(const gfx3DMatrix& aTransform, const ParentLayerRect& aParentLayerRect)
    57 {
    58   return TransformTo<LayoutDevicePixel>(aTransform, aParentLayerRect);
    59 }
    61 void
    62 ClientTiledThebesLayer::BeginPaint()
    63 {
    64   if (ClientManager()->IsRepeatTransaction()) {
    65     return;
    66   }
    68   mPaintData.mLowPrecisionPaintCount = 0;
    69   mPaintData.mPaintFinished = false;
    70   mPaintData.mCompositionBounds.SetEmpty();
    71   mPaintData.mCriticalDisplayPort.SetEmpty();
    73   if (!GetBaseTransform().Is2DIntegerTranslation()) {
    74     // Give up if the layer is transformed. The code below assumes that there
    75     // is no transform set, and not making that assumption would cause huge
    76     // complication to handle a quite rare case.
    77     //
    78     // FIXME The intention is to bail out of this function when there's a CSS
    79     //       transform set on the layer, but unfortunately there's no way to
    80     //       distinguish transforms due to scrolling from transforms due to
    81     //       CSS transforms.
    82     //
    83     //       Because of this, there may be unintended behaviour when setting
    84     //       2d CSS translations on the children of scrollable displayport
    85     //       layers.
    86     return;
    87   }
    89 #ifdef MOZ_WIDGET_ANDROID
    90   // Subframes on Fennec are not async scrollable because they have no displayport.
    91   // However, the code in RenderLayer() picks up a displayport from the nearest
    92   // scrollable ancestor container layer anyway, which is incorrect for Fennec. This
    93   // behaviour results in the subframe getting clipped improperly and perma-blank areas
    94   // while scrolling the subframe. To work around this, we detect if this layer is
    95   // the primary scrollable layer and disable the tiling behaviour if it is not.
    96   bool isPrimaryScrollableThebesLayer = false;
    97   if (Layer* scrollable = ClientManager()->GetPrimaryScrollableLayer()) {
    98     if (GetParent() == scrollable) {
    99       for (Layer* child = scrollable->GetFirstChild(); child; child = child->GetNextSibling()) {
   100         if (child->GetType() == Layer::TYPE_THEBES) {
   101           if (child == this) {
   102             // |this| is the first thebes layer child of the GetPrimaryScrollableLayer()
   103             isPrimaryScrollableThebesLayer = true;
   104           }
   105           break;
   106         }
   107       }
   108     }
   109   }
   110   if (!isPrimaryScrollableThebesLayer) {
   111     return;
   112   }
   113 #endif
   115   // Get the metrics of the nearest scrollable layer and the nearest layer
   116   // with a displayport.
   117   ContainerLayer* displayPortParent = nullptr;
   118   ContainerLayer* scrollParent = nullptr;
   119   for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
   120     const FrameMetrics& metrics = parent->GetFrameMetrics();
   121     if (!scrollParent && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) {
   122       scrollParent = parent;
   123     }
   124     if (!metrics.mDisplayPort.IsEmpty()) {
   125       displayPortParent = parent;
   126       // Any layer that has a displayport must be scrollable, so we can break
   127       // here.
   128       break;
   129     }
   130   }
   132   if (!displayPortParent || !scrollParent) {
   133     // No displayport or scroll parent, so we can't do progressive rendering.
   134     // Just set the composition bounds to empty and return.
   135 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G)
   136     // Both Android and b2g are guaranteed to have a displayport set, so this
   137     // should never happen.
   138     NS_WARNING("Tiled Thebes layer with no scrollable container parent");
   139 #endif
   140     return;
   141   }
   143   // Note, not handling transformed layers lets us assume that LayoutDevice
   144   // space of the scroll parent layer is the same as LayoutDevice space of
   145   // this layer.
   146   const FrameMetrics& scrollMetrics = scrollParent->GetFrameMetrics();
   147   const FrameMetrics& displayportMetrics = displayPortParent->GetFrameMetrics();
   149   // Calculate the transform required to convert ParentLayer space of our
   150   // display port parent to LayoutDevice space of this layer.
   151   gfx::Matrix4x4 transform = scrollParent->GetTransform();
   152   ContainerLayer* displayPortParentParent = displayPortParent->GetParent() ?
   153     displayPortParent->GetParent()->GetParent() : nullptr;
   154   for (ContainerLayer* parent = scrollParent->GetParent();
   155        parent != displayPortParentParent;
   156        parent = parent->GetParent()) {
   157     transform = transform * parent->GetTransform();
   158   }
   159   gfx3DMatrix layoutDeviceToScrollParentLayer;
   160   gfx::To3DMatrix(transform, layoutDeviceToScrollParentLayer);
   161   layoutDeviceToScrollParentLayer.ScalePost(scrollMetrics.mCumulativeResolution.scale,
   162                                             scrollMetrics.mCumulativeResolution.scale,
   163                                             1.f);
   165   mPaintData.mTransformParentLayerToLayoutDevice = layoutDeviceToScrollParentLayer.Inverse();
   167   // Compute the critical display port of the display port layer in
   168   // LayoutDevice space of this layer.
   169   ParentLayerRect criticalDisplayPort =
   170     (displayportMetrics.mCriticalDisplayPort + displayportMetrics.GetScrollOffset()) *
   171     displayportMetrics.GetZoomToParent();
   172   mPaintData.mCriticalDisplayPort = LayoutDeviceIntRect::ToUntyped(RoundedOut(
   173     ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayoutDevice,
   174                                       criticalDisplayPort)));
   176   // Compute the viewport of the display port layer in LayoutDevice space of
   177   // this layer.
   178   ParentLayerRect viewport =
   179     (displayportMetrics.mViewport + displayportMetrics.GetScrollOffset()) *
   180     displayportMetrics.GetZoomToParent();
   181   mPaintData.mViewport = ApplyParentLayerToLayoutTransform(
   182     mPaintData.mTransformParentLayerToLayoutDevice, viewport);
   184   // Store the scroll parent resolution. Because this is Gecko-side, before any
   185   // async transforms have occurred, we can use the zoom for this.
   186   mPaintData.mResolution = displayportMetrics.GetZoomToParent();
   188   // Store the parent composition bounds in LayoutDevice units.
   189   // This is actually in LayoutDevice units of the scrollParent's parent layer,
   190   // but because there is no transform, we can assume that these are the same.
   191   mPaintData.mCompositionBounds =
   192     scrollMetrics.mCompositionBounds / scrollMetrics.GetParentResolution();
   194   // Calculate the scroll offset since the last transaction
   195   mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoomToParent();
   196 }
   198 void
   199 ClientTiledThebesLayer::EndPaint(bool aFinish)
   200 {
   201   if (!aFinish && !mPaintData.mPaintFinished) {
   202     return;
   203   }
   205   mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
   206   mPaintData.mPaintFinished = true;
   207   mPaintData.mFirstPaint = false;
   208 }
   210 void
   211 ClientTiledThebesLayer::RenderLayer()
   212 {
   213   LayerManager::DrawThebesLayerCallback callback =
   214     ClientManager()->GetThebesLayerCallback();
   215   void *data = ClientManager()->GetThebesLayerCallbackData();
   216   if (!callback) {
   217     ClientManager()->SetTransactionIncomplete();
   218     return;
   219   }
   221   if (!mContentClient) {
   222     mContentClient = new TiledContentClient(this, ClientManager());
   224     mContentClient->Connect();
   225     ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
   226     MOZ_ASSERT(mContentClient->GetForwarder());
   227   }
   229   if (mContentClient->mTiledBuffer.HasFormatChanged()) {
   230     mValidRegion = nsIntRegion();
   231   }
   233   nsIntRegion invalidRegion = mVisibleRegion;
   234   invalidRegion.Sub(invalidRegion, mValidRegion);
   235   if (invalidRegion.IsEmpty()) {
   236     EndPaint(true);
   237     return;
   238   }
   240   // Only paint the mask layer on the first transaction.
   241   if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) {
   242     ToClientLayer(GetMaskLayer())->RenderLayer();
   243   }
   245   bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition();
   247   // Fast path for no progressive updates, no low-precision updates and no
   248   // critical display-port set, or no display-port set, or this is a fixed
   249   // position layer/contained in a fixed position layer
   250   const FrameMetrics& parentMetrics = GetParent()->GetFrameMetrics();
   251   if ((!gfxPrefs::UseProgressiveTilePainting() &&
   252        !gfxPrefs::UseLowPrecisionBuffer() &&
   253        parentMetrics.mCriticalDisplayPort.IsEmpty()) ||
   254        parentMetrics.mDisplayPort.IsEmpty() ||
   255        isFixed) {
   256     mValidRegion = mVisibleRegion;
   258     NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer");
   260     mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion,
   261                                              callback, data);
   263     ClientManager()->Hold(this);
   264     mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);
   266     return;
   267   }
   269   // Calculate everything we need to perform the paint.
   270   BeginPaint();
   271   if (mPaintData.mPaintFinished) {
   272     return;
   273   }
   275   // Make sure that tiles that fall outside of the visible region are
   276   // discarded on the first update.
   277   if (!ClientManager()->IsRepeatTransaction()) {
   278     mValidRegion.And(mValidRegion, mVisibleRegion);
   279     if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
   280       // Make sure that tiles that fall outside of the critical displayport are
   281       // discarded on the first update.
   282       mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort);
   283     }
   284   }
   286   nsIntRegion lowPrecisionInvalidRegion;
   287   if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
   288     if (gfxPrefs::UseLowPrecisionBuffer()) {
   289       // Calculate the invalid region for the low precision buffer
   290       lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion);
   292       // Remove the valid region from the low precision valid region (we don't
   293       // validate this part of the low precision buffer).
   294       lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
   295     }
   297     // Clip the invalid region to the critical display-port
   298     invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort);
   299     if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) {
   300       EndPaint(true);
   301       return;
   302     }
   303   }
   305   if (!invalidRegion.IsEmpty() && mPaintData.mLowPrecisionPaintCount == 0) {
   306     bool updatedBuffer = false;
   307     // Only draw progressively when the resolution is unchanged.
   308     if (gfxPrefs::UseProgressiveTilePainting() &&
   309         !ClientManager()->HasShadowTarget() &&
   310         mContentClient->mTiledBuffer.GetFrameResolution() == mPaintData.mResolution) {
   311       // Store the old valid region, then clear it before painting.
   312       // We clip the old valid region to the visible region, as it only gets
   313       // used to decide stale content (currently valid and previously visible)
   314       nsIntRegion oldValidRegion = mContentClient->mTiledBuffer.GetValidRegion();
   315       oldValidRegion.And(oldValidRegion, mVisibleRegion);
   316       if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
   317         oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort);
   318       }
   320       updatedBuffer =
   321         mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, invalidRegion,
   322                                                        oldValidRegion, &mPaintData,
   323                                                        callback, data);
   324     } else {
   325       updatedBuffer = true;
   326       mValidRegion = mVisibleRegion;
   327       if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
   328         mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort);
   329       }
   330       mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution);
   331       mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion,
   332                                                callback, data);
   333     }
   335     if (updatedBuffer) {
   336       ClientManager()->Hold(this);
   337       mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);
   339       // If there are low precision updates, mark the paint as unfinished and
   340       // request a repeat transaction.
   341       if (!lowPrecisionInvalidRegion.IsEmpty() && mPaintData.mPaintFinished) {
   342         ClientManager()->SetRepeatTransaction();
   343         mPaintData.mLowPrecisionPaintCount = 1;
   344         mPaintData.mPaintFinished = false;
   345       }
   347       // Return so that low precision updates aren't performed in the same
   348       // transaction as high-precision updates.
   349       EndPaint(false);
   350       return;
   351     }
   352   }
   354   // Render the low precision buffer, if there's area to invalidate and the
   355   // visible region is larger than the critical display port.
   356   bool updatedLowPrecision = false;
   357   if (!lowPrecisionInvalidRegion.IsEmpty() &&
   358       !nsIntRegion(mPaintData.mCriticalDisplayPort).Contains(mVisibleRegion)) {
   359     nsIntRegion oldValidRegion =
   360       mContentClient->mLowPrecisionTiledBuffer.GetValidRegion();
   361     oldValidRegion.And(oldValidRegion, mVisibleRegion);
   363     // If the frame resolution or format have changed, invalidate the buffer
   364     if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution ||
   365         mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) {
   366       if (!mLowPrecisionValidRegion.IsEmpty()) {
   367         updatedLowPrecision = true;
   368       }
   369       oldValidRegion.SetEmpty();
   370       mLowPrecisionValidRegion.SetEmpty();
   371       mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution);
   372       lowPrecisionInvalidRegion = mVisibleRegion;
   373     }
   375     // Invalidate previously valid content that is no longer visible
   376     if (mPaintData.mLowPrecisionPaintCount == 1) {
   377       mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, mVisibleRegion);
   378     }
   379     mPaintData.mLowPrecisionPaintCount++;
   381     // Remove the valid high-precision region from the invalid low-precision
   382     // region. We don't want to spend time drawing things twice.
   383     lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
   385     if (!lowPrecisionInvalidRegion.IsEmpty()) {
   386       updatedLowPrecision = mContentClient->mLowPrecisionTiledBuffer
   387                               .ProgressiveUpdate(mLowPrecisionValidRegion,
   388                                                  lowPrecisionInvalidRegion,
   389                                                  oldValidRegion, &mPaintData,
   390                                                  callback, data);
   391     }
   392   } else if (!mLowPrecisionValidRegion.IsEmpty()) {
   393     // Clear the low precision tiled buffer
   394     updatedLowPrecision = true;
   395     mLowPrecisionValidRegion.SetEmpty();
   396     mContentClient->mLowPrecisionTiledBuffer.PaintThebes(mLowPrecisionValidRegion,
   397                                                          mLowPrecisionValidRegion,
   398                                                          callback, data);
   399   }
   401   // We send a Painted callback if we clear the valid region of the low
   402   // precision buffer, so that the shadow buffer's valid region can be updated
   403   // and the associated resources can be freed.
   404   if (updatedLowPrecision) {
   405     ClientManager()->Hold(this);
   406     mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER);
   407   }
   409   EndPaint(false);
   410 }
   412 } // mozilla
   413 } // layers

mercurial