Tue, 06 Jan 2015 21:39:09 +0100
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 |