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