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