mobile/android/base/gfx/DisplayPortCalculator.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 package org.mozilla.gecko.gfx;
michael@0 7
michael@0 8 import org.mozilla.gecko.GeckoAppShell;
michael@0 9 import org.mozilla.gecko.PrefsHelper;
michael@0 10 import org.mozilla.gecko.util.FloatUtils;
michael@0 11
michael@0 12 import org.json.JSONArray;
michael@0 13
michael@0 14 import android.graphics.PointF;
michael@0 15 import android.graphics.RectF;
michael@0 16 import android.util.FloatMath;
michael@0 17 import android.util.Log;
michael@0 18
michael@0 19 import java.util.HashMap;
michael@0 20 import java.util.Map;
michael@0 21
michael@0 22 final class DisplayPortCalculator {
michael@0 23 private static final String LOGTAG = "GeckoDisplayPort";
michael@0 24 private static final PointF ZERO_VELOCITY = new PointF(0, 0);
michael@0 25
michael@0 26 // Keep this in sync with the TILEDLAYERBUFFER_TILE_SIZE defined in gfx/layers/TiledLayerBuffer.h
michael@0 27 private static final int TILE_SIZE = 256;
michael@0 28
michael@0 29 private static final String PREF_DISPLAYPORT_STRATEGY = "gfx.displayport.strategy";
michael@0 30 private static final String PREF_DISPLAYPORT_FM_MULTIPLIER = "gfx.displayport.strategy_fm.multiplier";
michael@0 31 private static final String PREF_DISPLAYPORT_FM_DANGER_X = "gfx.displayport.strategy_fm.danger_x";
michael@0 32 private static final String PREF_DISPLAYPORT_FM_DANGER_Y = "gfx.displayport.strategy_fm.danger_y";
michael@0 33 private static final String PREF_DISPLAYPORT_VB_MULTIPLIER = "gfx.displayport.strategy_vb.multiplier";
michael@0 34 private static final String PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_vb.threshold";
michael@0 35 private static final String PREF_DISPLAYPORT_VB_REVERSE_BUFFER = "gfx.displayport.strategy_vb.reverse_buffer";
michael@0 36 private static final String PREF_DISPLAYPORT_VB_DANGER_X_BASE = "gfx.displayport.strategy_vb.danger_x_base";
michael@0 37 private static final String PREF_DISPLAYPORT_VB_DANGER_Y_BASE = "gfx.displayport.strategy_vb.danger_y_base";
michael@0 38 private static final String PREF_DISPLAYPORT_VB_DANGER_X_INCR = "gfx.displayport.strategy_vb.danger_x_incr";
michael@0 39 private static final String PREF_DISPLAYPORT_VB_DANGER_Y_INCR = "gfx.displayport.strategy_vb.danger_y_incr";
michael@0 40 private static final String PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_pb.threshold";
michael@0 41
michael@0 42 private static DisplayPortStrategy sStrategy = new VelocityBiasStrategy(null);
michael@0 43
michael@0 44 static DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
michael@0 45 return sStrategy.calculate(metrics, (velocity == null ? ZERO_VELOCITY : velocity));
michael@0 46 }
michael@0 47
michael@0 48 static boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
michael@0 49 if (displayPort == null) {
michael@0 50 return true;
michael@0 51 }
michael@0 52 return sStrategy.aboutToCheckerboard(metrics, (velocity == null ? ZERO_VELOCITY : velocity), displayPort);
michael@0 53 }
michael@0 54
michael@0 55 static boolean drawTimeUpdate(long millis, int pixels) {
michael@0 56 return sStrategy.drawTimeUpdate(millis, pixels);
michael@0 57 }
michael@0 58
michael@0 59 static void resetPageState() {
michael@0 60 sStrategy.resetPageState();
michael@0 61 }
michael@0 62
michael@0 63 static void initPrefs() {
michael@0 64 final String[] prefs = { PREF_DISPLAYPORT_STRATEGY,
michael@0 65 PREF_DISPLAYPORT_FM_MULTIPLIER,
michael@0 66 PREF_DISPLAYPORT_FM_DANGER_X,
michael@0 67 PREF_DISPLAYPORT_FM_DANGER_Y,
michael@0 68 PREF_DISPLAYPORT_VB_MULTIPLIER,
michael@0 69 PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD,
michael@0 70 PREF_DISPLAYPORT_VB_REVERSE_BUFFER,
michael@0 71 PREF_DISPLAYPORT_VB_DANGER_X_BASE,
michael@0 72 PREF_DISPLAYPORT_VB_DANGER_Y_BASE,
michael@0 73 PREF_DISPLAYPORT_VB_DANGER_X_INCR,
michael@0 74 PREF_DISPLAYPORT_VB_DANGER_Y_INCR,
michael@0 75 PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD };
michael@0 76
michael@0 77 PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() {
michael@0 78 private Map<String, Integer> mValues = new HashMap<String, Integer>();
michael@0 79
michael@0 80 @Override public void prefValue(String pref, int value) {
michael@0 81 mValues.put(pref, value);
michael@0 82 }
michael@0 83
michael@0 84 @Override public void finish() {
michael@0 85 setStrategy(mValues);
michael@0 86 }
michael@0 87 });
michael@0 88 }
michael@0 89
michael@0 90 /**
michael@0 91 * Set the active strategy to use.
michael@0 92 * See the gfx.displayport.strategy pref in mobile/android/app/mobile.js to see the
michael@0 93 * mapping between ints and strategies.
michael@0 94 */
michael@0 95 static boolean setStrategy(Map<String, Integer> prefs) {
michael@0 96 Integer strategy = prefs.get(PREF_DISPLAYPORT_STRATEGY);
michael@0 97 if (strategy == null) {
michael@0 98 return false;
michael@0 99 }
michael@0 100
michael@0 101 switch (strategy) {
michael@0 102 case 0:
michael@0 103 sStrategy = new FixedMarginStrategy(prefs);
michael@0 104 break;
michael@0 105 case 1:
michael@0 106 sStrategy = new VelocityBiasStrategy(prefs);
michael@0 107 break;
michael@0 108 case 2:
michael@0 109 sStrategy = new DynamicResolutionStrategy(prefs);
michael@0 110 break;
michael@0 111 case 3:
michael@0 112 sStrategy = new NoMarginStrategy(prefs);
michael@0 113 break;
michael@0 114 case 4:
michael@0 115 sStrategy = new PredictionBiasStrategy(prefs);
michael@0 116 break;
michael@0 117 default:
michael@0 118 Log.e(LOGTAG, "Invalid strategy index specified");
michael@0 119 return false;
michael@0 120 }
michael@0 121 Log.i(LOGTAG, "Set strategy " + sStrategy.toString());
michael@0 122 return true;
michael@0 123 }
michael@0 124
michael@0 125 private static float getFloatPref(Map<String, Integer> prefs, String prefName, int defaultValue) {
michael@0 126 Integer value = (prefs == null ? null : prefs.get(prefName));
michael@0 127 return (float)(value == null || value < 0 ? defaultValue : value) / 1000f;
michael@0 128 }
michael@0 129
michael@0 130 private static abstract class DisplayPortStrategy {
michael@0 131 /** Calculates a displayport given a viewport and panning velocity. */
michael@0 132 public abstract DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity);
michael@0 133 /** Returns true if a checkerboard is about to be visible and we should not throttle drawing. */
michael@0 134 public abstract boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort);
michael@0 135 /** Notify the strategy of a new recorded draw time. Return false to turn off draw time recording. */
michael@0 136 public boolean drawTimeUpdate(long millis, int pixels) { return false; }
michael@0 137 /** Reset any page-specific state stored, as the page being displayed has changed. */
michael@0 138 public void resetPageState() {}
michael@0 139 }
michael@0 140
michael@0 141 /**
michael@0 142 * Return the dimensions for a rect that has area (width*height) that does not exceed the page size in the
michael@0 143 * given metrics object. The area in the returned FloatSize may be less than width*height if the page is
michael@0 144 * small, but it will never be larger than width*height.
michael@0 145 * Note that this process may change the relative aspect ratio of the given dimensions.
michael@0 146 */
michael@0 147 private static FloatSize reshapeForPage(float width, float height, ImmutableViewportMetrics metrics) {
michael@0 148 // figure out how much of the desired buffer amount we can actually use on the horizontal axis
michael@0 149 float usableWidth = Math.min(width, metrics.getPageWidth());
michael@0 150 // if we reduced the buffer amount on the horizontal axis, we should take that saved memory and
michael@0 151 // use it on the vertical axis
michael@0 152 float extraUsableHeight = (float)Math.floor(((width - usableWidth) * height) / usableWidth);
michael@0 153 float usableHeight = Math.min(height + extraUsableHeight, metrics.getPageHeight());
michael@0 154 if (usableHeight < height && usableWidth == width) {
michael@0 155 // and the reverse - if we shrunk the buffer on the vertical axis we can add it to the horizontal
michael@0 156 float extraUsableWidth = (float)Math.floor(((height - usableHeight) * width) / usableHeight);
michael@0 157 usableWidth = Math.min(width + extraUsableWidth, metrics.getPageWidth());
michael@0 158 }
michael@0 159 return new FloatSize(usableWidth, usableHeight);
michael@0 160 }
michael@0 161
michael@0 162 /**
michael@0 163 * Expand the given rect in all directions by a "danger zone". The size of the danger zone on an axis
michael@0 164 * is the size of the view on that axis multiplied by the given multiplier. The expanded rect is then
michael@0 165 * clamped to page bounds and returned.
michael@0 166 */
michael@0 167 private static RectF expandByDangerZone(RectF rect, float dangerZoneXMultiplier, float dangerZoneYMultiplier, ImmutableViewportMetrics metrics) {
michael@0 168 // calculate the danger zone amounts in pixels
michael@0 169 float dangerZoneX = metrics.getWidth() * dangerZoneXMultiplier;
michael@0 170 float dangerZoneY = metrics.getHeight() * dangerZoneYMultiplier;
michael@0 171 rect = RectUtils.expand(rect, dangerZoneX, dangerZoneY);
michael@0 172 // clamp to page bounds
michael@0 173 return clampToPageBounds(rect, metrics);
michael@0 174 }
michael@0 175
michael@0 176 /**
michael@0 177 * Expand the given margins such that when they are applied on the viewport, the resulting rect
michael@0 178 * does not have any partial tiles, except when it is clipped by the page bounds. This assumes
michael@0 179 * the tiles are TILE_SIZE by TILE_SIZE and start at the origin, such that there will always be
michael@0 180 * a tile at (0,0)-(TILE_SIZE,TILE_SIZE)).
michael@0 181 */
michael@0 182 private static DisplayPortMetrics getTileAlignedDisplayPortMetrics(RectF margins, float zoom, ImmutableViewportMetrics metrics) {
michael@0 183 float left = metrics.viewportRectLeft - margins.left;
michael@0 184 float top = metrics.viewportRectTop - margins.top;
michael@0 185 float right = metrics.viewportRectRight + margins.right;
michael@0 186 float bottom = metrics.viewportRectBottom + margins.bottom;
michael@0 187 left = Math.max(metrics.pageRectLeft, TILE_SIZE * FloatMath.floor(left / TILE_SIZE));
michael@0 188 top = Math.max(metrics.pageRectTop, TILE_SIZE * FloatMath.floor(top / TILE_SIZE));
michael@0 189 right = Math.min(metrics.pageRectRight, TILE_SIZE * FloatMath.ceil(right / TILE_SIZE));
michael@0 190 bottom = Math.min(metrics.pageRectBottom, TILE_SIZE * FloatMath.ceil(bottom / TILE_SIZE));
michael@0 191 return new DisplayPortMetrics(left, top, right, bottom, zoom);
michael@0 192 }
michael@0 193
michael@0 194 /**
michael@0 195 * Adjust the given margins so if they are applied on the viewport in the metrics, the resulting rect
michael@0 196 * does not exceed the page bounds. This code will maintain the total margin amount for a given axis;
michael@0 197 * it assumes that margins.left + metrics.getWidth() + margins.right is less than or equal to
michael@0 198 * metrics.getPageWidth(); and the same for the y axis.
michael@0 199 */
michael@0 200 private static RectF shiftMarginsForPageBounds(RectF margins, ImmutableViewportMetrics metrics) {
michael@0 201 // check how much we're overflowing in each direction. note that at most one of leftOverflow
michael@0 202 // and rightOverflow can be greater than zero, and at most one of topOverflow and bottomOverflow
michael@0 203 // can be greater than zero, because of the assumption described in the method javadoc.
michael@0 204 float leftOverflow = metrics.pageRectLeft - (metrics.viewportRectLeft - margins.left);
michael@0 205 float rightOverflow = (metrics.viewportRectRight + margins.right) - metrics.pageRectRight;
michael@0 206 float topOverflow = metrics.pageRectTop - (metrics.viewportRectTop - margins.top);
michael@0 207 float bottomOverflow = (metrics.viewportRectBottom + margins.bottom) - metrics.pageRectBottom;
michael@0 208
michael@0 209 // if the margins overflow the page bounds, shift them to other side on the same axis
michael@0 210 if (leftOverflow > 0) {
michael@0 211 margins.left -= leftOverflow;
michael@0 212 margins.right += leftOverflow;
michael@0 213 } else if (rightOverflow > 0) {
michael@0 214 margins.right -= rightOverflow;
michael@0 215 margins.left += rightOverflow;
michael@0 216 }
michael@0 217 if (topOverflow > 0) {
michael@0 218 margins.top -= topOverflow;
michael@0 219 margins.bottom += topOverflow;
michael@0 220 } else if (bottomOverflow > 0) {
michael@0 221 margins.bottom -= bottomOverflow;
michael@0 222 margins.top += bottomOverflow;
michael@0 223 }
michael@0 224 return margins;
michael@0 225 }
michael@0 226
michael@0 227 /**
michael@0 228 * Clamp the given rect to the page bounds and return it.
michael@0 229 */
michael@0 230 private static RectF clampToPageBounds(RectF rect, ImmutableViewportMetrics metrics) {
michael@0 231 if (rect.top < metrics.pageRectTop) rect.top = metrics.pageRectTop;
michael@0 232 if (rect.left < metrics.pageRectLeft) rect.left = metrics.pageRectLeft;
michael@0 233 if (rect.right > metrics.pageRectRight) rect.right = metrics.pageRectRight;
michael@0 234 if (rect.bottom > metrics.pageRectBottom) rect.bottom = metrics.pageRectBottom;
michael@0 235 return rect;
michael@0 236 }
michael@0 237
michael@0 238 /**
michael@0 239 * This class implements the variation where we basically don't bother with a a display port.
michael@0 240 */
michael@0 241 private static class NoMarginStrategy extends DisplayPortStrategy {
michael@0 242 NoMarginStrategy(Map<String, Integer> prefs) {
michael@0 243 // no prefs in this strategy
michael@0 244 }
michael@0 245
michael@0 246 @Override
michael@0 247 public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
michael@0 248 return new DisplayPortMetrics(metrics.viewportRectLeft,
michael@0 249 metrics.viewportRectTop,
michael@0 250 metrics.viewportRectRight,
michael@0 251 metrics.viewportRectBottom,
michael@0 252 metrics.zoomFactor);
michael@0 253 }
michael@0 254
michael@0 255 @Override
michael@0 256 public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
michael@0 257 return true;
michael@0 258 }
michael@0 259
michael@0 260 @Override
michael@0 261 public String toString() {
michael@0 262 return "NoMarginStrategy";
michael@0 263 }
michael@0 264 }
michael@0 265
michael@0 266 /**
michael@0 267 * This class implements the variation where we use a fixed-size margin on the display port.
michael@0 268 * The margin is always 300 pixels in all directions, except when we are (a) approaching a page
michael@0 269 * boundary, and/or (b) if we are limited by the page size. In these cases we try to maintain
michael@0 270 * the area of the display port by (a) shifting the buffer to the other side on the same axis,
michael@0 271 * and/or (b) increasing the buffer on the other axis to compensate for the reduced buffer on
michael@0 272 * one axis.
michael@0 273 */
michael@0 274 private static class FixedMarginStrategy extends DisplayPortStrategy {
michael@0 275 // The length of each axis of the display port will be the corresponding view length
michael@0 276 // multiplied by this factor.
michael@0 277 private final float SIZE_MULTIPLIER;
michael@0 278
michael@0 279 // If the visible rect is within the danger zone (measured as a fraction of the view size
michael@0 280 // from the edge of the displayport) we start redrawing to minimize checkerboarding.
michael@0 281 private final float DANGER_ZONE_X_MULTIPLIER;
michael@0 282 private final float DANGER_ZONE_Y_MULTIPLIER;
michael@0 283
michael@0 284 FixedMarginStrategy(Map<String, Integer> prefs) {
michael@0 285 SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_MULTIPLIER, 2000);
michael@0 286 DANGER_ZONE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_X, 100);
michael@0 287 DANGER_ZONE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_Y, 200);
michael@0 288 }
michael@0 289
michael@0 290 @Override
michael@0 291 public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
michael@0 292 float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER;
michael@0 293 float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER;
michael@0 294
michael@0 295 // we need to avoid having a display port that is larger than the page, or we will end up
michael@0 296 // painting things outside the page bounds (bug 729169). we simultaneously need to make
michael@0 297 // the display port as large as possible so that we redraw less. reshape the display
michael@0 298 // port dimensions to accomplish this.
michael@0 299 FloatSize usableSize = reshapeForPage(displayPortWidth, displayPortHeight, metrics);
michael@0 300 float horizontalBuffer = usableSize.width - metrics.getWidth();
michael@0 301 float verticalBuffer = usableSize.height - metrics.getHeight();
michael@0 302
michael@0 303 // and now calculate the display port margins based on how much buffer we've decided to use and
michael@0 304 // the page bounds, ensuring we use all of the available buffer amounts on one side or the other
michael@0 305 // on any given axis. (i.e. if we're scrolled to the top of the page, the vertical buffer is
michael@0 306 // entirely below the visible viewport, but if we're halfway down the page, the vertical buffer
michael@0 307 // is split).
michael@0 308 RectF margins = new RectF();
michael@0 309 margins.left = horizontalBuffer / 2.0f;
michael@0 310 margins.right = horizontalBuffer - margins.left;
michael@0 311 margins.top = verticalBuffer / 2.0f;
michael@0 312 margins.bottom = verticalBuffer - margins.top;
michael@0 313 margins = shiftMarginsForPageBounds(margins, metrics);
michael@0 314
michael@0 315 return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
michael@0 316 }
michael@0 317
michael@0 318 @Override
michael@0 319 public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
michael@0 320 // Increase the size of the viewport based on the danger zone multiplier (and clamp to page
michael@0 321 // boundaries), and intersect it with the current displayport to determine whether we're
michael@0 322 // close to checkerboarding.
michael@0 323 RectF adjustedViewport = expandByDangerZone(metrics.getViewport(), DANGER_ZONE_X_MULTIPLIER, DANGER_ZONE_Y_MULTIPLIER, metrics);
michael@0 324 return !displayPort.contains(adjustedViewport);
michael@0 325 }
michael@0 326
michael@0 327 @Override
michael@0 328 public String toString() {
michael@0 329 return "FixedMarginStrategy mult=" + SIZE_MULTIPLIER + ", dangerX=" + DANGER_ZONE_X_MULTIPLIER + ", dangerY=" + DANGER_ZONE_Y_MULTIPLIER;
michael@0 330 }
michael@0 331 }
michael@0 332
michael@0 333 /**
michael@0 334 * This class implements the variation with a small fixed-size margin with velocity bias.
michael@0 335 * In this variation, the default margins are pretty small relative to the view size, but
michael@0 336 * they are affected by the panning velocity. Specifically, if we are panning on one axis,
michael@0 337 * we remove the margins on the other axis because we are likely axis-locked. Also once
michael@0 338 * we are panning in one direction above a certain threshold velocity, we shift the buffer
michael@0 339 * so that it is almost entirely in the direction of the pan, with a little bit in the
michael@0 340 * reverse direction.
michael@0 341 */
michael@0 342 private static class VelocityBiasStrategy extends DisplayPortStrategy {
michael@0 343 // The length of each axis of the display port will be the corresponding view length
michael@0 344 // multiplied by this factor.
michael@0 345 private final float SIZE_MULTIPLIER;
michael@0 346 // The velocity above which we apply the velocity bias
michael@0 347 private final float VELOCITY_THRESHOLD;
michael@0 348 // How much of the buffer to keep in the reverse direction of the velocity
michael@0 349 private final float REVERSE_BUFFER;
michael@0 350 // If the visible rect is within the danger zone we start redrawing to minimize
michael@0 351 // checkerboarding. the danger zone amount is a linear function of the form:
michael@0 352 // viewportsize * (base + velocity * incr)
michael@0 353 // where base and incr are configurable values.
michael@0 354 private final float DANGER_ZONE_BASE_X_MULTIPLIER;
michael@0 355 private final float DANGER_ZONE_BASE_Y_MULTIPLIER;
michael@0 356 private final float DANGER_ZONE_INCR_X_MULTIPLIER;
michael@0 357 private final float DANGER_ZONE_INCR_Y_MULTIPLIER;
michael@0 358
michael@0 359 VelocityBiasStrategy(Map<String, Integer> prefs) {
michael@0 360 SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_MULTIPLIER, 2000);
michael@0 361 VELOCITY_THRESHOLD = GeckoAppShell.getDpi() * getFloatPref(prefs, PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD, 32);
michael@0 362 REVERSE_BUFFER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_REVERSE_BUFFER, 200);
michael@0 363 DANGER_ZONE_BASE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_BASE, 1000);
michael@0 364 DANGER_ZONE_BASE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_BASE, 1000);
michael@0 365 DANGER_ZONE_INCR_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_INCR, 0);
michael@0 366 DANGER_ZONE_INCR_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_INCR, 0);
michael@0 367 }
michael@0 368
michael@0 369 /**
michael@0 370 * Split the given amounts into margins based on the VELOCITY_THRESHOLD and REVERSE_BUFFER values.
michael@0 371 * If the velocity is above the VELOCITY_THRESHOLD on an axis, split the amount into REVERSE_BUFFER
michael@0 372 * and 1.0 - REVERSE_BUFFER fractions. The REVERSE_BUFFER fraction is set as the margin in the
michael@0 373 * direction opposite to the velocity, and the remaining fraction is set as the margin in the direction
michael@0 374 * of the velocity. If the velocity is lower than VELOCITY_THRESHOLD, split the amount evenly into the
michael@0 375 * two margins on that axis.
michael@0 376 */
michael@0 377 private RectF velocityBiasedMargins(float xAmount, float yAmount, PointF velocity) {
michael@0 378 RectF margins = new RectF();
michael@0 379
michael@0 380 if (velocity.x > VELOCITY_THRESHOLD) {
michael@0 381 margins.left = xAmount * REVERSE_BUFFER;
michael@0 382 } else if (velocity.x < -VELOCITY_THRESHOLD) {
michael@0 383 margins.left = xAmount * (1.0f - REVERSE_BUFFER);
michael@0 384 } else {
michael@0 385 margins.left = xAmount / 2.0f;
michael@0 386 }
michael@0 387 margins.right = xAmount - margins.left;
michael@0 388
michael@0 389 if (velocity.y > VELOCITY_THRESHOLD) {
michael@0 390 margins.top = yAmount * REVERSE_BUFFER;
michael@0 391 } else if (velocity.y < -VELOCITY_THRESHOLD) {
michael@0 392 margins.top = yAmount * (1.0f - REVERSE_BUFFER);
michael@0 393 } else {
michael@0 394 margins.top = yAmount / 2.0f;
michael@0 395 }
michael@0 396 margins.bottom = yAmount - margins.top;
michael@0 397
michael@0 398 return margins;
michael@0 399 }
michael@0 400
michael@0 401 @Override
michael@0 402 public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
michael@0 403 float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER;
michael@0 404 float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER;
michael@0 405
michael@0 406 // but if we're panning on one axis, set the margins for the other axis to zero since we are likely
michael@0 407 // axis locked and won't be displaying that extra area.
michael@0 408 if (Math.abs(velocity.x) > VELOCITY_THRESHOLD && FloatUtils.fuzzyEquals(velocity.y, 0)) {
michael@0 409 displayPortHeight = metrics.getHeight();
michael@0 410 } else if (Math.abs(velocity.y) > VELOCITY_THRESHOLD && FloatUtils.fuzzyEquals(velocity.x, 0)) {
michael@0 411 displayPortWidth = metrics.getWidth();
michael@0 412 }
michael@0 413
michael@0 414 // we need to avoid having a display port that is larger than the page, or we will end up
michael@0 415 // painting things outside the page bounds (bug 729169).
michael@0 416 displayPortWidth = Math.min(displayPortWidth, metrics.getPageWidth());
michael@0 417 displayPortHeight = Math.min(displayPortHeight, metrics.getPageHeight());
michael@0 418 float horizontalBuffer = displayPortWidth - metrics.getWidth();
michael@0 419 float verticalBuffer = displayPortHeight - metrics.getHeight();
michael@0 420
michael@0 421 // split the buffer amounts into margins based on velocity, and shift it to
michael@0 422 // take into account the page bounds
michael@0 423 RectF margins = velocityBiasedMargins(horizontalBuffer, verticalBuffer, velocity);
michael@0 424 margins = shiftMarginsForPageBounds(margins, metrics);
michael@0 425
michael@0 426 return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
michael@0 427 }
michael@0 428
michael@0 429 @Override
michael@0 430 public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
michael@0 431 // calculate the danger zone amounts based on the prefs
michael@0 432 float dangerZoneX = metrics.getWidth() * (DANGER_ZONE_BASE_X_MULTIPLIER + (velocity.x * DANGER_ZONE_INCR_X_MULTIPLIER));
michael@0 433 float dangerZoneY = metrics.getHeight() * (DANGER_ZONE_BASE_Y_MULTIPLIER + (velocity.y * DANGER_ZONE_INCR_Y_MULTIPLIER));
michael@0 434 // clamp it such that when added to the viewport, they don't exceed page size.
michael@0 435 // this is a prerequisite to calling shiftMarginsForPageBounds as we do below.
michael@0 436 dangerZoneX = Math.min(dangerZoneX, metrics.getPageWidth() - metrics.getWidth());
michael@0 437 dangerZoneY = Math.min(dangerZoneY, metrics.getPageHeight() - metrics.getHeight());
michael@0 438
michael@0 439 // split the danger zone into margins based on velocity, and ensure it doesn't exceed
michael@0 440 // page bounds.
michael@0 441 RectF dangerMargins = velocityBiasedMargins(dangerZoneX, dangerZoneY, velocity);
michael@0 442 dangerMargins = shiftMarginsForPageBounds(dangerMargins, metrics);
michael@0 443
michael@0 444 // we're about to checkerboard if the current viewport area + the danger zone margins
michael@0 445 // fall out of the current displayport anywhere.
michael@0 446 RectF adjustedViewport = new RectF(
michael@0 447 metrics.viewportRectLeft - dangerMargins.left,
michael@0 448 metrics.viewportRectTop - dangerMargins.top,
michael@0 449 metrics.viewportRectRight + dangerMargins.right,
michael@0 450 metrics.viewportRectBottom + dangerMargins.bottom);
michael@0 451 return !displayPort.contains(adjustedViewport);
michael@0 452 }
michael@0 453
michael@0 454 @Override
michael@0 455 public String toString() {
michael@0 456 return "VelocityBiasStrategy mult=" + SIZE_MULTIPLIER + ", threshold=" + VELOCITY_THRESHOLD + ", reverse=" + REVERSE_BUFFER
michael@0 457 + ", dangerBaseX=" + DANGER_ZONE_BASE_X_MULTIPLIER + ", dangerBaseY=" + DANGER_ZONE_BASE_Y_MULTIPLIER
michael@0 458 + ", dangerIncrX=" + DANGER_ZONE_INCR_Y_MULTIPLIER + ", dangerIncrY=" + DANGER_ZONE_INCR_Y_MULTIPLIER;
michael@0 459 }
michael@0 460 }
michael@0 461
michael@0 462 /**
michael@0 463 * This class implements the variation where we draw more of the page at low resolution while panning.
michael@0 464 * In this variation, as we pan faster, we increase the page area we are drawing, but reduce the draw
michael@0 465 * resolution to compensate. This results in the same device-pixel area drawn; the compositor then
michael@0 466 * scales this up to the viewport zoom level. This results in a large area of the page drawn but it
michael@0 467 * looks blurry. The assumption is that drawing extra that we never display is better than checkerboarding,
michael@0 468 * where we draw less but never even show it on the screen.
michael@0 469 */
michael@0 470 private static class DynamicResolutionStrategy extends DisplayPortStrategy {
michael@0 471 // The length of each axis of the display port will be the corresponding view length
michael@0 472 // multiplied by this factor.
michael@0 473 private static final float SIZE_MULTIPLIER = 1.5f;
michael@0 474
michael@0 475 // The velocity above which we start zooming out the display port to keep up
michael@0 476 // with the panning.
michael@0 477 private static final float VELOCITY_EXPANSION_THRESHOLD = GeckoAppShell.getDpi() / 16f;
michael@0 478
michael@0 479 // How much we increase the display port based on velocity. Assuming no friction and
michael@0 480 // splitting (see below), this should be be the number of frames (@60fps) between us
michael@0 481 // calculating the display port and the draw of the *next* display port getting composited
michael@0 482 // and displayed on the screen. This is because the timeline looks like this:
michael@0 483 // Java: pan pan pan pan pan pan ! pan pan pan pan pan pan !
michael@0 484 // Gecko: \-> draw -> composite / \-> draw -> composite /
michael@0 485 // The display port calculated on the first "pan" gets composited to the screen at the
michael@0 486 // first exclamation mark, and remains on the screen until the second exclamation mark.
michael@0 487 // In order to avoid checkerboarding, that display port must be able to contain all of
michael@0 488 // the panning until the second exclamation mark, which encompasses two entire draw/composite
michael@0 489 // cycles.
michael@0 490 // If we take into account friction, our velocity multiplier should be reduced as the
michael@0 491 // amount of pan will decrease each time. If we take into account display port splitting,
michael@0 492 // it should be increased as the splitting means some of the display port will be used to
michael@0 493 // draw in the opposite direction of the velocity. For now I'm assuming these two cancel
michael@0 494 // each other out.
michael@0 495 private static final float VELOCITY_MULTIPLIER = 60.0f;
michael@0 496
michael@0 497 // The following constants adjust how biased the display port is in the direction of panning.
michael@0 498 // When panning fast (above the FAST_THRESHOLD) we use the fast split factor to split the
michael@0 499 // display port "buffer" area, otherwise we use the slow split factor. This is based on the
michael@0 500 // assumption that if the user is panning fast, they are less likely to reverse directions
michael@0 501 // and go backwards, so we should spend more of our display port buffer in the direction of
michael@0 502 // panning.
michael@0 503 private static final float VELOCITY_FAST_THRESHOLD = VELOCITY_EXPANSION_THRESHOLD * 2.0f;
michael@0 504 private static final float FAST_SPLIT_FACTOR = 0.95f;
michael@0 505 private static final float SLOW_SPLIT_FACTOR = 0.8f;
michael@0 506
michael@0 507 // The following constants are used for viewport prediction; we use them to estimate where
michael@0 508 // the viewport will be soon and whether or not we should trigger a draw right now. "soon"
michael@0 509 // in the previous sentence really refers to the amount of time it would take to draw and
michael@0 510 // composite from the point at which we do the calculation, and that is not really a known
michael@0 511 // quantity. The velocity multiplier is how much we multiply the velocity by; it has the
michael@0 512 // same caveats as the VELOCITY_MULTIPLIER above except that it only needs to take into account
michael@0 513 // one draw/composite cycle instead of two. The danger zone multiplier is a multiplier of the
michael@0 514 // viewport size that we use as an extra "danger zone" around the viewport; if this danger
michael@0 515 // zone falls outside the display port then we are approaching the point at which we will
michael@0 516 // checkerboard, and hence should start drawing. Note that if DANGER_ZONE_MULTIPLIER is
michael@0 517 // greater than (SIZE_MULTIPLIER - 1.0f), then at zero velocity we will always be in the
michael@0 518 // danger zone, and thus will be constantly drawing.
michael@0 519 private static final float PREDICTION_VELOCITY_MULTIPLIER = 30.0f;
michael@0 520 private static final float DANGER_ZONE_MULTIPLIER = 0.20f; // must be less than (SIZE_MULTIPLIER - 1.0f)
michael@0 521
michael@0 522 DynamicResolutionStrategy(Map<String, Integer> prefs) {
michael@0 523 // ignore prefs for now
michael@0 524 }
michael@0 525
michael@0 526 @Override
michael@0 527 public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
michael@0 528 float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER;
michael@0 529 float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER;
michael@0 530
michael@0 531 // for resolution calculation purposes, we need to know what the adjusted display port dimensions
michael@0 532 // would be if we had zero velocity, so calculate that here before we increase the display port
michael@0 533 // based on velocity.
michael@0 534 FloatSize reshapedSize = reshapeForPage(displayPortWidth, displayPortHeight, metrics);
michael@0 535
michael@0 536 // increase displayPortWidth and displayPortHeight based on the velocity, but maintaining their
michael@0 537 // relative aspect ratio.
michael@0 538 if (velocity.length() > VELOCITY_EXPANSION_THRESHOLD) {
michael@0 539 float velocityFactor = Math.max(Math.abs(velocity.x) / displayPortWidth,
michael@0 540 Math.abs(velocity.y) / displayPortHeight);
michael@0 541 velocityFactor *= VELOCITY_MULTIPLIER;
michael@0 542
michael@0 543 displayPortWidth += (displayPortWidth * velocityFactor);
michael@0 544 displayPortHeight += (displayPortHeight * velocityFactor);
michael@0 545 }
michael@0 546
michael@0 547 // at this point, displayPortWidth and displayPortHeight are how much of the page (in device pixels)
michael@0 548 // we want to be rendered by Gecko. Note here "device pixels" is equivalent to CSS pixels multiplied
michael@0 549 // by metrics.zoomFactor
michael@0 550
michael@0 551 // we need to avoid having a display port that is larger than the page, or we will end up
michael@0 552 // painting things outside the page bounds (bug 729169). we simultaneously need to make
michael@0 553 // the display port as large as possible so that we redraw less. reshape the display
michael@0 554 // port dimensions to accomplish this. this may change the aspect ratio of the display port,
michael@0 555 // but we are assuming that this is desirable because the advantages from pre-drawing will
michael@0 556 // outweigh the disadvantages from any buffer reallocations that might occur.
michael@0 557 FloatSize usableSize = reshapeForPage(displayPortWidth, displayPortHeight, metrics);
michael@0 558 float horizontalBuffer = usableSize.width - metrics.getWidth();
michael@0 559 float verticalBuffer = usableSize.height - metrics.getHeight();
michael@0 560
michael@0 561 // at this point, horizontalBuffer and verticalBuffer are the dimensions of the buffer area we have.
michael@0 562 // the buffer area is the off-screen area that is part of the display port and will be pre-drawn in case
michael@0 563 // the user scrolls there. we now need to split the buffer area on each axis so that we know
michael@0 564 // what the exact margins on each side will be. first we split the buffer amount based on the direction
michael@0 565 // we're moving, so that we have a larger buffer in the direction of travel.
michael@0 566 RectF margins = new RectF();
michael@0 567 margins.left = splitBufferByVelocity(horizontalBuffer, velocity.x);
michael@0 568 margins.right = horizontalBuffer - margins.left;
michael@0 569 margins.top = splitBufferByVelocity(verticalBuffer, velocity.y);
michael@0 570 margins.bottom = verticalBuffer - margins.top;
michael@0 571
michael@0 572 // then, we account for running into the page bounds - so that if we hit the top of the page, we need
michael@0 573 // to drop the top margin and move that amount to the bottom margin.
michael@0 574 margins = shiftMarginsForPageBounds(margins, metrics);
michael@0 575
michael@0 576 // finally, we calculate the resolution we want to render the display port area at. We do this
michael@0 577 // so that as we expand the display port area (because of velocity), we reduce the resolution of
michael@0 578 // the painted area so as to maintain the size of the buffer Gecko is painting into. we calculate
michael@0 579 // the reduction in resolution by comparing the display port size with and without the velocity
michael@0 580 // changes applied.
michael@0 581 // this effectively means that as we pan faster and faster, the display port grows, but we paint
michael@0 582 // at lower resolutions. this paints more area to reduce checkerboard at the cost of increasing
michael@0 583 // compositor-scaling and blurriness. Once we stop panning, the blurriness must be entirely gone.
michael@0 584 // Note that usable* could be less than base* if we are pinch-zoomed out into overscroll, so we
michael@0 585 // clamp it to make sure this doesn't increase our display resolution past metrics.zoomFactor.
michael@0 586 float scaleFactor = Math.min(reshapedSize.width / usableSize.width, reshapedSize.height / usableSize.height);
michael@0 587 float displayResolution = metrics.zoomFactor * Math.min(1.0f, scaleFactor);
michael@0 588
michael@0 589 DisplayPortMetrics dpMetrics = new DisplayPortMetrics(
michael@0 590 metrics.viewportRectLeft - margins.left,
michael@0 591 metrics.viewportRectTop - margins.top,
michael@0 592 metrics.viewportRectRight + margins.right,
michael@0 593 metrics.viewportRectBottom + margins.bottom,
michael@0 594 displayResolution);
michael@0 595 return dpMetrics;
michael@0 596 }
michael@0 597
michael@0 598 /**
michael@0 599 * Split the given buffer amount into two based on the velocity.
michael@0 600 * Given an amount of total usable buffer on an axis, this will
michael@0 601 * return the amount that should be used on the left/top side of
michael@0 602 * the axis (the side which a negative velocity vector corresponds
michael@0 603 * to).
michael@0 604 */
michael@0 605 private float splitBufferByVelocity(float amount, float velocity) {
michael@0 606 // if no velocity, so split evenly
michael@0 607 if (FloatUtils.fuzzyEquals(velocity, 0)) {
michael@0 608 return amount / 2.0f;
michael@0 609 }
michael@0 610 // if we're moving quickly, assign more of the amount in that direction
michael@0 611 // since is is less likely that we will reverse direction immediately
michael@0 612 if (velocity < -VELOCITY_FAST_THRESHOLD) {
michael@0 613 return amount * FAST_SPLIT_FACTOR;
michael@0 614 }
michael@0 615 if (velocity > VELOCITY_FAST_THRESHOLD) {
michael@0 616 return amount * (1.0f - FAST_SPLIT_FACTOR);
michael@0 617 }
michael@0 618 // if we're moving slowly, then assign less of the amount in that direction
michael@0 619 if (velocity < 0) {
michael@0 620 return amount * SLOW_SPLIT_FACTOR;
michael@0 621 } else {
michael@0 622 return amount * (1.0f - SLOW_SPLIT_FACTOR);
michael@0 623 }
michael@0 624 }
michael@0 625
michael@0 626 @Override
michael@0 627 public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
michael@0 628 // Expand the viewport based on our velocity (and clamp it to page boundaries).
michael@0 629 // Then intersect it with the last-requested displayport to determine whether we're
michael@0 630 // close to checkerboarding.
michael@0 631
michael@0 632 RectF predictedViewport = metrics.getViewport();
michael@0 633
michael@0 634 // first we expand the viewport in the direction we're moving based on some
michael@0 635 // multiple of the current velocity.
michael@0 636 if (velocity.length() > 0) {
michael@0 637 if (velocity.x < 0) {
michael@0 638 predictedViewport.left += velocity.x * PREDICTION_VELOCITY_MULTIPLIER;
michael@0 639 } else if (velocity.x > 0) {
michael@0 640 predictedViewport.right += velocity.x * PREDICTION_VELOCITY_MULTIPLIER;
michael@0 641 }
michael@0 642
michael@0 643 if (velocity.y < 0) {
michael@0 644 predictedViewport.top += velocity.y * PREDICTION_VELOCITY_MULTIPLIER;
michael@0 645 } else if (velocity.y > 0) {
michael@0 646 predictedViewport.bottom += velocity.y * PREDICTION_VELOCITY_MULTIPLIER;
michael@0 647 }
michael@0 648 }
michael@0 649
michael@0 650 // then we expand the viewport evenly in all directions just to have an extra
michael@0 651 // safety zone. this also clamps it to page bounds.
michael@0 652 predictedViewport = expandByDangerZone(predictedViewport, DANGER_ZONE_MULTIPLIER, DANGER_ZONE_MULTIPLIER, metrics);
michael@0 653 return !displayPort.contains(predictedViewport);
michael@0 654 }
michael@0 655
michael@0 656 @Override
michael@0 657 public String toString() {
michael@0 658 return "DynamicResolutionStrategy";
michael@0 659 }
michael@0 660 }
michael@0 661
michael@0 662 /**
michael@0 663 * This class implements the variation where we use the draw time to predict where we will be when
michael@0 664 * a draw completes, and draw that instead of where we are now. In this variation, when our panning
michael@0 665 * speed drops below a certain threshold, we draw 9 viewports' worth of content so that the user can
michael@0 666 * pan in any direction without encountering checkerboarding.
michael@0 667 * Once the user is panning, we modify the displayport to encompass an area range of where we think
michael@0 668 * the user will be when the draw completes. This heuristic relies on both the estimated draw time
michael@0 669 * the panning velocity; unexpected changes in either of these values will cause the heuristic to
michael@0 670 * fail and show checkerboard.
michael@0 671 */
michael@0 672 private static class PredictionBiasStrategy extends DisplayPortStrategy {
michael@0 673 private static float VELOCITY_THRESHOLD;
michael@0 674
michael@0 675 private int mPixelArea; // area of the viewport, used in draw time calculations
michael@0 676 private int mMinFramesToDraw; // minimum number of frames we take to draw
michael@0 677 private int mMaxFramesToDraw; // maximum number of frames we take to draw
michael@0 678
michael@0 679 PredictionBiasStrategy(Map<String, Integer> prefs) {
michael@0 680 VELOCITY_THRESHOLD = GeckoAppShell.getDpi() * getFloatPref(prefs, PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD, 16);
michael@0 681 resetPageState();
michael@0 682 }
michael@0 683
michael@0 684 @Override
michael@0 685 public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
michael@0 686 float width = metrics.getWidth();
michael@0 687 float height = metrics.getHeight();
michael@0 688 mPixelArea = (int)(width * height);
michael@0 689
michael@0 690 if (velocity.length() < VELOCITY_THRESHOLD) {
michael@0 691 // if we're going slow, expand the displayport to 9x viewport size
michael@0 692 RectF margins = new RectF(width, height, width, height);
michael@0 693 return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
michael@0 694 }
michael@0 695
michael@0 696 // figure out how far we expect to be
michael@0 697 float minDx = velocity.x * mMinFramesToDraw;
michael@0 698 float minDy = velocity.y * mMinFramesToDraw;
michael@0 699 float maxDx = velocity.x * mMaxFramesToDraw;
michael@0 700 float maxDy = velocity.y * mMaxFramesToDraw;
michael@0 701
michael@0 702 // figure out how many pixels we will be drawing when we draw the above-calculated range.
michael@0 703 // this will be larger than the viewport area.
michael@0 704 float pixelsToDraw = (width + Math.abs(maxDx - minDx)) * (height + Math.abs(maxDy - minDy));
michael@0 705 // adjust how far we will get because of the time spent drawing all these extra pixels. this
michael@0 706 // will again increase the number of pixels drawn so really we could keep iterating this over
michael@0 707 // and over, but once seems enough for now.
michael@0 708 maxDx = maxDx * pixelsToDraw / mPixelArea;
michael@0 709 maxDy = maxDy * pixelsToDraw / mPixelArea;
michael@0 710
michael@0 711 // and finally generate the displayport. the min/max stuff takes care of
michael@0 712 // negative velocities as well as positive.
michael@0 713 RectF margins = new RectF(
michael@0 714 -Math.min(minDx, maxDx),
michael@0 715 -Math.min(minDy, maxDy),
michael@0 716 Math.max(minDx, maxDx),
michael@0 717 Math.max(minDy, maxDy));
michael@0 718 return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
michael@0 719 }
michael@0 720
michael@0 721 @Override
michael@0 722 public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
michael@0 723 // the code below is the same as in calculate() but is awkward to refactor since it has multiple outputs.
michael@0 724 // refer to the comments in calculate() to understand what this is doing.
michael@0 725 float minDx = velocity.x * mMinFramesToDraw;
michael@0 726 float minDy = velocity.y * mMinFramesToDraw;
michael@0 727 float maxDx = velocity.x * mMaxFramesToDraw;
michael@0 728 float maxDy = velocity.y * mMaxFramesToDraw;
michael@0 729 float pixelsToDraw = (metrics.getWidth() + Math.abs(maxDx - minDx)) * (metrics.getHeight() + Math.abs(maxDy - minDy));
michael@0 730 maxDx = maxDx * pixelsToDraw / mPixelArea;
michael@0 731 maxDy = maxDy * pixelsToDraw / mPixelArea;
michael@0 732
michael@0 733 // now that we have an idea of how far we will be when the draw completes, take the farthest
michael@0 734 // end of that range and see if it falls outside the displayport bounds. if it does, allow
michael@0 735 // the draw to go through
michael@0 736 RectF predictedViewport = metrics.getViewport();
michael@0 737 predictedViewport.left += maxDx;
michael@0 738 predictedViewport.top += maxDy;
michael@0 739 predictedViewport.right += maxDx;
michael@0 740 predictedViewport.bottom += maxDy;
michael@0 741
michael@0 742 predictedViewport = clampToPageBounds(predictedViewport, metrics);
michael@0 743 return !displayPort.contains(predictedViewport);
michael@0 744 }
michael@0 745
michael@0 746 @Override
michael@0 747 public boolean drawTimeUpdate(long millis, int pixels) {
michael@0 748 // calculate the number of frames it took to draw a viewport-sized area
michael@0 749 float normalizedTime = (float)mPixelArea * (float)millis / (float)pixels;
michael@0 750 int normalizedFrames = (int)FloatMath.ceil(normalizedTime * 60f / 1000f);
michael@0 751 // broaden our range on how long it takes to draw if the draw falls outside
michael@0 752 // the range. this allows it to grow gradually. this heuristic may need to
michael@0 753 // be tweaked into more of a floating window average or something.
michael@0 754 if (normalizedFrames <= mMinFramesToDraw) {
michael@0 755 mMinFramesToDraw--;
michael@0 756 } else if (normalizedFrames > mMaxFramesToDraw) {
michael@0 757 mMaxFramesToDraw++;
michael@0 758 } else {
michael@0 759 return true;
michael@0 760 }
michael@0 761 Log.d(LOGTAG, "Widened draw range to [" + mMinFramesToDraw + ", " + mMaxFramesToDraw + "]");
michael@0 762 return true;
michael@0 763 }
michael@0 764
michael@0 765 @Override
michael@0 766 public void resetPageState() {
michael@0 767 mMinFramesToDraw = 0;
michael@0 768 mMaxFramesToDraw = 2;
michael@0 769 }
michael@0 770
michael@0 771 @Override
michael@0 772 public String toString() {
michael@0 773 return "PredictionBiasStrategy threshold=" + VELOCITY_THRESHOLD;
michael@0 774 }
michael@0 775 }
michael@0 776 }

mercurial