gfx/layers/TiledLayerBuffer.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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 #ifndef GFX_TILEDLAYERBUFFER_H
     6 #define GFX_TILEDLAYERBUFFER_H
     8 // Debug defines
     9 //#define GFX_TILEDLAYER_DEBUG_OVERLAY
    10 //#define GFX_TILEDLAYER_PREF_WARNINGS
    12 #include <stdint.h>                     // for uint16_t, uint32_t
    13 #include <sys/types.h>                  // for int32_t
    14 #include "gfxPrefs.h"                   // for gfxPrefs::LayersTileWidth/Height
    15 #include "nsDebug.h"                    // for NS_ABORT_IF_FALSE
    16 #include "nsPoint.h"                    // for nsIntPoint
    17 #include "nsRect.h"                     // for nsIntRect
    18 #include "nsRegion.h"                   // for nsIntRegion
    19 #include "nsTArray.h"                   // for nsTArray
    21 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
    22 #include <ui/Fence.h>
    23 #endif
    25 namespace mozilla {
    26 namespace layers {
    28 // An abstract implementation of a tile buffer. This code covers the logic of
    29 // moving and reusing tiles and leaves the validation up to the implementor. To
    30 // avoid the overhead of virtual dispatch, we employ the curiously recurring
    31 // template pattern.
    32 //
    33 // Tiles are aligned to a grid with one of the grid points at (0,0) and other
    34 // grid points spaced evenly in the x- and y-directions by GetTileSize()
    35 // multiplied by mResolution. GetScaledTileSize() provides convenience for
    36 // accessing these values.
    37 //
    38 // This tile buffer stores a valid region, which defines the areas that have
    39 // up-to-date content. The contents of tiles within this region will be reused
    40 // from paint to paint. It also stores the region that was modified in the last
    41 // paint operation; this is useful when one tiled layer buffer shadows another
    42 // (as in an off-main-thread-compositing scenario), so that the shadow tiled
    43 // layer buffer can correctly reflect the updates of the master layer buffer.
    44 //
    45 // The associated Tile may be of any type as long as the derived class can
    46 // validate and return tiles of that type. Tiles will be frequently copied, so
    47 // the tile type should be a reference or some other type with an efficient
    48 // copy constructor.
    49 //
    50 // It is required that the derived class specify the base class as a friend. It
    51 // must also implement the following public method:
    52 //
    53 //   Tile GetPlaceholderTile() const;
    54 //
    55 //   Returns a temporary placeholder tile used as a marker. This placeholder tile
    56 //   must never be returned by validateTile and must be == to every instance
    57 //   of a placeholder tile.
    58 //
    59 // Additionally, it must implement the following protected methods:
    60 //
    61 //   Tile ValidateTile(Tile aTile, const nsIntPoint& aTileOrigin,
    62 //                     const nsIntRegion& aDirtyRect);
    63 //
    64 //   Validates the dirtyRect. The returned Tile will replace the tile.
    65 //
    66 //   void ReleaseTile(Tile aTile);
    67 //
    68 //   Destroys the given tile.
    69 //
    70 //   void SwapTiles(Tile& aTileA, Tile& aTileB);
    71 //
    72 //   Swaps two tiles.
    73 //
    74 // The contents of the tile buffer will be rendered at the resolution specified
    75 // in mResolution, which can be altered with SetResolution. The resolution
    76 // should always be a factor of the tile length, to avoid tiles covering
    77 // non-integer amounts of pixels.
    79 template<typename Derived, typename Tile>
    80 class TiledLayerBuffer
    81 {
    82 public:
    83   TiledLayerBuffer()
    84     : mRetainedWidth(0)
    85     , mRetainedHeight(0)
    86     , mResolution(1)
    87     , mTileSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight())
    88   {}
    90   ~TiledLayerBuffer() {}
    92   // Given a tile origin aligned to a multiple of GetScaledTileSize,
    93   // return the tile that describes that region.
    94   // NOTE: To get the valid area of that tile you must intersect
    95   //       (aTileOrigin.x, aTileOrigin.y,
    96   //        GetScaledTileSize().width, GetScaledTileSize().height)
    97   //       and GetValidRegion() to get the area of the tile that is valid.
    98   Tile GetTile(const nsIntPoint& aTileOrigin) const;
   100   // Given a tile x, y relative to the top left of the layer, this function
   101   // will return the tile for
   102   // (x*GetScaledTileSize().width, y*GetScaledTileSize().height,
   103   //  GetScaledTileSize().width, GetScaledTileSize().height)
   104   Tile GetTile(int x, int y) const;
   106   // This operates the same as GetTile(aTileOrigin), but will also replace the
   107   // specified tile with the placeholder tile. This does not call ReleaseTile
   108   // on the removed tile.
   109   bool RemoveTile(const nsIntPoint& aTileOrigin, Tile& aRemovedTile);
   111   // This operates the same as GetTile(x, y), but will also replace the
   112   // specified tile with the placeholder tile. This does not call ReleaseTile
   113   // on the removed tile.
   114   bool RemoveTile(int x, int y, Tile& aRemovedTile);
   116   const gfx::IntSize& GetTileSize() const { return mTileSize; }
   118   gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
   120   unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
   122   const nsIntRegion& GetValidRegion() const { return mValidRegion; }
   123   const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
   124   void ClearPaintedRegion() { mPaintedRegion.SetEmpty(); }
   126   // Given a position i, this function returns the position inside the current tile.
   127   int GetTileStart(int i, int aTileLength) const {
   128     return (i >= 0) ? (i % aTileLength)
   129                     : ((aTileLength - (-i % aTileLength)) %
   130                        aTileLength);
   131   }
   133   // Rounds the given coordinate down to the nearest tile boundary.
   134   int RoundDownToTileEdge(int aX, int aTileLength) const { return aX - GetTileStart(aX, aTileLength); }
   136   // Get and set draw scaling. mResolution affects the resolution at which the
   137   // contents of the buffer are drawn. mResolution has no effect on the
   138   // coordinate space of the valid region, but does affect the size of an
   139   // individual tile's rect in relation to the valid region.
   140   // Setting the resolution will invalidate the buffer.
   141   float GetResolution() const { return mResolution; }
   142   void SetResolution(float aResolution) {
   143     if (mResolution == aResolution) {
   144       return;
   145     }
   147     Update(nsIntRegion(), nsIntRegion());
   148     mResolution = aResolution;
   149   }
   150   bool IsLowPrecision() const { return mResolution < 1; }
   152   typedef Tile* Iterator;
   153   Iterator TilesBegin() { return mRetainedTiles.Elements(); }
   154   Iterator TilesEnd() { return mRetainedTiles.Elements() + mRetainedTiles.Length(); }
   156 protected:
   157   // The implementor should call Update() to change
   158   // the new valid region. This implementation will call
   159   // validateTile on each tile that is dirty, which is left
   160   // to the implementor.
   161   void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
   163   nsIntRegion     mValidRegion;
   164   nsIntRegion     mPaintedRegion;
   166   /**
   167    * mRetainedTiles is a rectangular buffer of mRetainedWidth x mRetainedHeight
   168    * stored as column major with the same origin as mValidRegion.GetBounds().
   169    * Any tile that does not intersect mValidRegion is a PlaceholderTile.
   170    * Only the region intersecting with mValidRegion should be read from a tile,
   171    * another other region is assumed to be uninitialized. The contents of the
   172    * tiles is scaled by mResolution.
   173    */
   174   nsTArray<Tile>  mRetainedTiles;
   175   int             mRetainedWidth;  // in tiles
   176   int             mRetainedHeight; // in tiles
   177   float           mResolution;
   178   gfx::IntSize    mTileSize;
   180 private:
   181   const Derived& AsDerived() const { return *static_cast<const Derived*>(this); }
   182   Derived& AsDerived() { return *static_cast<Derived*>(this); }
   184   bool IsPlaceholder(Tile aTile) const { return aTile == AsDerived().GetPlaceholderTile(); }
   185 };
   187 class ClientTiledLayerBuffer;
   188 class SurfaceDescriptorTiles;
   189 class ISurfaceAllocator;
   191 // Shadow layers may implement this interface in order to be notified when a
   192 // tiled layer buffer is updated.
   193 class TiledLayerComposer
   194 {
   195 public:
   196   /**
   197    * Update the current retained layer with the updated layer data.
   198    * It is expected that the tiles described by aTiledDescriptor are all in the
   199    * ReadLock state, so that the locks can be adopted when recreating a
   200    * ClientTiledLayerBuffer locally. This lock will be retained until the buffer
   201    * has completed uploading.
   202    */
   203   virtual void UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
   204                                    const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
   206   /**
   207    * If some part of the buffer is being rendered at a lower precision, this
   208    * returns that region. If it is not, an empty region will be returned.
   209    */
   210   virtual const nsIntRegion& GetValidLowPrecisionRegion() const = 0;
   212 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   213   /**
   214    * Store a fence that will signal when the current buffer is no longer being read.
   215    * Similar to android's GLConsumer::setReleaseFence()
   216    */
   217   virtual void SetReleaseFence(const android::sp<android::Fence>& aReleaseFence) = 0;
   218 #endif
   219 };
   221 // Normal integer division truncates towards zero,
   222 // we instead want to floor to hangle negative numbers.
   223 static inline int floor_div(int a, int b)
   224 {
   225   int rem = a % b;
   226   int div = a/b;
   227   if (rem == 0) {
   228     return div;
   229   } else {
   230     // If the signs are different substract 1.
   231     int sub;
   232     sub = a ^ b;
   233     // The results of this shift is either 0 or -1.
   234     sub >>= 8*sizeof(int)-1;
   235     return div+sub;
   236   }
   237 }
   239 template<typename Derived, typename Tile> Tile
   240 TiledLayerBuffer<Derived, Tile>::GetTile(const nsIntPoint& aTileOrigin) const
   241 {
   242   // TODO Cache firstTileOriginX/firstTileOriginY
   243   // Find the tile x/y of the first tile and the target tile relative to the (0, 0)
   244   // origin, the difference is the tile x/y relative to the start of the tile buffer.
   245   gfx::IntSize scaledTileSize = GetScaledTileSize();
   246   int firstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
   247   int firstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
   248   return GetTile(floor_div(aTileOrigin.x, scaledTileSize.width) - firstTileX,
   249                  floor_div(aTileOrigin.y, scaledTileSize.height) - firstTileY);
   250 }
   252 template<typename Derived, typename Tile> Tile
   253 TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y) const
   254 {
   255   int index = x * mRetainedHeight + y;
   256   return mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
   257 }
   259 template<typename Derived, typename Tile> bool
   260 TiledLayerBuffer<Derived, Tile>::RemoveTile(const nsIntPoint& aTileOrigin,
   261                                             Tile& aRemovedTile)
   262 {
   263   gfx::IntSize scaledTileSize = GetScaledTileSize();
   264   int firstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
   265   int firstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
   266   return RemoveTile(floor_div(aTileOrigin.x, scaledTileSize.width) - firstTileX,
   267                     floor_div(aTileOrigin.y, scaledTileSize.height) - firstTileY,
   268                     aRemovedTile);
   269 }
   271 template<typename Derived, typename Tile> bool
   272 TiledLayerBuffer<Derived, Tile>::RemoveTile(int x, int y, Tile& aRemovedTile)
   273 {
   274   int index = x * mRetainedHeight + y;
   275   const Tile& tileToRemove = mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
   276   if (!IsPlaceholder(tileToRemove)) {
   277     aRemovedTile = tileToRemove;
   278     mRetainedTiles[index] = AsDerived().GetPlaceholderTile();
   279     return true;
   280   }
   281   return false;
   282 }
   284 template<typename Derived, typename Tile> void
   285 TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& aNewValidRegion,
   286                                         const nsIntRegion& aPaintRegion)
   287 {
   288   gfx::IntSize scaledTileSize = GetScaledTileSize();
   290   nsTArray<Tile>  newRetainedTiles;
   291   nsTArray<Tile>& oldRetainedTiles = mRetainedTiles;
   292   const nsIntRect oldBound = mValidRegion.GetBounds();
   293   const nsIntRect newBound = aNewValidRegion.GetBounds();
   294   const nsIntPoint oldBufferOrigin(RoundDownToTileEdge(oldBound.x, scaledTileSize.width),
   295                                    RoundDownToTileEdge(oldBound.y, scaledTileSize.height));
   296   const nsIntPoint newBufferOrigin(RoundDownToTileEdge(newBound.x, scaledTileSize.width),
   297                                    RoundDownToTileEdge(newBound.y, scaledTileSize.height));
   298   const nsIntRegion& oldValidRegion = mValidRegion;
   299   const nsIntRegion& newValidRegion = aNewValidRegion;
   300   const int oldRetainedHeight = mRetainedHeight;
   302   // Pass 1: Recycle valid content from the old buffer
   303   // Recycle tiles from the old buffer that contain valid regions.
   304   // Insert placeholders tiles if we have no valid area for that tile
   305   // which we will allocate in pass 2.
   306   // TODO: Add a tile pool to reduce new allocation
   307   int tileX = 0;
   308   int tileY = 0;
   309   int tilesMissing = 0;
   310   // Iterate over the new drawing bounds in steps of tiles.
   311   for (int32_t x = newBound.x; x < newBound.XMost(); tileX++) {
   312     // Compute tileRect(x,y,width,height) in layer space coordinate
   313     // giving us the rect of the tile that hits the newBounds.
   314     int width = scaledTileSize.width - GetTileStart(x, scaledTileSize.width);
   315     if (x + width > newBound.XMost()) {
   316       width = newBound.x + newBound.width - x;
   317     }
   319     tileY = 0;
   320     for (int32_t y = newBound.y; y < newBound.YMost(); tileY++) {
   321       int height = scaledTileSize.height - GetTileStart(y, scaledTileSize.height);
   322       if (y + height > newBound.y + newBound.height) {
   323         height = newBound.y + newBound.height - y;
   324       }
   326       const nsIntRect tileRect(x,y,width,height);
   327       if (oldValidRegion.Intersects(tileRect) && newValidRegion.Intersects(tileRect)) {
   328         // This old tiles contains some valid area so move it to the new tile
   329         // buffer. Replace the tile in the old buffer with a placeholder
   330         // to leave the old buffer index unaffected.
   331         int tileX = floor_div(x - oldBufferOrigin.x, scaledTileSize.width);
   332         int tileY = floor_div(y - oldBufferOrigin.y, scaledTileSize.height);
   333         int index = tileX * oldRetainedHeight + tileY;
   335         // The tile may have been removed, skip over it in this case.
   336         if (IsPlaceholder(oldRetainedTiles.
   337                           SafeElementAt(index, AsDerived().GetPlaceholderTile()))) {
   338           newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile());
   339         } else {
   340           Tile tileWithPartialValidContent = oldRetainedTiles[index];
   341           newRetainedTiles.AppendElement(tileWithPartialValidContent);
   342           oldRetainedTiles[index] = AsDerived().GetPlaceholderTile();
   343         }
   345       } else {
   346         // This tile is either:
   347         // 1) Outside the new valid region and will simply be an empty
   348         // placeholder forever.
   349         // 2) The old buffer didn't have any data for this tile. We postpone
   350         // the allocation of this tile after we've reused any tile with
   351         // valid content because then we know we can safely recycle
   352         // with taking from a tile that has recyclable content.
   353         newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile());
   355         if (aPaintRegion.Intersects(tileRect)) {
   356           tilesMissing++;
   357         }
   358       }
   360       y += height;
   361     }
   363     x += width;
   364   }
   366   // Keep track of the number of horizontal/vertical tiles
   367   // in the buffer so that we can easily look up a tile.
   368   mRetainedWidth = tileX;
   369   mRetainedHeight = tileY;
   371   // Pass 1.5: Release excess tiles in oldRetainedTiles
   372   // Tiles in oldRetainedTiles that aren't in newRetainedTiles will be recycled
   373   // before creating new ones, but there could still be excess unnecessary
   374   // tiles. As tiles may not have a fixed memory cost (for example, due to
   375   // double-buffering), we should release these excess tiles first.
   376   int oldTileCount = 0;
   377   for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
   378     Tile oldTile = oldRetainedTiles[i];
   379     if (IsPlaceholder(oldTile)) {
   380       continue;
   381     }
   383     if (oldTileCount >= tilesMissing) {
   384       oldRetainedTiles[i] = AsDerived().GetPlaceholderTile();
   385       AsDerived().ReleaseTile(oldTile);
   386     } else {
   387       oldTileCount ++;
   388     }
   389   }
   391   NS_ABORT_IF_FALSE(aNewValidRegion.Contains(aPaintRegion), "Painting a region outside the visible region");
   392 #ifdef DEBUG
   393   nsIntRegion oldAndPainted(oldValidRegion);
   394   oldAndPainted.Or(oldAndPainted, aPaintRegion);
   395 #endif
   396   NS_ABORT_IF_FALSE(oldAndPainted.Contains(newValidRegion), "newValidRegion has not been fully painted");
   398   nsIntRegion regionToPaint(aPaintRegion);
   400   // Pass 2: Validate
   401   // We know at this point that any tile in the new buffer that had valid content
   402   // from the previous buffer is placed correctly in the new buffer.
   403   // We know that any tile in the old buffer that isn't a place holder is
   404   // of no use and can be recycled.
   405   // We also know that any place holder tile in the new buffer must be
   406   // allocated.
   407   tileX = 0;
   408 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   409   printf_stderr("Update %i, %i, %i, %i\n", newBound.x, newBound.y, newBound.width, newBound.height);
   410 #endif
   411   for (int x = newBound.x; x < newBound.x + newBound.width; tileX++) {
   412     // Compute tileRect(x,y,width,height) in layer space coordinate
   413     // giving us the rect of the tile that hits the newBounds.
   414     int tileStartX = RoundDownToTileEdge(x, scaledTileSize.width);
   415     int width = scaledTileSize.width - GetTileStart(x, scaledTileSize.width);
   416     if (x + width > newBound.XMost())
   417       width = newBound.XMost() - x;
   419     tileY = 0;
   420     for (int y = newBound.y; y < newBound.y + newBound.height; tileY++) {
   421       int tileStartY = RoundDownToTileEdge(y, scaledTileSize.height);
   422       int height = scaledTileSize.height - GetTileStart(y, scaledTileSize.height);
   423       if (y + height > newBound.YMost()) {
   424         height = newBound.YMost() - y;
   425       }
   427       const nsIntRect tileRect(x, y, width, height);
   429       nsIntRegion tileDrawRegion;
   430       tileDrawRegion.And(tileRect, regionToPaint);
   432       if (tileDrawRegion.IsEmpty()) {
   433         // We have a tile but it doesn't hit the draw region
   434         // because we can reuse all of the content from the
   435         // previous buffer.
   436 #ifdef DEBUG
   437         int currTileX = floor_div(x - newBufferOrigin.x, scaledTileSize.width);
   438         int currTileY = floor_div(y - newBufferOrigin.y, scaledTileSize.height);
   439         int index = currTileX * mRetainedHeight + currTileY;
   440         NS_ABORT_IF_FALSE(!newValidRegion.Intersects(tileRect) ||
   441                           !IsPlaceholder(newRetainedTiles.
   442                                          SafeElementAt(index, AsDerived().GetPlaceholderTile())),
   443                           "If we don't draw a tile we shouldn't have a placeholder there.");
   444 #endif
   445         y += height;
   446         continue;
   447       }
   449       int tileX = floor_div(x - newBufferOrigin.x, scaledTileSize.width);
   450       int tileY = floor_div(y - newBufferOrigin.y, scaledTileSize.height);
   451       int index = tileX * mRetainedHeight + tileY;
   452       NS_ABORT_IF_FALSE(index >= 0 &&
   453                         static_cast<unsigned>(index) < newRetainedTiles.Length(),
   454                         "index out of range");
   456       Tile newTile = newRetainedTiles[index];
   458       // Try to reuse a tile from the old retained tiles that had no partially
   459       // valid content.
   460       while (IsPlaceholder(newTile) && oldRetainedTiles.Length() > 0) {
   461         AsDerived().SwapTiles(newTile, oldRetainedTiles[oldRetainedTiles.Length()-1]);
   462         oldRetainedTiles.RemoveElementAt(oldRetainedTiles.Length()-1);
   463         if (!IsPlaceholder(newTile)) {
   464           oldTileCount--;
   465         }
   466       }
   468       // We've done our best effort to recycle a tile but it can be null
   469       // in which case it's up to the derived class's ValidateTile()
   470       // implementation to allocate a new tile before drawing
   471       nsIntPoint tileOrigin(tileStartX, tileStartY);
   472       newTile = AsDerived().ValidateTile(newTile, nsIntPoint(tileStartX, tileStartY),
   473                                          tileDrawRegion);
   474       NS_ABORT_IF_FALSE(!IsPlaceholder(newTile), "index out of range");
   475 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   476       printf_stderr("Store Validate tile %i, %i -> %i\n", tileStartX, tileStartY, index);
   477 #endif
   478       newRetainedTiles[index] = newTile;
   480       y += height;
   481     }
   483     x += width;
   484   }
   486   // At this point, oldTileCount should be zero
   487   NS_ABORT_IF_FALSE(oldTileCount == 0, "Failed to release old tiles");
   489   mRetainedTiles = newRetainedTiles;
   490   mValidRegion = aNewValidRegion;
   491   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
   492 }
   494 } // layers
   495 } // mozilla
   497 #endif // GFX_TILEDLAYERBUFFER_H

mercurial