Bug 732576. make getViewTransform fast. r=kats

This removes the logging, locking and allocations from getViewTransform.

This reduces the time spent from an median of 6.3ms to 0.061ms

We use a new scheme where the view transform is immutable and the member
variable containing it is atomically overwritten. So we may get a slightly old
view transform but this won't be a problem.
This commit is contained in:
Jeff Muizelaar 2012-03-02 14:31:27 -05:00
Родитель 6c418d073d
Коммит 589cb11de8
7 изменённых файлов: 147 добавлений и 42 удалений

Просмотреть файл

@ -51,6 +51,7 @@ import org.mozilla.gecko.gfx.PlaceholderLayerClient;
import org.mozilla.gecko.gfx.RectUtils; import org.mozilla.gecko.gfx.RectUtils;
import org.mozilla.gecko.gfx.SurfaceTextureLayer; import org.mozilla.gecko.gfx.SurfaceTextureLayer;
import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.gfx.ViewportMetrics;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.Tab.HistoryEntry; import org.mozilla.gecko.Tab.HistoryEntry;
import java.io.*; import java.io.*;
@ -1375,12 +1376,12 @@ abstract public class GeckoApp
if (tab == null) if (tab == null)
return; return;
ViewportMetrics targetViewport = mLayerController.getViewportMetrics(); ImmutableViewportMetrics targetViewport = mLayerController.getViewportMetrics();
ViewportMetrics pluginViewport; ImmutableViewportMetrics pluginViewport;
try { try {
JSONObject viewportObject = new JSONObject(metadata); JSONObject viewportObject = new JSONObject(metadata);
pluginViewport = new ViewportMetrics(viewportObject); pluginViewport = new ImmutableViewportMetrics(new ViewportMetrics(viewportObject));
} catch (JSONException e) { } catch (JSONException e) {
Log.e(LOGTAG, "Bad viewport metadata: ", e); Log.e(LOGTAG, "Bad viewport metadata: ", e);
return; return;
@ -1570,7 +1571,7 @@ abstract public class GeckoApp
} }
public void repositionPluginViews(Tab tab, boolean setVisible) { public void repositionPluginViews(Tab tab, boolean setVisible) {
ViewportMetrics targetViewport = mLayerController.getViewportMetrics(); ImmutableViewportMetrics targetViewport = mLayerController.getViewportMetrics();
if (targetViewport == null) if (targetViewport == null)
return; return;
@ -2729,20 +2730,20 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams
private int mOriginalY; private int mOriginalY;
private int mOriginalWidth; private int mOriginalWidth;
private int mOriginalHeight; private int mOriginalHeight;
private ViewportMetrics mOriginalViewport; private ImmutableViewportMetrics mOriginalViewport;
private float mLastResolution; private float mLastResolution;
public PluginLayoutParams(int aX, int aY, int aWidth, int aHeight, ViewportMetrics aViewport) { public PluginLayoutParams(int aX, int aY, int aWidth, int aHeight, ImmutableViewportMetrics aViewport) {
super(aWidth, aHeight, aX, aY); super(aWidth, aHeight, aX, aY);
Log.i(LOGTAG, "Creating plugin at " + aX + ", " + aY + ", " + aWidth + "x" + aHeight + ", (" + (aViewport.getZoomFactor() * 100) + "%)"); Log.i(LOGTAG, "Creating plugin at " + aX + ", " + aY + ", " + aWidth + "x" + aHeight + ", (" + (aViewport.zoomFactor * 100) + "%)");
mOriginalX = aX; mOriginalX = aX;
mOriginalY = aY; mOriginalY = aY;
mOriginalWidth = aWidth; mOriginalWidth = aWidth;
mOriginalHeight = aHeight; mOriginalHeight = aHeight;
mOriginalViewport = aViewport; mOriginalViewport = aViewport;
mLastResolution = aViewport.getZoomFactor(); mLastResolution = aViewport.zoomFactor;
clampToMaxSize(); clampToMaxSize();
} }
@ -2759,7 +2760,7 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams
} }
} }
public void reset(int aX, int aY, int aWidth, int aHeight, ViewportMetrics aViewport) { public void reset(int aX, int aY, int aWidth, int aHeight, ImmutableViewportMetrics aViewport) {
PointF origin = aViewport.getOrigin(); PointF origin = aViewport.getOrigin();
x = mOriginalX = aX + (int)origin.x; x = mOriginalX = aX + (int)origin.x;
@ -2767,7 +2768,7 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams
width = mOriginalWidth = aWidth; width = mOriginalWidth = aWidth;
height = mOriginalHeight = aHeight; height = mOriginalHeight = aHeight;
mOriginalViewport = aViewport; mOriginalViewport = aViewport;
mLastResolution = aViewport.getZoomFactor(); mLastResolution = aViewport.zoomFactor;
clampToMaxSize(); clampToMaxSize();
} }
@ -2785,14 +2786,14 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams
} }
} }
public void reposition(ViewportMetrics viewport) { public void reposition(ImmutableViewportMetrics viewport) {
PointF targetOrigin = viewport.getOrigin(); PointF targetOrigin = viewport.getOrigin();
PointF originalOrigin = mOriginalViewport.getOrigin(); PointF originalOrigin = mOriginalViewport.getOrigin();
Point offset = new Point(Math.round(originalOrigin.x - targetOrigin.x), Point offset = new Point(Math.round(originalOrigin.x - targetOrigin.x),
Math.round(originalOrigin.y - targetOrigin.y)); Math.round(originalOrigin.y - targetOrigin.y));
reposition(offset, viewport.getZoomFactor()); reposition(offset, viewport.zoomFactor);
} }
public float getLastResolution() { public float getLastResolution() {

Просмотреть файл

@ -116,6 +116,7 @@ FENNEC_JAVA_FILES = \
gfx/GeckoLayerClient.java \ gfx/GeckoLayerClient.java \
gfx/GLController.java \ gfx/GLController.java \
gfx/GLThread.java \ gfx/GLThread.java \
gfx/ImmutableViewportMetrics.java \
gfx/InputConnectionHandler.java \ gfx/InputConnectionHandler.java \
gfx/IntSize.java \ gfx/IntSize.java \
gfx/Layer.java \ gfx/Layer.java \

Просмотреть файл

@ -95,6 +95,9 @@ public class GeckoLayerClient implements GeckoEventResponder,
/* Used by robocop for testing purposes */ /* Used by robocop for testing purposes */
private DrawListener mDrawListener; private DrawListener mDrawListener;
/* Used as a temporary ViewTransform by getViewTransform */
private ViewTransform mCurrentViewTransform;
public GeckoLayerClient(Context context) { public GeckoLayerClient(Context context) {
mScreenSize = new IntSize(0, 0); mScreenSize = new IntSize(0, 0);
mBufferSize = new IntSize(0, 0); mBufferSize = new IntSize(0, 0);
@ -102,6 +105,9 @@ public class GeckoLayerClient implements GeckoEventResponder,
DEFAULT_DISPLAY_PORT_MARGIN, DEFAULT_DISPLAY_PORT_MARGIN,
DEFAULT_DISPLAY_PORT_MARGIN, DEFAULT_DISPLAY_PORT_MARGIN,
DEFAULT_DISPLAY_PORT_MARGIN); DEFAULT_DISPLAY_PORT_MARGIN);
// we can fill this in with dummy values because it is always written
// to before being read
mCurrentViewTransform = new ViewTransform(0, 0, 1);
} }
/** Attaches the root layer to the layer controller so that Gecko appears. */ /** Attaches the root layer to the layer controller so that Gecko appears. */
@ -317,18 +323,19 @@ public class GeckoLayerClient implements GeckoEventResponder,
} }
/** This function is invoked by Gecko via JNI; be careful when modifying signature. */ /** This function is invoked by Gecko via JNI; be careful when modifying signature. */
/* This functions needs to be fast because it is called by the compositor every frame.
* It avoids taking any locks or allocating any objects. We keep around a
* mCurrentViewTransform so we don't need to allocate a new ViewTransform
* everytime we're called. NOTE: we could probably switch to returning a ImmutableViewportMetrics
* which would avoid the copy into mCurrentViewTransform. */
public ViewTransform getViewTransform() { public ViewTransform getViewTransform() {
// NB: We don't begin a transaction here because this can be called in a synchronous // getViewportMetrics is thread safe so we don't need to synchronize
// manner between beginDrawing() and endDrawing(), and that will cause a deadlock. // on myLayerController.
ImmutableViewportMetrics viewportMetrics = mLayerController.getViewportMetrics();
synchronized (mLayerController) { mCurrentViewTransform.x = viewportMetrics.viewportRectLeft;
ViewportMetrics viewportMetrics = mLayerController.getViewportMetrics(); mCurrentViewTransform.y = viewportMetrics.viewportRectTop;
PointF viewportOrigin = viewportMetrics.getOrigin(); mCurrentViewTransform.scale = viewportMetrics.zoomFactor;
float scrollX = viewportOrigin.x; return mCurrentViewTransform;
float scrollY = viewportOrigin.y;
float zoomFactor = viewportMetrics.getZoomFactor();
return new ViewTransform(scrollX, scrollY, zoomFactor);
}
} }
/** This function is invoked by Gecko via JNI; be careful when modifying signature. */ /** This function is invoked by Gecko via JNI; be careful when modifying signature. */

