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.

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

mercurial