summaryrefslogtreecommitdiff
path: root/android
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.com>2014-09-24 09:10:12 +0200
committerTomaž Vajngerl <tomaz.vajngerl@collabora.com>2014-09-24 20:43:03 +0200
commit1fa58e5c60ac136e92e91c4efa0cdcc5c28afa75 (patch)
tree9457516bb166a4f3e75366b9d83c254b6c01fce2 /android
parent0aad04016ca567b93e4d85ae79657f0888514a13 (diff)
android: update DisplayPortCalculator
Change-Id: Ib47822940e83e8fb78a0a8b1e18028646bfb35ae
Diffstat (limited to 'android')
-rw-r--r--android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java415
-rw-r--r--android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java11
2 files changed, 356 insertions, 70 deletions
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java
index a0cb229a7128..a255974b5481 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java
@@ -7,16 +7,36 @@ package org.mozilla.gecko.gfx;
import android.graphics.PointF;
import android.graphics.RectF;
+import android.util.FloatMath;
import android.util.Log;
+import org.json.JSONArray;
import org.libreoffice.LOKitShell;
import org.mozilla.gecko.util.FloatUtils;
+import java.util.Map;
+
final class DisplayPortCalculator {
private static final String LOGTAG = "GeckoDisplayPortCalculator";
private static final PointF ZERO_VELOCITY = new PointF(0, 0);
- private static DisplayPortStrategy sStrategy = new FixedMarginStrategy();
+ // Keep this in sync with the TILEDLAYERBUFFER_TILE_SIZE defined in gfx/layers/TiledLayerBuffer.h
+ private static final int TILE_SIZE = 256;
+
+ private static final String PREF_DISPLAYPORT_STRATEGY = "gfx.displayport.strategy";
+ private static final String PREF_DISPLAYPORT_FM_MULTIPLIER = "gfx.displayport.strategy_fm.multiplier";
+ private static final String PREF_DISPLAYPORT_FM_DANGER_X = "gfx.displayport.strategy_fm.danger_x";
+ private static final String PREF_DISPLAYPORT_FM_DANGER_Y = "gfx.displayport.strategy_fm.danger_y";
+ private static final String PREF_DISPLAYPORT_VB_MULTIPLIER = "gfx.displayport.strategy_vb.multiplier";
+ private static final String PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_vb.threshold";
+ private static final String PREF_DISPLAYPORT_VB_REVERSE_BUFFER = "gfx.displayport.strategy_vb.reverse_buffer";
+ private static final String PREF_DISPLAYPORT_VB_DANGER_X_BASE = "gfx.displayport.strategy_vb.danger_x_base";
+ private static final String PREF_DISPLAYPORT_VB_DANGER_Y_BASE = "gfx.displayport.strategy_vb.danger_y_base";
+ private static final String PREF_DISPLAYPORT_VB_DANGER_X_INCR = "gfx.displayport.strategy_vb.danger_x_incr";
+ private static final String PREF_DISPLAYPORT_VB_DANGER_Y_INCR = "gfx.displayport.strategy_vb.danger_y_incr";
+ private static final String PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_pb.threshold";
+
+ private static DisplayPortStrategy sStrategy = new NoMarginStrategy(null);
static DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
return sStrategy.calculate(metrics, (velocity == null ? ZERO_VELOCITY : velocity));
@@ -29,35 +49,78 @@ final class DisplayPortCalculator {
return sStrategy.aboutToCheckerboard(metrics, (velocity == null ? ZERO_VELOCITY : velocity), displayPort);
}
+ static boolean drawTimeUpdate(long millis, int pixels) {
+ return sStrategy.drawTimeUpdate(millis, pixels);
+ }
+
+ static void resetPageState() {
+ sStrategy.resetPageState();
+ }
+
+ static void addPrefNames(JSONArray prefs) {
+ prefs.put(PREF_DISPLAYPORT_STRATEGY);
+ prefs.put(PREF_DISPLAYPORT_FM_MULTIPLIER);
+ prefs.put(PREF_DISPLAYPORT_FM_DANGER_X);
+ prefs.put(PREF_DISPLAYPORT_FM_DANGER_Y);
+ prefs.put(PREF_DISPLAYPORT_VB_MULTIPLIER);
+ prefs.put(PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD);
+ prefs.put(PREF_DISPLAYPORT_VB_REVERSE_BUFFER);
+ prefs.put(PREF_DISPLAYPORT_VB_DANGER_X_BASE);
+ prefs.put(PREF_DISPLAYPORT_VB_DANGER_Y_BASE);
+ prefs.put(PREF_DISPLAYPORT_VB_DANGER_X_INCR);
+ prefs.put(PREF_DISPLAYPORT_VB_DANGER_Y_INCR);
+ prefs.put(PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD);
+ }
+
/**
* Set the active strategy to use.
* See the gfx.displayport.strategy pref in mobile/android/app/mobile.js to see the
* mapping between ints and strategies.
*/
- static void setStrategy(int strategy) {
+ static boolean setStrategy(Map<String, Integer> prefs) {
+ Integer strategy = prefs.get(PREF_DISPLAYPORT_STRATEGY);
+ if (strategy == null) {
+ return false;
+ }
+
switch (strategy) {
case 0:
- default:
- sStrategy = new FixedMarginStrategy();
+ sStrategy = new FixedMarginStrategy(prefs);
break;
case 1:
- sStrategy = new VelocityBiasStrategy();
+ sStrategy = new VelocityBiasStrategy(prefs);
break;
case 2:
- sStrategy = new DynamicResolutionStrategy();
+ sStrategy = new DynamicResolutionStrategy(prefs);
break;
case 3:
- sStrategy = new NoMarginStrategy();
+ sStrategy = new NoMarginStrategy(prefs);
+ break;
+ case 4:
+ sStrategy = new PredictionBiasStrategy(prefs);
break;
+ default:
+ Log.e(LOGTAG, "Invalid strategy index specified");
+ return false;
}
- Log.i(LOGTAG, "Set strategy " + sStrategy.getClass().getName());
+ Log.i(LOGTAG, "Set strategy " + sStrategy.toString());
+ return true;
+ }
+
+ private static float getFloatPref(Map<String, Integer> prefs, String prefName, int defaultValue) {
+ Integer value = (prefs == null ? null : prefs.get(prefName));
+ return (float)(value == null || value < 0 ? defaultValue : value) / 1000f;
}
- private interface DisplayPortStrategy {
+ private static abstract class DisplayPortStrategy {
/** Calculates a displayport given a viewport and panning velocity. */
- public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity);
+ public abstract DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity);
/** Returns true if a checkerboard is about to be visible and we should not throttle drawing. */
- public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort);
+ public abstract boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort);
+ /** Notify the strategy of a new recorded draw time. Return false to turn off draw time recording. */
+ public boolean drawTimeUpdate(long millis, int pixels) { return false; }
+ /** Reset any page-specific state stored, as the page being displayed has changed. */
+ public void resetPageState() {}
}
/**
@@ -100,6 +163,24 @@ final class DisplayPortCalculator {
}
/**
+ * Expand the given margins such that when they are applied on the viewport, the resulting rect
+ * does not have any partial tiles, except when it is clipped by the page bounds. This assumes
+ * the tiles are TILE_SIZE by TILE_SIZE and start at the origin, such that there will always be
+ * a tile at (0,0)-(TILE_SIZE,TILE_SIZE)).
+ */
+ private static DisplayPortMetrics getTileAlignedDisplayPortMetrics(RectF margins, float zoom, ImmutableViewportMetrics metrics) {
+ float left = metrics.viewportRectLeft - margins.left;
+ float top = metrics.viewportRectTop - margins.top;
+ float right = metrics.viewportRectRight + margins.right;
+ float bottom = metrics.viewportRectBottom + margins.bottom;
+ left = Math.max(0.0f, TILE_SIZE * FloatMath.floor(left / TILE_SIZE));
+ top = Math.max(0.0f, TILE_SIZE * FloatMath.floor(top / TILE_SIZE));
+ right = Math.min(metrics.pageSizeWidth, TILE_SIZE * FloatMath.ceil(right / TILE_SIZE));
+ bottom = Math.min(metrics.pageSizeHeight, TILE_SIZE * FloatMath.ceil(bottom / TILE_SIZE));
+ return new DisplayPortMetrics(left, top, right, bottom, zoom);
+ }
+
+ /**
* Adjust the given margins so if they are applied on the viewport in the metrics, the resulting rect
* does not exceed the page bounds. This code will maintain the total margin amount for a given axis;
* it assumes that margins.left + metrics.getWidth() + margins.right is less than or equal to
@@ -133,9 +214,24 @@ final class DisplayPortCalculator {
}
/**
+ * Clamp the given rect to the page bounds and return it.
+ */
+ private static RectF clampToPageBounds(RectF rect, ImmutableViewportMetrics metrics) {
+ rect.left = Math.max(rect.left, 0);
+ rect.top = Math.max(rect.top, 0);
+ rect.right = Math.min(rect.right, metrics.pageSizeWidth);
+ rect.bottom = Math.min(rect.bottom, metrics.pageSizeHeight);
+ return rect;
+ }
+
+ /**
* This class implements the variation where we basically don't bother with a a display port.
*/
- private static class NoMarginStrategy implements DisplayPortStrategy {
+ private static class NoMarginStrategy extends DisplayPortStrategy {
+ NoMarginStrategy(Map<String, Integer> prefs) {
+ // no prefs in this strategy
+ }
+
public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
return new DisplayPortMetrics(metrics.viewportRectLeft,
metrics.viewportRectTop,
@@ -147,6 +243,11 @@ final class DisplayPortCalculator {
public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
return true;
}
+
+ @Override
+ public String toString() {
+ return "NoMarginStrategy";
+ }
}
/**
@@ -157,15 +258,21 @@ final class DisplayPortCalculator {
* and/or (b) increasing the buffer on the other axis to compensate for the reduced buffer on
* one axis.
*/
- private static class FixedMarginStrategy implements DisplayPortStrategy {
+ private static class FixedMarginStrategy extends DisplayPortStrategy {
// The length of each axis of the display port will be the corresponding view length
// multiplied by this factor.
- private static final float SIZE_MULTIPLIER = 1.5f;
+ private final float SIZE_MULTIPLIER;
// If the visible rect is within the danger zone (measured as a fraction of the view size
// from the edge of the displayport) we start redrawing to minimize checkerboarding.
- private static final float DANGER_ZONE_X_MULTIPLIER = 0.10f;
- private static final float DANGER_ZONE_Y_MULTIPLIER = 0.20f;
+ private final float DANGER_ZONE_X_MULTIPLIER;
+ private final float DANGER_ZONE_Y_MULTIPLIER;
+
+ FixedMarginStrategy(Map<String, Integer> prefs) {
+ SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_MULTIPLIER, 2000);
+ DANGER_ZONE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_X, 100);
+ DANGER_ZONE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_Y, 200);
+ }
public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER;
@@ -191,15 +298,7 @@ final class DisplayPortCalculator {
margins.bottom = verticalBuffer - margins.top;
margins = shiftMarginsForPageBounds(margins, metrics);
- // note that unless the viewport size changes, or the page dimensions change (either because of
- // content changes or zooming), the size of the display port should remain constant. this
- // is intentional to avoid re-creating textures and all sorts of other reallocations in the
- // draw and composition code.
- return new DisplayPortMetrics(metrics.viewportRectLeft - margins.left,
- metrics.viewportRectTop - margins.top,
- metrics.viewportRectRight + margins.right,
- metrics.viewportRectBottom + margins.bottom,
- metrics.zoomFactor);
+ return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
}
public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
@@ -209,6 +308,11 @@ final class DisplayPortCalculator {
RectF adjustedViewport = expandByDangerZone(metrics.getViewport(), DANGER_ZONE_X_MULTIPLIER, DANGER_ZONE_Y_MULTIPLIER, metrics);
return !displayPort.contains(adjustedViewport);
}
+
+ @Override
+ public String toString() {
+ return "FixedMarginStrategy mult=" + SIZE_MULTIPLIER + ", dangerX=" + DANGER_ZONE_X_MULTIPLIER + ", dangerY=" + DANGER_ZONE_Y_MULTIPLIER;
+ }
}
/**
@@ -217,14 +321,67 @@ final class DisplayPortCalculator {
* they are affected by the panning velocity. Specifically, if we are panning on one axis,
* we remove the margins on the other axis because we are likely axis-locked. Also once
* we are panning in one direction above a certain threshold velocity, we shift the buffer
- * so that it is entirely in the direction of the pan.
+ * so that it is almost entirely in the direction of the pan, with a little bit in the
+ * reverse direction.
*/
- private static class VelocityBiasStrategy implements DisplayPortStrategy {
+ private static class VelocityBiasStrategy extends DisplayPortStrategy {
// The length of each axis of the display port will be the corresponding view length
// multiplied by this factor.
- private static final float SIZE_MULTIPLIER = 1.2f;
+ private final float SIZE_MULTIPLIER;
// The velocity above which we apply the velocity bias
- private static final float VELOCITY_THRESHOLD = LOKitShell.getDpi() / 32f;
+ private final float VELOCITY_THRESHOLD;
+ // How much of the buffer to keep in the reverse direction of the velocity
+ private final float REVERSE_BUFFER;
+ // If the visible rect is within the danger zone we start redrawing to minimize
+ // checkerboarding. the danger zone amount is a linear function of the form:
+ // viewportsize * (base + velocity * incr)
+ // where base and incr are configurable values.
+ private final float DANGER_ZONE_BASE_X_MULTIPLIER;
+ private final float DANGER_ZONE_BASE_Y_MULTIPLIER;
+ private final float DANGER_ZONE_INCR_X_MULTIPLIER;
+ private final float DANGER_ZONE_INCR_Y_MULTIPLIER;
+
+ VelocityBiasStrategy(Map<String, Integer> prefs) {
+ SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_MULTIPLIER, 2000);
+ VELOCITY_THRESHOLD = LOKitShell.getDpi() * getFloatPref(prefs, PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD, 32);
+ REVERSE_BUFFER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_REVERSE_BUFFER, 200);
+ DANGER_ZONE_BASE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_BASE, 1000);
+ DANGER_ZONE_BASE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_BASE, 1000);
+ DANGER_ZONE_INCR_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_INCR, 0);
+ DANGER_ZONE_INCR_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_INCR, 0);
+ }
+
+ /**
+ * Split the given amounts into margins based on the VELOCITY_THRESHOLD and REVERSE_BUFFER values.
+ * If the velocity is above the VELOCITY_THRESHOLD on an axis, split the amount into REVERSE_BUFFER
+ * and 1.0 - REVERSE_BUFFER fractions. The REVERSE_BUFFER fraction is set as the margin in the
+ * direction opposite to the velocity, and the remaining fraction is set as the margin in the direction
+ * of the velocity. If the velocity is lower than VELOCITY_THRESHOLD, split the amount evenly into the
+ * two margins on that axis.
+ */
+ private RectF velocityBiasedMargins(float xAmount, float yAmount, PointF velocity) {
+ RectF margins = new RectF();
+
+ if (velocity.x > VELOCITY_THRESHOLD) {
+ margins.left = xAmount * REVERSE_BUFFER;
+ } else if (velocity.x < -VELOCITY_THRESHOLD) {
+ margins.left = xAmount * (1.0f - REVERSE_BUFFER);
+ } else {
+ margins.left = xAmount / 2.0f;
+ }
+ margins.right = xAmount - margins.left;
+
+ if (velocity.y > VELOCITY_THRESHOLD) {
+ margins.top = yAmount * REVERSE_BUFFER;
+ } else if (velocity.y < -VELOCITY_THRESHOLD) {
+ margins.top = yAmount * (1.0f - REVERSE_BUFFER);
+ } else {
+ margins.top = yAmount / 2.0f;
+ }
+ margins.bottom = yAmount - margins.top;
+
+ return margins;
+ }
public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER;
@@ -245,43 +402,43 @@ final class DisplayPortCalculator {
float horizontalBuffer = displayPortWidth - metrics.getWidth();
float verticalBuffer = displayPortHeight - metrics.getHeight();
- // if we're panning above the VELOCITY_THRESHOLD on an axis, apply the margin so that it
- // is entirely in the direction of panning. Otherwise, split the margin evenly on both sides of
- // the display port.
- RectF margins = new RectF();
- if (velocity.x > VELOCITY_THRESHOLD) {
- margins.right = horizontalBuffer;
- } else if (velocity.x < -VELOCITY_THRESHOLD) {
- margins.left = horizontalBuffer;
- } else {
- margins.left = horizontalBuffer / 2.0f;
- margins.right = horizontalBuffer - margins.left;
- }
- if (velocity.y > VELOCITY_THRESHOLD) {
- margins.bottom = verticalBuffer;
- } else if (velocity.y < -VELOCITY_THRESHOLD) {
- margins.top = verticalBuffer;
- } else {
- margins.top = verticalBuffer / 2.0f;
- margins.bottom = verticalBuffer - margins.top;
- }
- // and finally shift the margins to account for page bounds
+ // split the buffer amounts into margins based on velocity, and shift it to
+ // take into account the page bounds
+ RectF margins = velocityBiasedMargins(horizontalBuffer, verticalBuffer, velocity);
margins = shiftMarginsForPageBounds(margins, metrics);
- return new DisplayPortMetrics(metrics.viewportRectLeft - margins.left,
- metrics.viewportRectTop - margins.top,
- metrics.viewportRectRight + margins.right,
- metrics.viewportRectBottom + margins.bottom,
- metrics.zoomFactor);
+ return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
}
public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
- // Since we have such a small margin, we want to be drawing more aggressively. At the start of a
- // pan the velocity is going to be large so we're almost certainly going to go into checkerboard
- // on every frame, so drawing all the time seems like the right thing. At the end of the pan we
- // want to re-center the displayport and draw stuff on all sides, so again we don't want to throttle
- // there. When we're not panning we're not drawing anyway so it doesn't make a difference there.
- return true;
+ // calculate the danger zone amounts based on the prefs
+ float dangerZoneX = metrics.getWidth() * (DANGER_ZONE_BASE_X_MULTIPLIER + (velocity.x * DANGER_ZONE_INCR_X_MULTIPLIER));
+ float dangerZoneY = metrics.getHeight() * (DANGER_ZONE_BASE_Y_MULTIPLIER + (velocity.y * DANGER_ZONE_INCR_Y_MULTIPLIER));
+ // clamp it such that when added to the viewport, they don't exceed page size.
+ // this is a prerequisite to calling shiftMarginsForPageBounds as we do below.
+ dangerZoneX = Math.min(dangerZoneX, metrics.pageSizeWidth - metrics.getWidth());
+ dangerZoneY = Math.min(dangerZoneY, metrics.pageSizeHeight - metrics.getHeight());
+
+ // split the danger zone into margins based on velocity, and ensure it doesn't exceed
+ // page bounds.
+ RectF dangerMargins = velocityBiasedMargins(dangerZoneX, dangerZoneY, velocity);
+ dangerMargins = shiftMarginsForPageBounds(dangerMargins, metrics);
+
+ // we're about to checkerboard if the current viewport area + the danger zone margins
+ // fall out of the current displayport anywhere.
+ RectF adjustedViewport = new RectF(
+ metrics.viewportRectLeft - dangerMargins.left,
+ metrics.viewportRectTop - dangerMargins.top,
+ metrics.viewportRectRight + dangerMargins.right,
+ metrics.viewportRectBottom + dangerMargins.bottom);
+ return !displayPort.contains(adjustedViewport);
+ }
+
+ @Override
+ public String toString() {
+ return "VelocityBiasStrategy mult=" + SIZE_MULTIPLIER + ", threshold=" + VELOCITY_THRESHOLD + ", reverse=" + REVERSE_BUFFER
+ + ", dangerBaseX=" + DANGER_ZONE_BASE_X_MULTIPLIER + ", dangerBaseY=" + DANGER_ZONE_BASE_Y_MULTIPLIER
+ + ", dangerIncrX=" + DANGER_ZONE_INCR_Y_MULTIPLIER + ", dangerIncrY=" + DANGER_ZONE_INCR_Y_MULTIPLIER;
}
}
@@ -293,7 +450,7 @@ final class DisplayPortCalculator {
* looks blurry. The assumption is that drawing extra that we never display is better than checkerboarding,
* where we draw less but never even show it on the screen.
*/
- private static class DynamicResolutionStrategy implements DisplayPortStrategy {
+ private static class DynamicResolutionStrategy extends DisplayPortStrategy {
// The length of each axis of the display port will be the corresponding view length
// multiplied by this factor.
private static final float SIZE_MULTIPLIER = 1.5f;
@@ -345,6 +502,10 @@ final class DisplayPortCalculator {
private static final float PREDICTION_VELOCITY_MULTIPLIER = 30.0f;
private static final float DANGER_ZONE_MULTIPLIER = 0.20f; // must be less than (SIZE_MULTIPLIER - 1.0f)
+ DynamicResolutionStrategy(Map<String, Integer> prefs) {
+ // ignore prefs for now
+ }
+
public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER;
float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER;
@@ -358,7 +519,7 @@ final class DisplayPortCalculator {
// relative aspect ratio.
if (velocity.length() > VELOCITY_EXPANSION_THRESHOLD) {
float velocityFactor = Math.max(Math.abs(velocity.x) / displayPortWidth,
- Math.abs(velocity.y) / displayPortHeight);
+ Math.abs(velocity.y) / displayPortHeight);
velocityFactor *= VELOCITY_MULTIPLIER;
displayPortWidth += (displayPortWidth * velocityFactor);
@@ -408,11 +569,11 @@ final class DisplayPortCalculator {
float displayResolution = metrics.zoomFactor * Math.min(1.0f, scaleFactor);
DisplayPortMetrics dpMetrics = new DisplayPortMetrics(
- metrics.viewportRectLeft - margins.left,
- metrics.viewportRectTop - margins.top,
- metrics.viewportRectRight + margins.right,
- metrics.viewportRectBottom + margins.bottom,
- displayResolution);
+ metrics.viewportRectLeft - margins.left,
+ metrics.viewportRectTop - margins.top,
+ metrics.viewportRectRight + margins.right,
+ metrics.viewportRectBottom + margins.bottom,
+ displayResolution);
return dpMetrics;
}
@@ -472,5 +633,123 @@ final class DisplayPortCalculator {
predictedViewport = expandByDangerZone(predictedViewport, DANGER_ZONE_MULTIPLIER, DANGER_ZONE_MULTIPLIER, metrics);
return !displayPort.contains(predictedViewport);
}
+
+ @Override
+ public String toString() {
+ return "DynamicResolutionStrategy";
+ }
+ }
+
+ /**
+ * This class implements the variation where we use the draw time to predict where we will be when
+ * a draw completes, and draw that instead of where we are now. In this variation, when our panning
+ * speed drops below a certain threshold, we draw 9 viewports' worth of content so that the user can
+ * pan in any direction without encountering checkerboarding.
+ * Once the user is panning, we modify the displayport to encompass an area range of where we think
+ * the user will be when the draw completes. This heuristic relies on both the estimated draw time
+ * the panning velocity; unexpected changes in either of these values will cause the heuristic to
+ * fail and show checkerboard.
+ */
+ private static class PredictionBiasStrategy extends DisplayPortStrategy {
+ private static float VELOCITY_THRESHOLD;
+
+ private int mPixelArea; // area of the viewport, used in draw time calculations
+ private int mMinFramesToDraw; // minimum number of frames we take to draw
+ private int mMaxFramesToDraw; // maximum number of frames we take to draw
+
+ PredictionBiasStrategy(Map<String, Integer> prefs) {
+ VELOCITY_THRESHOLD = LOKitShell.getDpi() * getFloatPref(prefs, PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD, 16);
+ resetPageState();
+ }
+
+ public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
+ float width = metrics.getWidth();
+ float height = metrics.getHeight();
+ mPixelArea = (int)(width * height);
+
+ if (velocity.length() < VELOCITY_THRESHOLD) {
+ // if we're going slow, expand the displayport to 9x viewport size
+ RectF margins = new RectF(width, height, width, height);
+ return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
+ }
+
+ // figure out how far we expect to be
+ float minDx = velocity.x * mMinFramesToDraw;
+ float minDy = velocity.y * mMinFramesToDraw;
+ float maxDx = velocity.x * mMaxFramesToDraw;
+ float maxDy = velocity.y * mMaxFramesToDraw;
+
+ // figure out how many pixels we will be drawing when we draw the above-calculated range.
+ // this will be larger than the viewport area.
+ float pixelsToDraw = (width + Math.abs(maxDx - minDx)) * (height + Math.abs(maxDy - minDy));
+ // adjust how far we will get because of the time spent drawing all these extra pixels. this
+ // will again increase the number of pixels drawn so really we could keep iterating this over
+ // and over, but once seems enough for now.
+ maxDx = maxDx * pixelsToDraw / mPixelArea;
+ maxDy = maxDy * pixelsToDraw / mPixelArea;
+
+ // and finally generate the displayport. the min/max stuff takes care of
+ // negative velocities as well as positive.
+ RectF margins = new RectF(
+ -Math.min(minDx, maxDx),
+ -Math.min(minDy, maxDy),
+ Math.max(minDx, maxDx),
+ Math.max(minDy, maxDy));
+ return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
+ }
+
+ public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
+ // the code below is the same as in calculate() but is awkward to refactor since it has multiple outputs.
+ // refer to the comments in calculate() to understand what this is doing.
+ float minDx = velocity.x * mMinFramesToDraw;
+ float minDy = velocity.y * mMinFramesToDraw;
+ float maxDx = velocity.x * mMaxFramesToDraw;
+ float maxDy = velocity.y * mMaxFramesToDraw;
+ float pixelsToDraw = (metrics.getWidth() + Math.abs(maxDx - minDx)) * (metrics.getHeight() + Math.abs(maxDy - minDy));
+ maxDx = maxDx * pixelsToDraw / mPixelArea;
+ maxDy = maxDy * pixelsToDraw / mPixelArea;
+
+ // now that we have an idea of how far we will be when the draw completes, take the farthest
+ // end of that range and see if it falls outside the displayport bounds. if it does, allow
+ // the draw to go through
+ RectF predictedViewport = metrics.getViewport();
+ predictedViewport.left += maxDx;
+ predictedViewport.top += maxDy;
+ predictedViewport.right += maxDx;
+ predictedViewport.bottom += maxDy;
+
+ predictedViewport = clampToPageBounds(predictedViewport, metrics);
+ return !displayPort.contains(predictedViewport);
+ }
+
+ @Override
+ public boolean drawTimeUpdate(long millis, int pixels) {
+ // calculate the number of frames it took to draw a viewport-sized area
+ float normalizedTime = (float)mPixelArea * (float)millis / (float)pixels;
+ int normalizedFrames = (int)FloatMath.ceil(normalizedTime * 60f / 1000f);
+ // broaden our range on how long it takes to draw if the draw falls outside
+ // the range. this allows it to grow gradually. this heuristic may need to
+ // be tweaked into more of a floating window average or something.
+ if (normalizedFrames <= mMinFramesToDraw) {
+ mMinFramesToDraw--;
+ } else if (normalizedFrames > mMaxFramesToDraw) {
+ mMaxFramesToDraw++;
+ } else {
+ return true;
+ }
+ Log.d(LOGTAG, "Widened draw range to [" + mMinFramesToDraw + ", " + mMaxFramesToDraw + "]");
+ return true;
+ }
+
+ @Override
+ public void resetPageState() {
+ mMinFramesToDraw = 0;
+ mMaxFramesToDraw = 2;
+ }
+
+ @Override
+ public String toString() {
+ return "PredictionBiasStrategy threshold=" + VELOCITY_THRESHOLD;
+ }
}
}
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java
index be06322d1a3b..3c9c39b8d60e 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java
@@ -6,6 +6,7 @@
package org.mozilla.gecko.gfx;
import android.graphics.RectF;
+import org.mozilla.gecko.util.FloatUtils;
/*
* This class keeps track of the area we request Gecko to paint, as well
@@ -32,6 +33,11 @@ public final class DisplayPortMetrics {
return mPosition.contains(rect);
}
+ public boolean fuzzyEquals(DisplayPortMetrics metrics) {
+ return RectUtils.fuzzyEquals(mPosition, metrics.mPosition)
+ && FloatUtils.fuzzyEquals(mResolution, metrics.mResolution);
+ }
+
public String toJSON() {
StringBuffer sb = new StringBuffer(256);
sb.append("{ \"left\": ").append(mPosition.left)
@@ -43,9 +49,10 @@ public final class DisplayPortMetrics {
return sb.toString();
}
+ @Override
public String toString() {
- return "DisplayPortMetrics(" + mPosition.left + ","
+ return "DisplayPortMetrics v=(" + mPosition.left + ","
+ mPosition.top + "," + mPosition.right + ","
- + mPosition.bottom + "," + mResolution + ")";
+ + mPosition.bottom + ") z=" + mResolution;
}
}