Просмотреть файл

@ -0,0 +1,64 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import android.graphics.PointF;
import android.graphics.RectF;
/**
* ImmutableViewportMetrics are used to store the viewport metrics
* in way that we can access a version of them from multiple threads
* without having to take a lock
*/
public class ImmutableViewportMetrics {
// We need to flatten the RectF and FloatSize structures
// because Java doesn't have the concept of const classes
public final float pageSizeWidth;
public final float pageSizeHeight;
public final float viewportRectBottom;
public final float viewportRectLeft;
public final float viewportRectRight;
public final float viewportRectTop;
public final float zoomFactor;
public ImmutableViewportMetrics(ViewportMetrics m) {
RectF viewportRect = m.getViewport();
viewportRectBottom = viewportRect.bottom;
viewportRectLeft = viewportRect.left;
viewportRectRight = viewportRect.right;
viewportRectTop = viewportRect.top;
FloatSize pageSize = m.getPageSize();
pageSizeWidth = pageSize.width;
pageSizeHeight = pageSize.height;
zoomFactor = m.getZoomFactor();
}
// some helpers to make ImmutableViewportMetrics act more like ViewportMetrics
public PointF getOrigin() {
return new PointF(viewportRectLeft, viewportRectTop);
}
public FloatSize getSize() {
return new FloatSize(viewportRectRight - viewportRectLeft, viewportRectBottom - viewportRectTop);
}
public RectF getViewport() {
return new RectF(viewportRectLeft,
viewportRectTop,
viewportRectRight,
viewportRectBottom);
}
public FloatSize getPageSize() {
return new FloatSize(pageSizeWidth, pageSizeHeight);
}
}

