gfx/layers/TiledLayerBuffer.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/layers/TiledLayerBuffer.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,497 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#ifndef GFX_TILEDLAYERBUFFER_H
     1.9 +#define GFX_TILEDLAYERBUFFER_H
    1.10 +
    1.11 +// Debug defines
    1.12 +//#define GFX_TILEDLAYER_DEBUG_OVERLAY
    1.13 +//#define GFX_TILEDLAYER_PREF_WARNINGS
    1.14 +
    1.15 +#include <stdint.h>                     // for uint16_t, uint32_t
    1.16 +#include <sys/types.h>                  // for int32_t
    1.17 +#include "gfxPrefs.h"                   // for gfxPrefs::LayersTileWidth/Height
    1.18 +#include "nsDebug.h"                    // for NS_ABORT_IF_FALSE
    1.19 +#include "nsPoint.h"                    // for nsIntPoint
    1.20 +#include "nsRect.h"                     // for nsIntRect
    1.21 +#include "nsRegion.h"                   // for nsIntRegion
    1.22 +#include "nsTArray.h"                   // for nsTArray
    1.23 +
    1.24 +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
    1.25 +#include <ui/Fence.h>
    1.26 +#endif
    1.27 +
    1.28 +namespace mozilla {
    1.29 +namespace layers {
    1.30 +
    1.31 +// An abstract implementation of a tile buffer. This code covers the logic of
    1.32 +// moving and reusing tiles and leaves the validation up to the implementor. To
    1.33 +// avoid the overhead of virtual dispatch, we employ the curiously recurring
    1.34 +// template pattern.
    1.35 +//
    1.36 +// Tiles are aligned to a grid with one of the grid points at (0,0) and other
    1.37 +// grid points spaced evenly in the x- and y-directions by GetTileSize()
    1.38 +// multiplied by mResolution. GetScaledTileSize() provides convenience for
    1.39 +// accessing these values.
    1.40 +//
    1.41 +// This tile buffer stores a valid region, which defines the areas that have
    1.42 +// up-to-date content. The contents of tiles within this region will be reused
    1.43 +// from paint to paint. It also stores the region that was modified in the last
    1.44 +// paint operation; this is useful when one tiled layer buffer shadows another
    1.45 +// (as in an off-main-thread-compositing scenario), so that the shadow tiled
    1.46 +// layer buffer can correctly reflect the updates of the master layer buffer.
    1.47 +//
    1.48 +// The associated Tile may be of any type as long as the derived class can
    1.49 +// validate and return tiles of that type. Tiles will be frequently copied, so
    1.50 +// the tile type should be a reference or some other type with an efficient
    1.51 +// copy constructor.
    1.52 +//
    1.53 +// It is required that the derived class specify the base class as a friend. It
    1.54 +// must also implement the following public method:
    1.55 +//
    1.56 +//   Tile GetPlaceholderTile() const;
    1.57 +//
    1.58 +//   Returns a temporary placeholder tile used as a marker. This placeholder tile
    1.59 +//   must never be returned by validateTile and must be == to every instance
    1.60 +//   of a placeholder tile.
    1.61 +//
    1.62 +// Additionally, it must implement the following protected methods:
    1.63 +//
    1.64 +//   Tile ValidateTile(Tile aTile, const nsIntPoint& aTileOrigin,
    1.65 +//                     const nsIntRegion& aDirtyRect);
    1.66 +//
    1.67 +//   Validates the dirtyRect. The returned Tile will replace the tile.
    1.68 +//
    1.69 +//   void ReleaseTile(Tile aTile);
    1.70 +//
    1.71 +//   Destroys the given tile.
    1.72 +//
    1.73 +//   void SwapTiles(Tile& aTileA, Tile& aTileB);
    1.74 +//
    1.75 +//   Swaps two tiles.
    1.76 +//
    1.77 +// The contents of the tile buffer will be rendered at the resolution specified
    1.78 +// in mResolution, which can be altered with SetResolution. The resolution
    1.79 +// should always be a factor of the tile length, to avoid tiles covering
    1.80 +// non-integer amounts of pixels.
    1.81 +
    1.82 +template<typename Derived, typename Tile>
    1.83 +class TiledLayerBuffer
    1.84 +{
    1.85 +public:
    1.86 +  TiledLayerBuffer()
    1.87 +    : mRetainedWidth(0)
    1.88 +    , mRetainedHeight(0)
    1.89 +    , mResolution(1)
    1.90 +    , mTileSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight())
    1.91 +  {}
    1.92 +
    1.93 +  ~TiledLayerBuffer() {}
    1.94 +
    1.95 +  // Given a tile origin aligned to a multiple of GetScaledTileSize,
    1.96 +  // return the tile that describes that region.
    1.97 +  // NOTE: To get the valid area of that tile you must intersect
    1.98 +  //       (aTileOrigin.x, aTileOrigin.y,
    1.99 +  //        GetScaledTileSize().width, GetScaledTileSize().height)
   1.100 +  //       and GetValidRegion() to get the area of the tile that is valid.
   1.101 +  Tile GetTile(const nsIntPoint& aTileOrigin) const;
   1.102 +
   1.103 +  // Given a tile x, y relative to the top left of the layer, this function
   1.104 +  // will return the tile for
   1.105 +  // (x*GetScaledTileSize().width, y*GetScaledTileSize().height,
   1.106 +  //  GetScaledTileSize().width, GetScaledTileSize().height)
   1.107 +  Tile GetTile(int x, int y) const;
   1.108 +
   1.109 +  // This operates the same as GetTile(aTileOrigin), but will also replace the
   1.110 +  // specified tile with the placeholder tile. This does not call ReleaseTile
   1.111 +  // on the removed tile.
   1.112 +  bool RemoveTile(const nsIntPoint& aTileOrigin, Tile& aRemovedTile);
   1.113 +
   1.114 +  // This operates the same as GetTile(x, y), but will also replace the
   1.115 +  // specified tile with the placeholder tile. This does not call ReleaseTile
   1.116 +  // on the removed tile.
   1.117 +  bool RemoveTile(int x, int y, Tile& aRemovedTile);
   1.118 +
   1.119 +  const gfx::IntSize& GetTileSize() const { return mTileSize; }
   1.120 +
   1.121 +  gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
   1.122 +
   1.123 +  unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
   1.124 +
   1.125 +  const nsIntRegion& GetValidRegion() const { return mValidRegion; }
   1.126 +  const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
   1.127 +  void ClearPaintedRegion() { mPaintedRegion.SetEmpty(); }
   1.128 +
   1.129 +  // Given a position i, this function returns the position inside the current tile.
   1.130 +  int GetTileStart(int i, int aTileLength) const {
   1.131 +    return (i >= 0) ? (i % aTileLength)
   1.132 +                    : ((aTileLength - (-i % aTileLength)) %
   1.133 +                       aTileLength);
   1.134 +  }
   1.135 +
   1.136 +  // Rounds the given coordinate down to the nearest tile boundary.
   1.137 +  int RoundDownToTileEdge(int aX, int aTileLength) const { return aX - GetTileStart(aX, aTileLength); }
   1.138 +
   1.139 +  // Get and set draw scaling. mResolution affects the resolution at which the
   1.140 +  // contents of the buffer are drawn. mResolution has no effect on the
   1.141 +  // coordinate space of the valid region, but does affect the size of an
   1.142 +  // individual tile's rect in relation to the valid region.
   1.143 +  // Setting the resolution will invalidate the buffer.
   1.144 +  float GetResolution() const { return mResolution; }
   1.145 +  void SetResolution(float aResolution) {
   1.146 +    if (mResolution == aResolution) {
   1.147 +      return;
   1.148 +    }
   1.149 +
   1.150 +    Update(nsIntRegion(), nsIntRegion());
   1.151 +    mResolution = aResolution;
   1.152 +  }
   1.153 +  bool IsLowPrecision() const { return mResolution < 1; }
   1.154 +
   1.155 +  typedef Tile* Iterator;
   1.156 +  Iterator TilesBegin() { return mRetainedTiles.Elements(); }
   1.157 +  Iterator TilesEnd() { return mRetainedTiles.Elements() + mRetainedTiles.Length(); }
   1.158 +
   1.159 +protected:
   1.160 +  // The implementor should call Update() to change
   1.161 +  // the new valid region. This implementation will call
   1.162 +  // validateTile on each tile that is dirty, which is left
   1.163 +  // to the implementor.
   1.164 +  void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
   1.165 +
   1.166 +  nsIntRegion     mValidRegion;
   1.167 +  nsIntRegion     mPaintedRegion;
   1.168 +
   1.169 +  /**
   1.170 +   * mRetainedTiles is a rectangular buffer of mRetainedWidth x mRetainedHeight
   1.171 +   * stored as column major with the same origin as mValidRegion.GetBounds().
   1.172 +   * Any tile that does not intersect mValidRegion is a PlaceholderTile.
   1.173 +   * Only the region intersecting with mValidRegion should be read from a tile,
   1.174 +   * another other region is assumed to be uninitialized. The contents of the
   1.175 +   * tiles is scaled by mResolution.
   1.176 +   */
   1.177 +  nsTArray<Tile>  mRetainedTiles;
   1.178 +  int             mRetainedWidth;  // in tiles
   1.179 +  int             mRetainedHeight; // in tiles
   1.180 +  float           mResolution;
   1.181 +  gfx::IntSize    mTileSize;
   1.182 +
   1.183 +private:
   1.184 +  const Derived& AsDerived() const { return *static_cast<const Derived*>(this); }
   1.185 +  Derived& AsDerived() { return *static_cast<Derived*>(this); }
   1.186 +
   1.187 +  bool IsPlaceholder(Tile aTile) const { return aTile == AsDerived().GetPlaceholderTile(); }
   1.188 +};
   1.189 +
   1.190 +class ClientTiledLayerBuffer;
   1.191 +class SurfaceDescriptorTiles;
   1.192 +class ISurfaceAllocator;
   1.193 +
   1.194 +// Shadow layers may implement this interface in order to be notified when a
   1.195 +// tiled layer buffer is updated.
   1.196 +class TiledLayerComposer
   1.197 +{
   1.198 +public:
   1.199 +  /**
   1.200 +   * Update the current retained layer with the updated layer data.
   1.201 +   * It is expected that the tiles described by aTiledDescriptor are all in the
   1.202 +   * ReadLock state, so that the locks can be adopted when recreating a
   1.203 +   * ClientTiledLayerBuffer locally. This lock will be retained until the buffer
   1.204 +   * has completed uploading.
   1.205 +   */
   1.206 +  virtual void UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
   1.207 +                                   const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
   1.208 +
   1.209 +  /**
   1.210 +   * If some part of the buffer is being rendered at a lower precision, this
   1.211 +   * returns that region. If it is not, an empty region will be returned.
   1.212 +   */
   1.213 +  virtual const nsIntRegion& GetValidLowPrecisionRegion() const = 0;
   1.214 +
   1.215 +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   1.216 +  /**
   1.217 +   * Store a fence that will signal when the current buffer is no longer being read.
   1.218 +   * Similar to android's GLConsumer::setReleaseFence()
   1.219 +   */
   1.220 +  virtual void SetReleaseFence(const android::sp<android::Fence>& aReleaseFence) = 0;
   1.221 +#endif
   1.222 +};
   1.223 +
   1.224 +// Normal integer division truncates towards zero,
   1.225 +// we instead want to floor to hangle negative numbers.
   1.226 +static inline int floor_div(int a, int b)
   1.227 +{
   1.228 +  int rem = a % b;
   1.229 +  int div = a/b;
   1.230 +  if (rem == 0) {
   1.231 +    return div;
   1.232 +  } else {
   1.233 +    // If the signs are different substract 1.
   1.234 +    int sub;
   1.235 +    sub = a ^ b;
   1.236 +    // The results of this shift is either 0 or -1.
   1.237 +    sub >>= 8*sizeof(int)-1;
   1.238 +    return div+sub;
   1.239 +  }
   1.240 +}
   1.241 +
   1.242 +template<typename Derived, typename Tile> Tile
   1.243 +TiledLayerBuffer<Derived, Tile>::GetTile(const nsIntPoint& aTileOrigin) const
   1.244 +{
   1.245 +  // TODO Cache firstTileOriginX/firstTileOriginY
   1.246 +  // Find the tile x/y of the first tile and the target tile relative to the (0, 0)
   1.247 +  // origin, the difference is the tile x/y relative to the start of the tile buffer.
   1.248 +  gfx::IntSize scaledTileSize = GetScaledTileSize();
   1.249 +  int firstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
   1.250 +  int firstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
   1.251 +  return GetTile(floor_div(aTileOrigin.x, scaledTileSize.width) - firstTileX,
   1.252 +                 floor_div(aTileOrigin.y, scaledTileSize.height) - firstTileY);
   1.253 +}
   1.254 +
   1.255 +template<typename Derived, typename Tile> Tile
   1.256 +TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y) const
   1.257 +{
   1.258 +  int index = x * mRetainedHeight + y;
   1.259 +  return mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
   1.260 +}
   1.261 +
   1.262 +template<typename Derived, typename Tile> bool
   1.263 +TiledLayerBuffer<Derived, Tile>::RemoveTile(const nsIntPoint& aTileOrigin,
   1.264 +                                            Tile& aRemovedTile)
   1.265 +{
   1.266 +  gfx::IntSize scaledTileSize = GetScaledTileSize();
   1.267 +  int firstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
   1.268 +  int firstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
   1.269 +  return RemoveTile(floor_div(aTileOrigin.x, scaledTileSize.width) - firstTileX,
   1.270 +                    floor_div(aTileOrigin.y, scaledTileSize.height) - firstTileY,
   1.271 +                    aRemovedTile);
   1.272 +}
   1.273 +
   1.274 +template<typename Derived, typename Tile> bool
   1.275 +TiledLayerBuffer<Derived, Tile>::RemoveTile(int x, int y, Tile& aRemovedTile)
   1.276 +{
   1.277 +  int index = x * mRetainedHeight + y;
   1.278 +  const Tile& tileToRemove = mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
   1.279 +  if (!IsPlaceholder(tileToRemove)) {
   1.280 +    aRemovedTile = tileToRemove;
   1.281 +    mRetainedTiles[index] = AsDerived().GetPlaceholderTile();
   1.282 +    return true;
   1.283 +  }
   1.284 +  return false;
   1.285 +}
   1.286 +
   1.287 +template<typename Derived, typename Tile> void
   1.288 +TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& aNewValidRegion,
   1.289 +                                        const nsIntRegion& aPaintRegion)
   1.290 +{
   1.291 +  gfx::IntSize scaledTileSize = GetScaledTileSize();
   1.292 +
   1.293 +  nsTArray<Tile>  newRetainedTiles;
   1.294 +  nsTArray<Tile>& oldRetainedTiles = mRetainedTiles;
   1.295 +  const nsIntRect oldBound = mValidRegion.GetBounds();
   1.296 +  const nsIntRect newBound = aNewValidRegion.GetBounds();
   1.297 +  const nsIntPoint oldBufferOrigin(RoundDownToTileEdge(oldBound.x, scaledTileSize.width),
   1.298 +                                   RoundDownToTileEdge(oldBound.y, scaledTileSize.height));
   1.299 +  const nsIntPoint newBufferOrigin(RoundDownToTileEdge(newBound.x, scaledTileSize.width),
   1.300 +                                   RoundDownToTileEdge(newBound.y, scaledTileSize.height));
   1.301 +  const nsIntRegion& oldValidRegion = mValidRegion;
   1.302 +  const nsIntRegion& newValidRegion = aNewValidRegion;
   1.303 +  const int oldRetainedHeight = mRetainedHeight;
   1.304 +
   1.305 +  // Pass 1: Recycle valid content from the old buffer
   1.306 +  // Recycle tiles from the old buffer that contain valid regions.
   1.307 +  // Insert placeholders tiles if we have no valid area for that tile
   1.308 +  // which we will allocate in pass 2.
   1.309 +  // TODO: Add a tile pool to reduce new allocation
   1.310 +  int tileX = 0;
   1.311 +  int tileY = 0;
   1.312 +  int tilesMissing = 0;
   1.313 +  // Iterate over the new drawing bounds in steps of tiles.
   1.314 +  for (int32_t x = newBound.x; x < newBound.XMost(); tileX++) {
   1.315 +    // Compute tileRect(x,y,width,height) in layer space coordinate
   1.316 +    // giving us the rect of the tile that hits the newBounds.
   1.317 +    int width = scaledTileSize.width - GetTileStart(x, scaledTileSize.width);
   1.318 +    if (x + width > newBound.XMost()) {
   1.319 +      width = newBound.x + newBound.width - x;
   1.320 +    }
   1.321 +
   1.322 +    tileY = 0;
   1.323 +    for (int32_t y = newBound.y; y < newBound.YMost(); tileY++) {
   1.324 +      int height = scaledTileSize.height - GetTileStart(y, scaledTileSize.height);
   1.325 +      if (y + height > newBound.y + newBound.height) {
   1.326 +        height = newBound.y + newBound.height - y;
   1.327 +      }
   1.328 +
   1.329 +      const nsIntRect tileRect(x,y,width,height);
   1.330 +      if (oldValidRegion.Intersects(tileRect) && newValidRegion.Intersects(tileRect)) {
   1.331 +        // This old tiles contains some valid area so move it to the new tile
   1.332 +        // buffer. Replace the tile in the old buffer with a placeholder
   1.333 +        // to leave the old buffer index unaffected.
   1.334 +        int tileX = floor_div(x - oldBufferOrigin.x, scaledTileSize.width);
   1.335 +        int tileY = floor_div(y - oldBufferOrigin.y, scaledTileSize.height);
   1.336 +        int index = tileX * oldRetainedHeight + tileY;
   1.337 +
   1.338 +        // The tile may have been removed, skip over it in this case.
   1.339 +        if (IsPlaceholder(oldRetainedTiles.
   1.340 +                          SafeElementAt(index, AsDerived().GetPlaceholderTile()))) {
   1.341 +          newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile());
   1.342 +        } else {
   1.343 +          Tile tileWithPartialValidContent = oldRetainedTiles[index];
   1.344 +          newRetainedTiles.AppendElement(tileWithPartialValidContent);
   1.345 +          oldRetainedTiles[index] = AsDerived().GetPlaceholderTile();
   1.346 +        }
   1.347 +
   1.348 +      } else {
   1.349 +        // This tile is either:
   1.350 +        // 1) Outside the new valid region and will simply be an empty
   1.351 +        // placeholder forever.
   1.352 +        // 2) The old buffer didn't have any data for this tile. We postpone
   1.353 +        // the allocation of this tile after we've reused any tile with
   1.354 +        // valid content because then we know we can safely recycle
   1.355 +        // with taking from a tile that has recyclable content.
   1.356 +        newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile());
   1.357 +
   1.358 +        if (aPaintRegion.Intersects(tileRect)) {
   1.359 +          tilesMissing++;
   1.360 +        }
   1.361 +      }
   1.362 +
   1.363 +      y += height;
   1.364 +    }
   1.365 +
   1.366 +    x += width;
   1.367 +  }
   1.368 +
   1.369 +  // Keep track of the number of horizontal/vertical tiles
   1.370 +  // in the buffer so that we can easily look up a tile.
   1.371 +  mRetainedWidth = tileX;
   1.372 +  mRetainedHeight = tileY;
   1.373 +
   1.374 +  // Pass 1.5: Release excess tiles in oldRetainedTiles
   1.375 +  // Tiles in oldRetainedTiles that aren't in newRetainedTiles will be recycled
   1.376 +  // before creating new ones, but there could still be excess unnecessary
   1.377 +  // tiles. As tiles may not have a fixed memory cost (for example, due to
   1.378 +  // double-buffering), we should release these excess tiles first.
   1.379 +  int oldTileCount = 0;
   1.380 +  for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
   1.381 +    Tile oldTile = oldRetainedTiles[i];
   1.382 +    if (IsPlaceholder(oldTile)) {
   1.383 +      continue;
   1.384 +    }
   1.385 +
   1.386 +    if (oldTileCount >= tilesMissing) {
   1.387 +      oldRetainedTiles[i] = AsDerived().GetPlaceholderTile();
   1.388 +      AsDerived().ReleaseTile(oldTile);
   1.389 +    } else {
   1.390 +      oldTileCount ++;
   1.391 +    }
   1.392 +  }
   1.393 +
   1.394 +  NS_ABORT_IF_FALSE(aNewValidRegion.Contains(aPaintRegion), "Painting a region outside the visible region");
   1.395 +#ifdef DEBUG
   1.396 +  nsIntRegion oldAndPainted(oldValidRegion);
   1.397 +  oldAndPainted.Or(oldAndPainted, aPaintRegion);
   1.398 +#endif
   1.399 +  NS_ABORT_IF_FALSE(oldAndPainted.Contains(newValidRegion), "newValidRegion has not been fully painted");
   1.400 +
   1.401 +  nsIntRegion regionToPaint(aPaintRegion);
   1.402 +
   1.403 +  // Pass 2: Validate
   1.404 +  // We know at this point that any tile in the new buffer that had valid content
   1.405 +  // from the previous buffer is placed correctly in the new buffer.
   1.406 +  // We know that any tile in the old buffer that isn't a place holder is
   1.407 +  // of no use and can be recycled.
   1.408 +  // We also know that any place holder tile in the new buffer must be
   1.409 +  // allocated.
   1.410 +  tileX = 0;
   1.411 +#ifdef GFX_TILEDLAYER_PREF_WARNINGS
   1.412 +  printf_stderr("Update %i, %i, %i, %i\n", newBound.x, newBound.y, newBound.width, newBound.height);
   1.413 +#endif
   1.414 +  for (int x = newBound.x; x < newBound.x + newBound.width; tileX++) {
   1.415 +    // Compute tileRect(x,y,width,height) in layer space coordinate
   1.416 +    // giving us the rect of the tile that hits the newBounds.
   1.417 +    int tileStartX = RoundDownToTileEdge(x, scaledTileSize.width);
   1.418 +    int width = scaledTileSize.width - GetTileStart(x, scaledTileSize.width);
   1.419 +    if (x + width > newBound.XMost())
   1.420 +      width = newBound.XMost() - x;
   1.421 +
   1.422 +    tileY = 0;
   1.423 +    for (int y = newBound.y; y < newBound.y + newBound.height; tileY++) {
   1.424 +      int tileStartY = RoundDownToTileEdge(y, scaledTileSize.height);
   1.425 +      int height = scaledTileSize.height - GetTileStart(y, scaledTileSize.height);
   1.426 +      if (y + height > newBound.YMost()) {
   1.427 +        height = newBound.YMost() - y;
   1.428 +      }
   1.429 +
   1.430 +      const nsIntRect tileRect(x, y, width, height);
   1.431 +
   1.432 +      nsIntRegion tileDrawRegion;
   1.433 +      tileDrawRegion.And(tileRect, regionToPaint);
   1.434 +
   1.435 +      if (tileDrawRegion.IsEmpty()) {
   1.436 +        // We have a tile but it doesn't hit the draw region
   1.437 +        // because we can reuse all of the content from the
   1.438 +        // previous buffer.
   1.439 +#ifdef DEBUG
   1.440 +        int currTileX = floor_div(x - newBufferOrigin.x, scaledTileSize.width);
   1.441 +        int currTileY = floor_div(y - newBufferOrigin.y, scaledTileSize.height);
   1.442 +        int index = currTileX * mRetainedHeight + currTileY;
   1.443 +        NS_ABORT_IF_FALSE(!newValidRegion.Intersects(tileRect) ||
   1.444 +                          !IsPlaceholder(newRetainedTiles.
   1.445 +                                         SafeElementAt(index, AsDerived().GetPlaceholderTile())),
   1.446 +                          "If we don't draw a tile we shouldn't have a placeholder there.");
   1.447 +#endif
   1.448 +        y += height;
   1.449 +        continue;
   1.450 +      }
   1.451 +
   1.452 +      int tileX = floor_div(x - newBufferOrigin.x, scaledTileSize.width);
   1.453 +      int tileY = floor_div(y - newBufferOrigin.y, scaledTileSize.height);
   1.454 +      int index = tileX * mRetainedHeight + tileY;
   1.455 +      NS_ABORT_IF_FALSE(index >= 0 &&
   1.456 +                        static_cast<unsigned>(index) < newRetainedTiles.Length(),
   1.457 +                        "index out of range");
   1.458 +
   1.459 +      Tile newTile = newRetainedTiles[index];
   1.460 +
   1.461 +      // Try to reuse a tile from the old retained tiles that had no partially
   1.462 +      // valid content.
   1.463 +      while (IsPlaceholder(newTile) && oldRetainedTiles.Length() > 0) {
   1.464 +        AsDerived().SwapTiles(newTile, oldRetainedTiles[oldRetainedTiles.Length()-1]);
   1.465 +        oldRetainedTiles.RemoveElementAt(oldRetainedTiles.Length()-1);
   1.466 +        if (!IsPlaceholder(newTile)) {
   1.467 +          oldTileCount--;
   1.468 +        }
   1.469 +      }
   1.470 +
   1.471 +      // We've done our best effort to recycle a tile but it can be null
   1.472 +      // in which case it's up to the derived class's ValidateTile()
   1.473 +      // implementation to allocate a new tile before drawing
   1.474 +      nsIntPoint tileOrigin(tileStartX, tileStartY);
   1.475 +      newTile = AsDerived().ValidateTile(newTile, nsIntPoint(tileStartX, tileStartY),
   1.476 +                                         tileDrawRegion);
   1.477 +      NS_ABORT_IF_FALSE(!IsPlaceholder(newTile), "index out of range");
   1.478 +#ifdef GFX_TILEDLAYER_PREF_WARNINGS
   1.479 +      printf_stderr("Store Validate tile %i, %i -> %i\n", tileStartX, tileStartY, index);
   1.480 +#endif
   1.481 +      newRetainedTiles[index] = newTile;
   1.482 +
   1.483 +      y += height;
   1.484 +    }
   1.485 +
   1.486 +    x += width;
   1.487 +  }
   1.488 +
   1.489 +  // At this point, oldTileCount should be zero
   1.490 +  NS_ABORT_IF_FALSE(oldTileCount == 0, "Failed to release old tiles");
   1.491 +
   1.492 +  mRetainedTiles = newRetainedTiles;
   1.493 +  mValidRegion = aNewValidRegion;
   1.494 +  mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
   1.495 +}
   1.496 +
   1.497 +} // layers
   1.498 +} // mozilla
   1.499 +
   1.500 +#endif // GFX_TILEDLAYERBUFFER_H

mercurial