author | Augustin Trancart <augustin.trancart@gmail.com> |
Wed, 04 Sep 2013 14:07:11 -0400 | |
changeset 145496 | d6515c4d6cf852624deff2be262cfe9e948f5ebf |
parent 145495 | 6366f575b4c623a06c110cc81334047a9623970a |
child 145497 | 05c2d7b68a229d4221c25e1052689e6473ecd51f |
push id | 25214 |
push user | kwierso@gmail.com |
push date | Thu, 05 Sep 2013 00:02:20 +0000 |
treeherder | mozilla-central@99bd249e5a20 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | kats |
bugs | 711959 |
milestone | 26.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
|
--- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -199,16 +199,17 @@ FENNEC_JAVA_FILES = \ gfx/NinePatchTileLayer.java \ gfx/PanningPerfAPI.java \ gfx/PanZoomController.java \ gfx/PanZoomTarget.java \ gfx/PluginLayer.java \ gfx/PointUtils.java \ gfx/ProgressiveUpdateData.java \ gfx/RectUtils.java \ + gfx/RenderTask.java \ gfx/ScrollbarLayer.java \ gfx/SimpleScaleGestureDetector.java \ gfx/SingleTileLayer.java \ gfx/SubdocumentScrollHelper.java \ gfx/TextLayer.java \ gfx/TextureGenerator.java \ gfx/TextureReaper.java \ gfx/TileLayer.java \
--- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -5,16 +5,17 @@ package org.mozilla.gecko.gfx; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.R; import org.mozilla.gecko.Tab; import org.mozilla.gecko.Tabs; import org.mozilla.gecko.gfx.Layer.RenderContext; +import org.mozilla.gecko.gfx.RenderTask; import org.mozilla.gecko.mozglue.DirectBufferAllocator; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; @@ -45,29 +46,35 @@ public class LayerRenderer implements Ta * 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 static final int FRAME_RATE_METER_WIDTH = 128; private static final int FRAME_RATE_METER_HEIGHT = 32; + private static final long NANOS_PER_MS = 1000000; + private static final int NANOS_PER_SECOND = 1000000000; + private final LayerView mView; private final NinePatchTileLayer mShadowLayer; private TextLayer mFrameRateLayer; private final ScrollbarLayer mHorizScrollLayer; private final ScrollbarLayer mVertScrollLayer; private final FadeRunnable mFadeRunnable; private ByteBuffer mCoordByteBuffer; private FloatBuffer mCoordBuffer; private RenderContext mLastPageContext; private int mMaxTextureSize; private int mBackgroundColor; private int mOverscrollColor; + private long mLastFrameTime; + private final CopyOnWriteArrayList<RenderTask> mTasks; + private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>(); // Dropped frames display private int[] mFrameTimings; private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames; // Render profiling output private int mFramesRendered; @@ -133,16 +140,20 @@ public class LayerRenderer implements Ta mOverscrollColor = view.getContext().getResources().getColor(R.color.background_normal); CairoImage shadowImage = new BufferedCairoImage(view.getShadowPattern()); mShadowLayer = new NinePatchTileLayer(shadowImage); Bitmap scrollbarImage = view.getScrollbarImage(); IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight()); scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size); + + mTasks = new CopyOnWriteArrayList<RenderTask>(); + mLastFrameTime = System.nanoTime(); + mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true); mHorizScrollLayer = new ScrollbarLayer(this, diagonalFlip(scrollbarImage), new IntSize(size.height, size.width), false); mFadeRunnable = new FadeRunnable(); mFrameTimings = new int[60]; mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0; // Initialize the FloatBuffer that will be used to store all vertices and texture @@ -236,16 +247,40 @@ public class LayerRenderer implements Ta GLES20.glDisableVertexAttribArray(mPositionHandle); GLES20.glUseProgram(0); } public int getMaxTextureSize() { return mMaxTextureSize; } + public void postRenderTask(RenderTask aTask) { + mTasks.add(aTask); + mView.requestRender(); + } + + public void removeRenderTask(RenderTask aTask) { + mTasks.remove(aTask); + } + + private void runRenderTasks(CopyOnWriteArrayList<RenderTask> tasks, boolean after, long frameStartTime) { + for (RenderTask task : tasks) { + if (task.runAfter != after) { + continue; + } + + boolean stillRunning = task.run(frameStartTime - mLastFrameTime, frameStartTime); + + // Remove the task from the list if its finished + if (!stillRunning) { + tasks.remove(task); + } + } + } + public void addLayer(Layer layer) { synchronized (mExtraLayers) { if (mExtraLayers.contains(layer)) { mExtraLayers.remove(layer); } mExtraLayers.add(layer); } @@ -294,17 +329,17 @@ public class LayerRenderer implements Ta } private RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor, PointF offset) { return new RenderContext(viewport, pageRect, zoomFactor, offset, mPositionHandle, mTextureHandle, mCoordBuffer); } private void updateDroppedFrames(long frameStartTime) { - int frameElapsedTime = (int)(SystemClock.uptimeMillis() - frameStartTime); + int frameElapsedTime = (int)((System.nanoTime() - frameStartTime) / NANOS_PER_MS); /* Update the running statistics. */ mFrameTimingsSum -= mFrameTimings[mCurrentFrame]; mFrameTimingsSum += frameElapsedTime; mDroppedFrames -= (mFrameTimings[mCurrentFrame] + 1) / MAX_FRAME_TIME; mDroppedFrames += (frameElapsedTime + 1) / MAX_FRAME_TIME; mFrameTimings[mCurrentFrame] = frameElapsedTime; @@ -451,25 +486,28 @@ public class LayerRenderer implements Ta (screenSize.height - bottom) + (bottom - top)); scissorRect.offset(Math.round(-mRenderOffset.x), Math.round(-mRenderOffset.y)); return scissorRect; } /** This function is invoked via JNI; be careful when modifying signature. */ public void beginDrawing() { - mFrameStartTime = SystemClock.uptimeMillis(); + mFrameStartTime = System.nanoTime(); TextureReaper.get().reap(); TextureGenerator.get().fill(); mUpdated = true; Layer rootLayer = mView.getLayerClient().getRoot(); + // Run through pre-render tasks + runRenderTasks(mTasks, false, mFrameStartTime); + if (!mPageContext.fuzzyEquals(mLastPageContext) && !mView.isFullScreen()) { // The viewport or page changed, so show the scrollbars again // as per UX decision. Don't do this if we're in full-screen mode though. mVertScrollLayer.unfade(); mHorizScrollLayer.unfade(); mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY); } else if (mFadeRunnable.timeToFade()) { boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade(); @@ -599,22 +637,24 @@ public class LayerRenderer implements Ta PanningPerfAPI.recordCheckerboard(checkerboard); if (checkerboard < 0.0f || checkerboard > 1.0f) { Log.e(LOGTAG, "Checkerboard value out of bounds: " + checkerboard); } mCompleteFramesRendered += 1.0f - checkerboard; mFramesRendered ++; - if (mFrameStartTime - mProfileOutputTime > 1000) { + if (mFrameStartTime - mProfileOutputTime > NANOS_PER_SECOND) { mProfileOutputTime = mFrameStartTime; printCheckerboardStats(); } } + runRenderTasks(mTasks, true, mFrameStartTime); + /* Draw the FPS. */ if (mFrameRateLayer != null) { updateDroppedFrames(mFrameStartTime); GLES20.glEnable(GLES20.GL_BLEND); GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); mFrameRateLayer.draw(mScreenContext); } @@ -647,16 +687,17 @@ public class LayerRenderer implements Ta mView.post(new Runnable() { @Override public void run() { mView.getChildAt(0).setBackgroundColor(Color.TRANSPARENT); } }); mView.setPaintState(LayerView.PAINT_AFTER_FIRST); } + mLastFrameTime = mFrameStartTime; } } @Override public void onTabChanged(final Tab tab, Tabs.TabEvents msg, Object data) { // Sets the background of the newly selected tab. This background color // gets cleared in endDrawing(). This function runs on the UI thread, // but other code that touches the paint state is run on the compositor
--- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -396,16 +396,24 @@ public class LayerView extends FrameLayo public void addLayer(Layer layer) { mRenderer.addLayer(layer); } public void removeLayer(Layer layer) { mRenderer.removeLayer(layer); } + public void postRenderTask(RenderTask task) { + mRenderer.postRenderTask(task); + } + + public void removeRenderTask(RenderTask task) { + mRenderer.removeRenderTask(task); + } + public int getMaxTextureSize() { return mRenderer.getMaxTextureSize(); } /** Used by robocop for testing purposes. Not for production use! */ public IntBuffer getPixels() { return mRenderer.getPixels(); }
new file mode 100644 --- /dev/null +++ b/mobile/android/base/gfx/RenderTask.java @@ -0,0 +1,80 @@ +/* -*- 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; + +/** + * A class used to schedule a callback to occur when the next frame is drawn. + * Subclasses must redefine the internalRun method, not the run method. + */ +public abstract class RenderTask { + /** + * Whether to run the task after the render, or before. + */ + public final boolean runAfter; + + /** + * Time when this task has first run, in ns. Useful for tasks which run for a specific duration. + */ + private long mStartTime; + + /** + * Whether we should initialise mStartTime on the next frame run. + */ + private boolean mResetStartTime = true; + + /** + * The callback to run on each frame. timeDelta is the time elapsed since + * the last call, in nanoseconds. Returns true if it should continue + * running, or false if it should be removed from the task queue. Returning + * true implicitly schedules a redraw. + * + * This method first initializes the start time if resetStartTime has been invoked, + * then calls internalRun. + * + * Note : subclasses should override internalRun. + * + * @param timeDelta the time between the beginning of last frame and the beginning of this frame, in ns. + * @param currentFrameStartTime the startTime of the current frame, in ns. + * @return true if animation should be run at the next frame, false otherwise + * @see RenderTask#internalRun(long, long) + */ + public final boolean run(long timeDelta, long currentFrameStartTime) { + if (mResetStartTime) { + mStartTime = currentFrameStartTime; + mResetStartTime = false; + } + return internalRun(timeDelta, currentFrameStartTime); + } + + /** + * Abstract method to be overridden by subclasses. + * @param timeDelta the time between the beginning of last frame and the beginning of this frame, in ns + * @param currentFrameStartTime the startTime of the current frame, in ns. + * @return true if animation should be run at the next frame, false otherwise + */ + protected abstract boolean internalRun(long timeDelta, long currentFrameStartTime); + + public RenderTask(boolean aRunAfter) { + runAfter = aRunAfter; + } + + /** + * Get the start time of this task. + * It is the start time of the first frame this task was run on. + * @return the start time in ns + */ + public long getStartTime() { + return mStartTime; + } + + /** + * Schedule a reset of the recorded start time next time {@link RenderTask#run(long, long)} is run. + * @see RenderTask#getStartTime() + */ + public void resetStartTime() { + mResetStartTime = true; + } +}