Просмотреть файл

@ -81,7 +81,20 @@ public class LayerController implements Tabs.OnTabsChangedListener {
private Layer mRootLayer; /* The root layer. */ private Layer mRootLayer; /* The root layer. */
private LayerView mView; /* The main rendering view. */ private LayerView mView; /* The main rendering view. */
private Context mContext; /* The current context. */ private Context mContext; /* The current context. */
private ViewportMetrics mViewportMetrics; /* The current viewport metrics. */
/* This is volatile so that we can read and write to it from different threads.
* We avoid synchronization to make getting the viewport metrics from
* the compositor as cheap as possible. The viewport is immutable so
* we don't need to worry about anyone mutating it while we're reading from it.
* Specifically:
* 1) reading mViewportMetrics from any thread is fine without synchronization
* 2) writing to mViewportMetrics requires synchronizing on the layer controller object
* 3) whenver reading multiple fields from mViewportMetrics without synchronization (i.e. in
* case 1 above) you should always frist grab a local copy of the reference, and then use
* that because mViewportMetrics might get reassigned in between reading the different
* fields. */
private volatile ImmutableViewportMetrics mViewportMetrics; /* The current viewport metrics. */
private boolean mWaitForTouchListeners; private boolean mWaitForTouchListeners;
private PanZoomController mPanZoomController; private PanZoomController mPanZoomController;
@ -124,7 +137,7 @@ public class LayerController implements Tabs.OnTabsChangedListener {
mContext = context; mContext = context;
mForceRedraw = true; mForceRedraw = true;
mViewportMetrics = new ViewportMetrics(); mViewportMetrics = new ImmutableViewportMetrics(new ViewportMetrics());
mPanZoomController = new PanZoomController(this); mPanZoomController = new PanZoomController(this);
mView = new LayerView(context, this); mView = new LayerView(context, this);
@ -152,7 +165,7 @@ public class LayerController implements Tabs.OnTabsChangedListener {
public Layer getRoot() { return mRootLayer; } public Layer getRoot() { return mRootLayer; }
public LayerView getView() { return mView; } public LayerView getView() { return mView; }
public Context getContext() { return mContext; } public Context getContext() { return mContext; }
public ViewportMetrics getViewportMetrics() { return mViewportMetrics; } public ImmutableViewportMetrics getViewportMetrics() { return mViewportMetrics; }
public RectF getViewport() { public RectF getViewport() {
return mViewportMetrics.getViewport(); return mViewportMetrics.getViewport();
@ -171,7 +184,7 @@ public class LayerController implements Tabs.OnTabsChangedListener {
} }
public float getZoomFactor() { public float getZoomFactor() {
return mViewportMetrics.getZoomFactor(); return mViewportMetrics.zoomFactor;
} }
public Bitmap getBackgroundPattern() { return getDrawable("background"); } public Bitmap getBackgroundPattern() { return getDrawable("background"); }
@ -203,10 +216,11 @@ public class LayerController implements Tabs.OnTabsChangedListener {
public void setViewportSize(FloatSize size) { public void setViewportSize(FloatSize size) {
// Resize the viewport, and modify its zoom factor so that the page retains proportionally // Resize the viewport, and modify its zoom factor so that the page retains proportionally
// zoomed relative to the screen. // zoomed relative to the screen.
float oldHeight = mViewportMetrics.getSize().height; ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
float oldWidth = mViewportMetrics.getSize().width; float oldHeight = viewportMetrics.getSize().height;
float oldZoomFactor = mViewportMetrics.getZoomFactor(); float oldWidth = viewportMetrics.getSize().width;
mViewportMetrics.setSize(size); float oldZoomFactor = viewportMetrics.getZoomFactor();
viewportMetrics.setSize(size);
// if the viewport got larger (presumably because the vkb went away), and the page // if the viewport got larger (presumably because the vkb went away), and the page
// is smaller than the new viewport size, increase the page size so that the panzoomcontroller // is smaller than the new viewport size, increase the page size so that the panzoomcontroller
@ -214,9 +228,9 @@ public class LayerController implements Tabs.OnTabsChangedListener {
// gecko increasing the page size to match the new viewport size, which will happen the next // gecko increasing the page size to match the new viewport size, which will happen the next
// time we get a draw update. // time we get a draw update.
if (size.width >= oldWidth && size.height >= oldHeight) { if (size.width >= oldWidth && size.height >= oldHeight) {
FloatSize pageSize = mViewportMetrics.getPageSize(); FloatSize pageSize = viewportMetrics.getPageSize();
if (pageSize.width < size.width || pageSize.height < size.height) { if (pageSize.width < size.width || pageSize.height < size.height) {
mViewportMetrics.setPageSize(new FloatSize(Math.max(pageSize.width, size.width), viewportMetrics.setPageSize(new FloatSize(Math.max(pageSize.width, size.width),
Math.max(pageSize.height, size.height))); Math.max(pageSize.height, size.height)));
} }
} }
@ -231,7 +245,8 @@ public class LayerController implements Tabs.OnTabsChangedListener {
newFocus = new PointF(size.width / 2.0f, size.height / 2.0f); newFocus = new PointF(size.width / 2.0f, size.height / 2.0f);
} }
float newZoomFactor = size.width * oldZoomFactor / oldWidth; float newZoomFactor = size.width * oldZoomFactor / oldWidth;
mViewportMetrics.scaleTo(newZoomFactor, newFocus); viewportMetrics.scaleTo(newZoomFactor, newFocus);
mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
setForceRedraw(); setForceRedraw();
@ -246,9 +261,11 @@ public class LayerController implements Tabs.OnTabsChangedListener {
/** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */ /** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
public void scrollBy(PointF point) { public void scrollBy(PointF point) {
PointF origin = mViewportMetrics.getOrigin(); ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
PointF origin = viewportMetrics.getOrigin();
origin.offset(point.x, point.y); origin.offset(point.x, point.y);
mViewportMetrics.setOrigin(origin); viewportMetrics.setOrigin(origin);
mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
notifyLayerClientOfGeometryChange(); notifyLayerClientOfGeometryChange();
GeckoApp.mAppContext.repositionPluginViews(false); GeckoApp.mAppContext.repositionPluginViews(false);
@ -260,7 +277,9 @@ public class LayerController implements Tabs.OnTabsChangedListener {
if (mViewportMetrics.getPageSize().fuzzyEquals(size)) if (mViewportMetrics.getPageSize().fuzzyEquals(size))
return; return;
mViewportMetrics.setPageSize(size); ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
viewportMetrics.setPageSize(size);
mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
// Page size is owned by the layer client, so no need to notify it of // Page size is owned by the layer client, so no need to notify it of
// this change. // this change.
@ -280,7 +299,7 @@ public class LayerController implements Tabs.OnTabsChangedListener {
* while calling this. * while calling this.
*/ */
public void setViewportMetrics(ViewportMetrics viewport) { public void setViewportMetrics(ViewportMetrics viewport) {
mViewportMetrics = new ViewportMetrics(viewport); mViewportMetrics = new ImmutableViewportMetrics(viewport);
// this function may or may not be called on the UI thread, // this function may or may not be called on the UI thread,
// but repositionPluginViews must only be called on the UI thread. // but repositionPluginViews must only be called on the UI thread.
GeckoApp.mAppContext.runOnUiThread(new Runnable() { GeckoApp.mAppContext.runOnUiThread(new Runnable() {
@ -296,7 +315,9 @@ public class LayerController implements Tabs.OnTabsChangedListener {
* scale operation. You must hold the monitor while calling this. * scale operation. You must hold the monitor while calling this.
*/ */
public void scaleWithFocus(float zoomFactor, PointF focus) { public void scaleWithFocus(float zoomFactor, PointF focus) {
mViewportMetrics.scaleTo(zoomFactor, focus); ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
viewportMetrics.scaleTo(zoomFactor, focus);
mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
// We assume the zoom level will only be modified by the // We assume the zoom level will only be modified by the
// PanZoomController, so no need to notify it of this change. // PanZoomController, so no need to notify it of this change.
@ -375,10 +396,11 @@ public class LayerController implements Tabs.OnTabsChangedListener {
if (mRootLayer == null) if (mRootLayer == null)
return null; return null;
ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
// Undo the transforms. // Undo the transforms.
PointF origin = mViewportMetrics.getOrigin(); PointF origin = viewportMetrics.getOrigin();
PointF newPoint = new PointF(origin.x, origin.y); PointF newPoint = new PointF(origin.x, origin.y);
float zoom = mViewportMetrics.getZoomFactor(); float zoom = viewportMetrics.zoomFactor;
viewPoint.x /= zoom; viewPoint.x /= zoom;
viewPoint.y /= zoom; viewPoint.y /= zoom;
newPoint.offset(viewPoint.x, viewPoint.y); newPoint.offset(viewPoint.x, viewPoint.y);

Просмотреть файл

@ -79,6 +79,16 @@ public class ViewportMetrics {
mZoomFactor = viewport.getZoomFactor(); mZoomFactor = viewport.getZoomFactor();
} }
public ViewportMetrics(ImmutableViewportMetrics viewport) {
mPageSize = new FloatSize(viewport.pageSizeWidth, viewport.pageSizeHeight);
mViewportRect = new RectF(viewport.viewportRectLeft,
viewport.viewportRectTop,
viewport.viewportRectRight,
viewport.viewportRectBottom);
mZoomFactor = viewport.zoomFactor;
}
public ViewportMetrics(JSONObject json) throws JSONException { public ViewportMetrics(JSONObject json) throws JSONException {
float x = (float)json.getDouble("x"); float x = (float)json.getDouble("x");
float y = (float)json.getDouble("y"); float y = (float)json.getDouble("y");

Просмотреть файл

@ -228,7 +228,7 @@ public class PanZoomController
public void pageSizeUpdated() { public void pageSizeUpdated() {
if (mState == PanZoomState.NOTHING) { if (mState == PanZoomState.NOTHING) {
ViewportMetrics validated = getValidViewportMetrics(); ViewportMetrics validated = getValidViewportMetrics();
if (! mController.getViewportMetrics().fuzzyEquals(validated)) { if (! (new ViewportMetrics(mController.getViewportMetrics())).fuzzyEquals(validated)) {
// page size changed such that we are now in overscroll. snap to the // page size changed such that we are now in overscroll. snap to the
// the nearest valid viewport // the nearest valid viewport
mController.setViewportMetrics(validated); mController.setViewportMetrics(validated);