Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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/. */
5 #ifndef GFX_TILEDLAYERBUFFER_H
6 #define GFX_TILEDLAYERBUFFER_H
8 // Debug defines
9 //#define GFX_TILEDLAYER_DEBUG_OVERLAY
10 //#define GFX_TILEDLAYER_PREF_WARNINGS
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
21 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
22 #include <ui/Fence.h>
23 #endif
25 namespace mozilla {
26 namespace layers {
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.
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 {}
90 ~TiledLayerBuffer() {}
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;
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;
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);
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);
116 const gfx::IntSize& GetTileSize() const { return mTileSize; }
118 gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
120 unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
122 const nsIntRegion& GetValidRegion() const { return mValidRegion; }
123 const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
124 void ClearPaintedRegion() { mPaintedRegion.SetEmpty(); }
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 }
133 // Rounds the given coordinate down to the nearest tile boundary.
134 int RoundDownToTileEdge(int aX, int aTileLength) const { return aX - GetTileStart(aX, aTileLength); }
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 }
147 Update(nsIntRegion(), nsIntRegion());
148 mResolution = aResolution;
149 }
150 bool IsLowPrecision() const { return mResolution < 1; }
152 typedef Tile* Iterator;
153 Iterator TilesBegin() { return mRetainedTiles.Elements(); }
154 Iterator TilesEnd() { return mRetainedTiles.Elements() + mRetainedTiles.Length(); }
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);
163 nsIntRegion mValidRegion;
164 nsIntRegion mPaintedRegion;
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;
180 private:
181 const Derived& AsDerived() const { return *static_cast<const Derived*>(this); }
182 Derived& AsDerived() { return *static_cast<Derived*>(this); }
184 bool IsPlaceholder(Tile aTile) const { return aTile == AsDerived().GetPlaceholderTile(); }
185 };
187 class ClientTiledLayerBuffer;
188 class SurfaceDescriptorTiles;
189 class ISurfaceAllocator;
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;
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;
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 };
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 }
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 }
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 }
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 }
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 }
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();
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;
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 }
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 }
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;
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 }
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());
355 if (aPaintRegion.Intersects(tileRect)) {
356 tilesMissing++;
357 }
358 }
360 y += height;
361 }
363 x += width;
364 }
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;
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 }
383 if (oldTileCount >= tilesMissing) {
384 oldRetainedTiles[i] = AsDerived().GetPlaceholderTile();
385 AsDerived().ReleaseTile(oldTile);
386 } else {
387 oldTileCount ++;
388 }
389 }
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");
398 nsIntRegion regionToPaint(aPaintRegion);
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;
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 }
427 const nsIntRect tileRect(x, y, width, height);
429 nsIntRegion tileDrawRegion;
430 tileDrawRegion.And(tileRect, regionToPaint);
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 }
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");
456 Tile newTile = newRetainedTiles[index];
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 }
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;
480 y += height;
481 }
483 x += width;
484 }
486 // At this point, oldTileCount should be zero
487 NS_ABORT_IF_FALSE(oldTileCount == 0, "Failed to release old tiles");
489 mRetainedTiles = newRetainedTiles;
490 mValidRegion = aNewValidRegion;
491 mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
492 }
494 } // layers
495 } // mozilla
497 #endif // GFX_TILEDLAYERBUFFER_H