Bug 721070 - Checkerboarding at bottom after pinch zooming out. r=kats,pcwalton
authorChris Lord <chrislord.net@gmail.com>
Thu, 26 Jan 2012 20:19:35 +0000
changeset 86746 34b1cc9454d2cd54cc1b4cb4c7fcc5897385e9b2
parent 86745 ddde7a49f6f7122cadbaea55ad185aa6776d3f98
child 86747 812a3da795fe39f9560becd59f09443b2e6255b8
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats, pcwalton
bugs721070
milestone12.0a1
Bug 721070 - Checkerboarding at bottom after pinch zooming out. r=kats,pcwalton Certain conditions would cause MultiTileLayer to incorrectly invalidate tiles, or to reuse tiles incorrectly, which would cause checkerboarding (sometimes permanent until a redraw occurs). This addresses said issues: - Removes manual invalidation on Document:Shown and Tab:Selected. This is unnecessary, as the entire buffer will be invalidated when this happens anyway. Sometimes Document:Shown happens *after* drawing has occurred, which caused the entire screen to checkerboard incorrectly. - Fix re-using off-screen tiles with the incorrect resolution. Tiles are stored in a hash-map based on their unscaled position. As only tiles that intersect with the update region in tile-space are invalidated, sometimes tiles that were off-screen, but whose unscaled tile origin appears on-screen would be incorrectly re-used. Fix this by checking that a tile's resolution matches the current resolution before reusing it.
mobile/android/base/gfx/GeckoSoftwareLayerClient.java
mobile/android/base/gfx/MultiTileLayer.java
--- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
+++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
@@ -157,18 +157,16 @@ public class GeckoSoftwareLayerClient ex
 
         layerController.setRoot(mTileLayer);
         if (mGeckoViewport != null) {
             layerController.setViewportMetrics(mGeckoViewport);
         }
 
         GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this);
         GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this);
-        GeckoAppShell.registerGeckoEventListener("Document:Shown", this);
-        GeckoAppShell.registerGeckoEventListener("Tab:Selected:Done", this);
 
         sendResizeEventIfNecessary();
     }
 
     private boolean setHasDirectTexture(boolean hasDirectTexture) {
         if (mTileLayer != null && hasDirectTexture == mHasDirectTexture)
             return false;
 
@@ -247,17 +245,16 @@ public class GeckoSoftwareLayerClient ex
             Point oldOrigin = PointUtils.round(mGeckoViewport.getDisplayportOrigin());
             originChanged = !origin.equals(oldOrigin);
         }
 
         if (originChanged) {
             Point tileOrigin = new Point((origin.x / TILE_SIZE.width) * TILE_SIZE.width,
                                          (origin.y / TILE_SIZE.height) * TILE_SIZE.height);
             mRenderOffset.set(origin.x - tileOrigin.x, origin.y - tileOrigin.y);
-            ((MultiTileLayer)mTileLayer).invalidateBuffer();
         }
 
         // If the window size has changed, reallocate the buffer to match.
         if (mBufferSize.width != width || mBufferSize.height != height) {
             mBufferSize = new IntSize(width, height);
 
             // We over-allocate to allow for the render offset. nsWindow
             // assumes that this will happen.
@@ -514,26 +511,16 @@ public class GeckoSoftwareLayerClient ex
         if ("Viewport:UpdateAndDraw".equals(event)) {
             mUpdateViewportOnEndDraw = true;
 
             // Redraw everything.
             Rect rect = new Rect(0, 0, mBufferSize.width, mBufferSize.height);
             GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.DRAW, rect));
         } else if ("Viewport:UpdateLater".equals(event)) {
             mUpdateViewportOnEndDraw = true;
-        } else if (("Document:Shown".equals(event) ||
-                    "Tab:Selected:Done".equals(event)) &&
-                   (mTileLayer instanceof MultiTileLayer)) {
-            beginTransaction(mTileLayer);
-            try {
-                ((MultiTileLayer)mTileLayer).invalidateTiles();
-                ((MultiTileLayer)mTileLayer).invalidateBuffer();
-            } finally {
-                endTransaction(mTileLayer);
-            }
         }
     }
 
     // Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color
     // cannot be parsed, returns white.
     private static int parseColorFromGecko(String string) {
         if (sColorPattern == null) {
             sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)");
--- a/mobile/android/base/gfx/MultiTileLayer.java
+++ b/mobile/android/base/gfx/MultiTileLayer.java
@@ -328,26 +328,30 @@ public class MultiTileLayer extends Laye
                 Rect tilespaceTileRect = new Rect(x - origin.x, y - origin.y,
                                                   (x - origin.x) + mTileSize.width,
                                                   (y - origin.y) + mTileSize.height);
                 if (!opRegion.op(tilespaceTileRect, updateRegion, Region.Op.INTERSECT)) {
                     continue;
                 }
 
                 // Dirty tile, find out if we already have this tile to reuse.
-                boolean reusedTile = true;
+                boolean reusedTile;
                 Point tileOrigin = new Point(x, y);
                 SubTile tile = mPositionHash.get(longFromPoint(tileOrigin));
 
                 // If we don't, get an unused tile (we store these at the head of the list).
                 if (tile == null) {
                     tile = mTiles.removeFirst();
                     reusedTile = false;
                 } else {
                     mTiles.remove(tile);
+
+                    // Reuse the tile (i.e. keep the texture data and metrics)
+                    // only if the resolution matches
+                    reusedTile = FloatUtils.fuzzyEquals(tile.getResolution(), getResolution());
                 }
 
                 // Place tile at the end of the tile-list so it isn't re-used.
                 mTiles.add(tile);
 
                 // Work out the tile's invalid area in this tile's space.
                 if (opRegion.isComplex()) {
                     Log.w(LOGTAG, "MultiTileLayer encountered complex dirty region");
@@ -391,20 +395,46 @@ public class MultiTileLayer extends Laye
         }
 
         super.endTransaction();
     }
 
     @Override
     public void draw(RenderContext context) {
         for (SubTile layer : mTiles) {
+            // Skip invalid tiles
+            if (layer.key == null) {
+                continue;
+            }
+
             // Avoid work, only draw tiles that intersect with the viewport
             RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize()));
-            if (RectF.intersects(layerBounds, context.viewport))
+            if (RectF.intersects(layerBounds, context.viewport)) {
                 layer.draw(context);
+            }
+        }
+    }
+
+    @Override
+    public void setOrigin(Point origin) {
+        Point oldOrigin = getOrigin();
+
+        if (!origin.equals(oldOrigin)) {
+            super.setOrigin(origin);
+            invalidateBuffer();
+        }
+    }
+
+    @Override
+    public void setResolution(float resolution) {
+        float oldResolution = getResolution();
+
+        if (!FloatUtils.fuzzyEquals(resolution, oldResolution)) {
+            super.setResolution(resolution);
+            invalidateBuffer();
         }
     }
 
     public void setRenderOffset(Point offset) {
         mRenderOffset.set(offset.x, offset.y);
     }
 
     /**