Bug 708519 - Repair the frame rate meter r=kats
authorPatrick Walton <pwalton@mozilla.com>
Fri, 09 Dec 2011 10:01:26 -0500
changeset 84024 aa93820dbda1aaea60e4cf3d495e71f9aeab4e83
parent 84023 7983b5d3e8ee88c38ae7cf8c54bd5f61a811dded
child 84025 45bb8d3c8fe7934559f3a442aa413253cb8c78af
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs708519
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 708519 - Repair the frame rate meter r=kats
mobile/android/base/gfx/LayerRenderer.java
--- a/mobile/android/base/gfx/LayerRenderer.java
+++ b/mobile/android/base/gfx/LayerRenderer.java
@@ -49,60 +49,66 @@ import org.mozilla.gecko.gfx.TextureReap
 import org.mozilla.gecko.gfx.TextLayer;
 import org.mozilla.gecko.gfx.TileLayer;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.opengl.GLSurfaceView;
+import android.os.SystemClock;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.WindowManager;
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
 import java.nio.ByteBuffer;
 
 /**
  * The layer renderer implements the rendering logic for a layer view.
  */
 public class LayerRenderer implements GLSurfaceView.Renderer {
     private static final float BACKGROUND_COLOR_R = 0.81f;
     private static final float BACKGROUND_COLOR_G = 0.81f;
     private static final float BACKGROUND_COLOR_B = 0.81f;
 
+    /*
+     * The amount of time a frame is allowed to take to render before we declare it a dropped
+     * frame.
+     */
+    private static final int MAX_FRAME_TIME = 16;   /* 1000 ms / 60 FPS */
+
     private final LayerView mView;
     private final SingleTileLayer mCheckerboardLayer;
     private final NinePatchTileLayer mShadowLayer;
-    private final TextLayer mFPSLayer;
+    private final TextLayer mFrameRateLayer;
     private final ScrollbarLayer mHorizScrollLayer;
     private final ScrollbarLayer mVertScrollLayer;
 
-    // FPS display
-    private long mFrameCountTimestamp;
-    private long mFrameTime;
-    private int mFrameCount;            // number of frames since last timestamp
+    // Dropped frames display
+    private int[] mFrameTimings;
+    private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames;
 
     public LayerRenderer(LayerView view) {
         mView = view;
 
         LayerController controller = view.getController();
 
         CairoImage checkerboardImage = new BufferedCairoImage(controller.getCheckerboardPattern());
         mCheckerboardLayer = new SingleTileLayer(true, checkerboardImage);
 
         CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern());
         mShadowLayer = new NinePatchTileLayer(shadowImage);
 
-        mFPSLayer = TextLayer.create(new IntSize(64, 32), "-- FPS");
+        mFrameRateLayer = TextLayer.create(new IntSize(64, 32), "-- ms/--");
         mHorizScrollLayer = ScrollbarLayer.create(false);
         mVertScrollLayer = ScrollbarLayer.create(true);
 
-        mFrameCountTimestamp = System.currentTimeMillis();
-        mFrameCount = 0;
+        mFrameTimings = new int[60];
+        mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0;
     }
 
     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
         gl.glClearDepthf(1.0f);             /* FIXME: Is this needed? */
         gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
         gl.glShadeModel(GL10.GL_SMOOTH);    /* FIXME: Is this needed? */
         gl.glDisable(GL10.GL_DITHER);
@@ -111,28 +117,29 @@ public class LayerRenderer implements GL
 
     /**
      * Called whenever a new frame is about to be drawn.
      *
      * FIXME: This is racy. Layers and page sizes can be modified by the pan/zoom controller while
      * this is going on.
      */
     public void onDrawFrame(GL10 gl) {
-        checkFPS();
+        long frameStartTime = SystemClock.uptimeMillis();
+
         TextureReaper.get().reap(gl);
 
         LayerController controller = mView.getController();
         Layer rootLayer = controller.getRoot();
         RenderContext screenContext = createScreenContext(), pageContext = createPageContext();
 
         /* Update layers. */
         if (rootLayer != null) rootLayer.update(gl);
         mShadowLayer.update(gl);
         mCheckerboardLayer.update(gl);
-        mFPSLayer.update(gl);
+        mFrameRateLayer.update(gl);
         mVertScrollLayer.update(gl);
         mHorizScrollLayer.update(gl);
 
         /* Draw the background. */
         gl.glClearColor(BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, 1.0f);
         gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
 
         /* Draw the drop shadow, if we need to. */
@@ -160,19 +167,20 @@ public class LayerRenderer implements GL
         if (pageRect.height() > screenSize.height)
             mVertScrollLayer.draw(pageContext);
 
         /* Draw the horizontal scrollbar. */
         if (pageRect.width() > screenSize.width)
             mHorizScrollLayer.draw(pageContext);
 
         /* Draw the FPS. */
+        updateDroppedFrames(frameStartTime);
         try {
             gl.glEnable(GL10.GL_BLEND);
-            mFPSLayer.draw(screenContext);
+            mFrameRateLayer.draw(screenContext);
         } finally {
             gl.glDisable(GL10.GL_BLEND);
         }
 
         PanningPerfAPI.recordFrameTime();
     }
 
     private RenderContext createScreenContext() {
@@ -228,33 +236,31 @@ public class LayerRenderer implements GL
             public void run() {
                 mView.setViewportSize(new IntSize(width, height));
             }
         });
 
         /* TODO: Throw away tile images? */
     }
 
-    private void checkFPS() {
-        mFrameTime += mView.getRenderTime();
-        mFrameCount ++;
+    private void updateDroppedFrames(long frameStartTime) {
+        int frameElapsedTime = (int)(SystemClock.uptimeMillis() - frameStartTime);
 
-        if (System.currentTimeMillis() >= mFrameCountTimestamp + 1000) {
-            mFrameCountTimestamp = System.currentTimeMillis();
+        /* Update the running statistics. */
+        mFrameTimingsSum -= mFrameTimings[mCurrentFrame];
+        mFrameTimingsSum += frameElapsedTime;
+        mDroppedFrames -= (mFrameTimings[mCurrentFrame] + 1) / MAX_FRAME_TIME;
+        mDroppedFrames += (frameElapsedTime + 1) / MAX_FRAME_TIME;
 
-            // Extrapolate FPS based on time taken by frames drawn.
-            // XXX This doesn't take into account the vblank, so the FPS
-            //     can show higher than it actually is.
-            mFrameCount = (int)(mFrameCount * 1000000000L / mFrameTime);
+        mFrameTimings[mCurrentFrame] = (int)frameElapsedTime;
+        mCurrentFrame = (mCurrentFrame + 1) % mFrameTimings.length;
+
+        int averageTime = mFrameTimingsSum / mFrameTimings.length;
 
-            mFPSLayer.beginTransaction();
-            try {
-                mFPSLayer.setText(mFrameCount + " FPS");
-            } finally {
-                mFPSLayer.endTransaction();
-            }
-
-            mFrameCount = 0;
-            mFrameTime = 0;
+        mFrameRateLayer.beginTransaction();
+        try {
+            mFrameRateLayer.setText(averageTime + " ms/" + mDroppedFrames);
+        } finally {
+            mFrameRateLayer.endTransaction();
         }
     }
 }