mobile/android/base/gfx/LayerRenderer.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 package org.mozilla.gecko.gfx;
     8 import org.mozilla.gecko.GeckoAppShell;
     9 import org.mozilla.gecko.R;
    10 import org.mozilla.gecko.Tab;
    11 import org.mozilla.gecko.Tabs;
    12 import org.mozilla.gecko.gfx.Layer.RenderContext;
    13 import org.mozilla.gecko.gfx.RenderTask;
    14 import org.mozilla.gecko.mozglue.DirectBufferAllocator;
    16 import android.content.Context;
    17 import android.content.SharedPreferences;
    18 import android.content.res.Resources;
    19 import android.graphics.Bitmap;
    20 import android.graphics.Canvas;
    21 import android.graphics.Color;
    22 import android.graphics.Matrix;
    23 import android.graphics.PointF;
    24 import android.graphics.Rect;
    25 import android.graphics.RectF;
    26 import android.opengl.GLES20;
    27 import android.os.SystemClock;
    28 import android.util.Log;
    29 import org.mozilla.gecko.mozglue.JNITarget;
    31 import java.nio.ByteBuffer;
    32 import java.nio.ByteOrder;
    33 import java.nio.FloatBuffer;
    34 import java.nio.IntBuffer;
    35 import java.util.concurrent.CopyOnWriteArrayList;
    37 import javax.microedition.khronos.egl.EGLConfig;
    39 /**
    40  * The layer renderer implements the rendering logic for a layer view.
    41  */
    42 public class LayerRenderer implements Tabs.OnTabsChangedListener {
    43     private static final String LOGTAG = "GeckoLayerRenderer";
    44     private static final String PROFTAG = "GeckoLayerRendererProf";
    46     /*
    47      * The amount of time a frame is allowed to take to render before we declare it a dropped
    48      * frame.
    49      */
    50     private static final int MAX_FRAME_TIME = 16;   /* 1000 ms / 60 FPS */
    52     private static final int FRAME_RATE_METER_WIDTH = 128;
    53     private static final int FRAME_RATE_METER_HEIGHT = 32;
    55     private static final long NANOS_PER_MS = 1000000;
    56     private static final int NANOS_PER_SECOND = 1000000000;
    58     private final LayerView mView;
    59     private TextLayer mFrameRateLayer;
    60     private final ScrollbarLayer mHorizScrollLayer;
    61     private final ScrollbarLayer mVertScrollLayer;
    62     private final FadeRunnable mFadeRunnable;
    63     private ByteBuffer mCoordByteBuffer;
    64     private FloatBuffer mCoordBuffer;
    65     private RenderContext mLastPageContext;
    66     private int mMaxTextureSize;
    67     private int mBackgroundColor;
    68     private int mOverscrollColor;
    70     private long mLastFrameTime;
    71     private final CopyOnWriteArrayList<RenderTask> mTasks;
    73     private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
    75     // Dropped frames display
    76     private int[] mFrameTimings;
    77     private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames;
    79     // Render profiling output
    80     private int mFramesRendered;
    81     private float mCompleteFramesRendered;
    82     private boolean mProfileRender;
    83     private long mProfileOutputTime;
    85     private IntBuffer mPixelBuffer;
    87     // Used by GLES 2.0
    88     private int mProgram;
    89     private int mPositionHandle;
    90     private int mTextureHandle;
    91     private int mSampleHandle;
    92     private int mTMatrixHandle;
    94     // column-major matrix applied to each vertex to shift the viewport from
    95     // one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by
    96     // a factor of 2 to fill up the screen
    97     public static final float[] DEFAULT_TEXTURE_MATRIX = {
    98         2.0f, 0.0f, 0.0f, 0.0f,
    99         0.0f, 2.0f, 0.0f, 0.0f,
   100         0.0f, 0.0f, 2.0f, 0.0f,
   101         -1.0f, -1.0f, 0.0f, 1.0f
   102     };
   104     private static final int COORD_BUFFER_SIZE = 20;
   106     // The shaders run on the GPU directly, the vertex shader is only applying the
   107     // matrix transform detailed above
   109     // Note we flip the y-coordinate in the vertex shader from a
   110     // coordinate system with (0,0) in the top left to one with (0,0) in
   111     // the bottom left.
   113     public static final String DEFAULT_VERTEX_SHADER =
   114         "uniform mat4 uTMatrix;\n" +
   115         "attribute vec4 vPosition;\n" +
   116         "attribute vec2 aTexCoord;\n" +
   117         "varying vec2 vTexCoord;\n" +
   118         "void main() {\n" +
   119         "    gl_Position = uTMatrix * vPosition;\n" +
   120         "    vTexCoord.x = aTexCoord.x;\n" +
   121         "    vTexCoord.y = 1.0 - aTexCoord.y;\n" +
   122         "}\n";
   124     // We use highp because the screenshot textures
   125     // we use are large and we stretch them alot
   126     // so we need all the precision we can get.
   127     // Unfortunately, highp is not required by ES 2.0
   128     // so on GPU's like Mali we end up getting mediump
   129     public static final String DEFAULT_FRAGMENT_SHADER =
   130         "precision highp float;\n" +
   131         "varying vec2 vTexCoord;\n" +
   132         "uniform sampler2D sTexture;\n" +
   133         "void main() {\n" +
   134         "    gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
   135         "}\n";
   137     public LayerRenderer(LayerView view) {
   138         mView = view;
   139         try {
   140             mOverscrollColor = view.getContext().getResources().getColor(R.color.background_normal);
   141         } catch (Resources.NotFoundException nfe) { mOverscrollColor = Color.BLACK; }
   143         Bitmap scrollbarImage = view.getScrollbarImage();
   144         IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight());
   145         scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size);
   147         mTasks = new CopyOnWriteArrayList<RenderTask>();
   148         mLastFrameTime = System.nanoTime();
   150         mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true);
   151         mHorizScrollLayer = new ScrollbarLayer(this, diagonalFlip(scrollbarImage), new IntSize(size.height, size.width), false);
   152         mFadeRunnable = new FadeRunnable();
   154         mFrameTimings = new int[60];
   155         mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0;
   157         // Initialize the FloatBuffer that will be used to store all vertices and texture
   158         // coordinates in draw() commands.
   159         mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4);
   160         mCoordByteBuffer.order(ByteOrder.nativeOrder());
   161         mCoordBuffer = mCoordByteBuffer.asFloatBuffer();
   163         Tabs.registerOnTabsChangedListener(this);
   164     }
   166     private Bitmap expandCanvasToPowerOfTwo(Bitmap image, IntSize size) {
   167         IntSize potSize = size.nextPowerOfTwo();
   168         if (size.equals(potSize)) {
   169             return image;
   170         }
   171         // make the bitmap size a power-of-two in both dimensions if it's not already.
   172         Bitmap potImage = Bitmap.createBitmap(potSize.width, potSize.height, image.getConfig());
   173         new Canvas(potImage).drawBitmap(image, new Matrix(), null);
   174         return potImage;
   175     }
   177     private Bitmap diagonalFlip(Bitmap image) {
   178         Matrix rotation = new Matrix();
   179         rotation.setValues(new float[] { 0, 1, 0, 1, 0, 0, 0, 0, 1 }); // transform (x,y) into (y,x)
   180         Bitmap rotated = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), rotation, true);
   181         return rotated;
   182     }
   184     public void destroy() {
   185         DirectBufferAllocator.free(mCoordByteBuffer);
   186         mCoordByteBuffer = null;
   187         mCoordBuffer = null;
   188         mHorizScrollLayer.destroy();
   189         mVertScrollLayer.destroy();
   190         if (mFrameRateLayer != null) {
   191             mFrameRateLayer.destroy();
   192         }
   193         Tabs.unregisterOnTabsChangedListener(this);
   194     }
   196     void onSurfaceCreated(EGLConfig config) {
   197         checkMonitoringEnabled();
   198         createDefaultProgram();
   199         activateDefaultProgram();
   200     }
   202     public void createDefaultProgram() {
   203         int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DEFAULT_VERTEX_SHADER);
   204         int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DEFAULT_FRAGMENT_SHADER);
   206         mProgram = GLES20.glCreateProgram();
   207         GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
   208         GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
   209         GLES20.glLinkProgram(mProgram);                  // creates OpenGL program executables
   211         // Get handles to the vertex shader's vPosition, aTexCoord, sTexture, and uTMatrix members.
   212         mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
   213         mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
   214         mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
   215         mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix");
   217         int maxTextureSizeResult[] = new int[1];
   218         GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0);
   219         mMaxTextureSize = maxTextureSizeResult[0];
   220     }
   222     // Activates the shader program.
   223     public void activateDefaultProgram() {
   224         // Add the program to the OpenGL environment
   225         GLES20.glUseProgram(mProgram);
   227         // Set the transformation matrix
   228         GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false, DEFAULT_TEXTURE_MATRIX, 0);
   230         // Enable the arrays from which we get the vertex and texture coordinates
   231         GLES20.glEnableVertexAttribArray(mPositionHandle);
   232         GLES20.glEnableVertexAttribArray(mTextureHandle);
   234         GLES20.glUniform1i(mSampleHandle, 0);
   236         // TODO: Move these calls into a separate deactivate() call that is called after the
   237         // underlay and overlay are rendered.
   238     }
   240     // Deactivates the shader program. This must be done to avoid crashes after returning to the
   241     // Gecko C++ compositor from Java.
   242     public void deactivateDefaultProgram() {
   243         GLES20.glDisableVertexAttribArray(mTextureHandle);
   244         GLES20.glDisableVertexAttribArray(mPositionHandle);
   245         GLES20.glUseProgram(0);
   246     }
   248     public int getMaxTextureSize() {
   249         return mMaxTextureSize;
   250     }
   252     public void postRenderTask(RenderTask aTask) {
   253         mTasks.add(aTask);
   254         mView.requestRender();
   255     }
   257     public void removeRenderTask(RenderTask aTask) {
   258         mTasks.remove(aTask);
   259     }
   261     private void runRenderTasks(CopyOnWriteArrayList<RenderTask> tasks, boolean after, long frameStartTime) {
   262         for (RenderTask task : tasks) {
   263             if (task.runAfter != after) {
   264                 continue;
   265             }
   267             boolean stillRunning = task.run(frameStartTime - mLastFrameTime, frameStartTime);
   269             // Remove the task from the list if its finished
   270             if (!stillRunning) {
   271                 tasks.remove(task);
   272             }
   273         }
   274     }
   276     public void addLayer(Layer layer) {
   277         synchronized (mExtraLayers) {
   278             if (mExtraLayers.contains(layer)) {
   279                 mExtraLayers.remove(layer);
   280             }
   282             mExtraLayers.add(layer);
   283         }
   284     }
   286     public void removeLayer(Layer layer) {
   287         synchronized (mExtraLayers) {
   288             mExtraLayers.remove(layer);
   289         }
   290     }
   292     private void printCheckerboardStats() {
   293         Log.d(PROFTAG, "Frames rendered over last 1000ms: " + mCompleteFramesRendered + "/" + mFramesRendered);
   294         mFramesRendered = 0;
   295         mCompleteFramesRendered = 0;
   296     }
   298     /** Used by robocop for testing purposes. Not for production use! */
   299     IntBuffer getPixels() {
   300         IntBuffer pixelBuffer = IntBuffer.allocate(mView.getWidth() * mView.getHeight());
   301         synchronized (pixelBuffer) {
   302             mPixelBuffer = pixelBuffer;
   303             mView.requestRender();
   304             try {
   305                 pixelBuffer.wait();
   306             } catch (InterruptedException ie) {
   307             }
   308             mPixelBuffer = null;
   309         }
   310         return pixelBuffer;
   311     }
   313     private RenderContext createScreenContext(ImmutableViewportMetrics metrics, PointF offset) {
   314         RectF viewport = new RectF(0.0f, 0.0f, metrics.getWidth(), metrics.getHeight());
   315         RectF pageRect = metrics.getPageRect();
   317         return createContext(viewport, pageRect, 1.0f, offset);
   318     }
   320     private RenderContext createPageContext(ImmutableViewportMetrics metrics, PointF offset) {
   321         RectF viewport = metrics.getViewport();
   322         RectF pageRect = metrics.getPageRect();
   323         float zoomFactor = metrics.zoomFactor;
   325         return createContext(new RectF(RectUtils.round(viewport)), pageRect, zoomFactor, offset);
   326     }
   328     private RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor, PointF offset) {
   329         if (mCoordBuffer == null) {
   330             throw new IllegalStateException();
   331         }
   332         return new RenderContext(viewport, pageRect, zoomFactor, offset, mPositionHandle, mTextureHandle,
   333                                  mCoordBuffer);
   334     }
   336     private void updateDroppedFrames(long frameStartTime) {
   337         int frameElapsedTime = (int)((System.nanoTime() - frameStartTime) / NANOS_PER_MS);
   339         /* Update the running statistics. */
   340         mFrameTimingsSum -= mFrameTimings[mCurrentFrame];
   341         mFrameTimingsSum += frameElapsedTime;
   342         mDroppedFrames -= (mFrameTimings[mCurrentFrame] + 1) / MAX_FRAME_TIME;
   343         mDroppedFrames += (frameElapsedTime + 1) / MAX_FRAME_TIME;
   345         mFrameTimings[mCurrentFrame] = frameElapsedTime;
   346         mCurrentFrame = (mCurrentFrame + 1) % mFrameTimings.length;
   348         int averageTime = mFrameTimingsSum / mFrameTimings.length;
   349         mFrameRateLayer.beginTransaction();     // called on compositor thread
   350         try {
   351             mFrameRateLayer.setText(averageTime + " ms/" + mDroppedFrames);
   352         } finally {
   353             mFrameRateLayer.endTransaction();
   354         }
   355     }
   357     /* Given the new dimensions for the surface, moves the frame rate layer appropriately. */
   358     private void moveFrameRateLayer(int width, int height) {
   359         mFrameRateLayer.beginTransaction();     // called on compositor thread
   360         try {
   361             Rect position = new Rect(width - FRAME_RATE_METER_WIDTH - 8,
   362                                     height - FRAME_RATE_METER_HEIGHT + 8,
   363                                     width - 8,
   364                                     height + 8);
   365             mFrameRateLayer.setPosition(position);
   366         } finally {
   367             mFrameRateLayer.endTransaction();
   368         }
   369     }
   371     void checkMonitoringEnabled() {
   372         /* Do this I/O off the main thread to minimize its impact on startup time. */
   373         new Thread(new Runnable() {
   374             @Override
   375             public void run() {
   376                 Context context = mView.getContext();
   377                 SharedPreferences preferences = context.getSharedPreferences("GeckoApp", 0);
   378                 if (preferences.getBoolean("showFrameRate", false)) {
   379                     IntSize frameRateLayerSize = new IntSize(FRAME_RATE_METER_WIDTH, FRAME_RATE_METER_HEIGHT);
   380                     mFrameRateLayer = TextLayer.create(frameRateLayerSize, "-- ms/--");
   381                     moveFrameRateLayer(mView.getWidth(), mView.getHeight());
   382                 }
   383                 mProfileRender = Log.isLoggable(PROFTAG, Log.DEBUG);
   384             }
   385         }).start();
   386     }
   388     /*
   389      * create a vertex shader type (GLES20.GL_VERTEX_SHADER)
   390      * or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
   391      */
   392     public static int loadShader(int type, String shaderCode) {
   393         int shader = GLES20.glCreateShader(type);
   394         GLES20.glShaderSource(shader, shaderCode);
   395         GLES20.glCompileShader(shader);
   396         return shader;
   397     }
   399     public Frame createFrame(ImmutableViewportMetrics metrics) {
   400         return new Frame(metrics);
   401     }
   403     class FadeRunnable implements Runnable {
   404         private boolean mStarted;
   405         private long mRunAt;
   407         void scheduleStartFade(long delay) {
   408             mRunAt = SystemClock.elapsedRealtime() + delay;
   409             if (!mStarted) {
   410                 mView.postDelayed(this, delay);
   411                 mStarted = true;
   412             }
   413         }
   415         void scheduleNextFadeFrame() {
   416             if (mStarted) {
   417                 Log.e(LOGTAG, "scheduleNextFadeFrame() called while scheduled for starting fade");
   418             }
   419             mView.postDelayed(this, 1000L / 60L); // request another frame at 60fps
   420         }
   422         boolean timeToFade() {
   423             return !mStarted;
   424         }
   426         @Override
   427         public void run() {
   428             long timeDelta = mRunAt - SystemClock.elapsedRealtime();
   429             if (timeDelta > 0) {
   430                 // the run-at time was pushed back, so reschedule
   431                 mView.postDelayed(this, timeDelta);
   432             } else {
   433                 // reached the run-at time, execute
   434                 mStarted = false;
   435                 mView.requestRender();
   436             }
   437         }
   438     }
   440     public class Frame {
   441         // The timestamp recording the start of this frame.
   442         private long mFrameStartTime;
   443         // A fixed snapshot of the viewport metrics that this frame is using to render content.
   444         private ImmutableViewportMetrics mFrameMetrics;
   445         // A rendering context for page-positioned layers, and one for screen-positioned layers.
   446         private RenderContext mPageContext, mScreenContext;
   447         // Whether a layer was updated.
   448         private boolean mUpdated;
   449         private final Rect mPageRect;
   450         private final Rect mAbsolutePageRect;
   451         private final PointF mRenderOffset;
   453         public Frame(ImmutableViewportMetrics metrics) {
   454             mFrameMetrics = metrics;
   456             // Work out the offset due to margins
   457             Layer rootLayer = mView.getLayerClient().getRoot();
   458             mRenderOffset = mFrameMetrics.getMarginOffset();
   459             mPageContext = createPageContext(metrics, mRenderOffset);
   460             mScreenContext = createScreenContext(metrics, mRenderOffset);
   462             RectF pageRect = mFrameMetrics.getPageRect();
   463             mAbsolutePageRect = RectUtils.round(pageRect);
   465             PointF origin = mFrameMetrics.getOrigin();
   466             pageRect.offset(-origin.x, -origin.y);
   467             mPageRect = RectUtils.round(pageRect);
   468         }
   470         private void setScissorRect() {
   471             Rect scissorRect = transformToScissorRect(mPageRect);
   472             GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
   473             GLES20.glScissor(scissorRect.left, scissorRect.top,
   474                              scissorRect.width(), scissorRect.height());
   475         }
   477         private Rect transformToScissorRect(Rect rect) {
   478             IntSize screenSize = new IntSize(mFrameMetrics.getSize());
   480             int left = Math.max(0, rect.left);
   481             int top = Math.max(0, rect.top);
   482             int right = Math.min(screenSize.width, rect.right);
   483             int bottom = Math.min(screenSize.height, rect.bottom);
   485             Rect scissorRect = new Rect(left, screenSize.height - bottom, right,
   486                                         (screenSize.height - bottom) + (bottom - top));
   487             scissorRect.offset(Math.round(-mRenderOffset.x), Math.round(-mRenderOffset.y));
   489             return scissorRect;
   490         }
   492         /** This function is invoked via JNI; be careful when modifying signature. */
   493         @JNITarget
   494         public void beginDrawing() {
   495             mFrameStartTime = System.nanoTime();
   497             TextureReaper.get().reap();
   498             TextureGenerator.get().fill();
   500             mUpdated = true;
   502             Layer rootLayer = mView.getLayerClient().getRoot();
   504             // Run through pre-render tasks
   505             runRenderTasks(mTasks, false, mFrameStartTime);
   507             if (!mPageContext.fuzzyEquals(mLastPageContext) && !mView.isFullScreen()) {
   508                 // The viewport or page changed, so show the scrollbars again
   509                 // as per UX decision. Don't do this if we're in full-screen mode though.
   510                 mVertScrollLayer.unfade();
   511                 mHorizScrollLayer.unfade();
   512                 mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
   513             } else if (mFadeRunnable.timeToFade()) {
   514                 boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
   515                 if (stillFading) {
   516                     mFadeRunnable.scheduleNextFadeFrame();
   517                 }
   518             }
   519             mLastPageContext = mPageContext;
   521             /* Update layers. */
   522             if (rootLayer != null) {
   523                 // Called on compositor thread.
   524                 mUpdated &= rootLayer.update(mPageContext);
   525             }
   527             if (mFrameRateLayer != null) {
   528                 // Called on compositor thread.
   529                 mUpdated &= mFrameRateLayer.update(mScreenContext);
   530             }
   532             mUpdated &= mVertScrollLayer.update(mPageContext);  // called on compositor thread
   533             mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread
   535             for (Layer layer : mExtraLayers) {
   536                 mUpdated &= layer.update(mPageContext); // called on compositor thread
   537             }
   538         }
   540         /** Retrieves the bounds for the layer, rounded in such a way that it
   541          * can be used as a mask for something that will render underneath it.
   542          * This will round the bounds inwards, but stretch the mask towards any
   543          * near page edge, where near is considered to be 'within 2 pixels'.
   544          * Returns null if the given layer is null.
   545          */
   546         private Rect getMaskForLayer(Layer layer) {
   547             if (layer == null) {
   548                 return null;
   549             }
   551             RectF bounds = RectUtils.contract(layer.getBounds(mPageContext), 1.0f, 1.0f);
   552             Rect mask = RectUtils.roundIn(bounds);
   554             // If the mask is within two pixels of any page edge, stretch it over
   555             // that edge. This is to avoid drawing thin slivers when masking
   556             // layers.
   557             if (mask.top <= 2) {
   558                 mask.top = -1;
   559             }
   560             if (mask.left <= 2) {
   561                 mask.left = -1;
   562             }
   564             // Because we're drawing relative to the page-rect, we only need to
   565             // take into account its width and height (and not its origin)
   566             int pageRight = mPageRect.width();
   567             int pageBottom = mPageRect.height();
   569             if (mask.right >= pageRight - 2) {
   570                 mask.right = pageRight + 1;
   571             }
   572             if (mask.bottom >= pageBottom - 2) {
   573                 mask.bottom = pageBottom + 1;
   574             }
   576             return mask;
   577         }
   579         private void clear(int color) {
   580             GLES20.glClearColor(((color >> 16) & 0xFF) / 255.0f,
   581                                 ((color >> 8) & 0xFF) / 255.0f,
   582                                 (color & 0xFF) / 255.0f,
   583                                 0.0f);
   584             // The bits set here need to match up with those used
   585             // in gfx/layers/opengl/LayerManagerOGL.cpp.
   586             GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT |
   587                            GLES20.GL_DEPTH_BUFFER_BIT);
   588         }
   590         /** This function is invoked via JNI; be careful when modifying signature. */
   591         @JNITarget
   592         public void drawBackground() {
   593             // Any GL state which is changed here must be restored in
   594             // CompositorOGL::RestoreState
   596             GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
   598             // Draw the overscroll background area as a solid color
   599             clear(mOverscrollColor);
   601             // Update background color.
   602             mBackgroundColor = mView.getBackgroundColor();
   604             // Clear the page area to the page background colour.
   605             setScissorRect();
   606             clear(mBackgroundColor);
   607             GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
   608         }
   610         // Draws the layer the client added to us.
   611         void drawRootLayer() {
   612             Layer rootLayer = mView.getLayerClient().getRoot();
   613             if (rootLayer == null) {
   614                 return;
   615             }
   617             rootLayer.draw(mPageContext);
   618         }
   620         @JNITarget
   621         public void drawForeground() {
   622             // Any GL state which is changed here must be restored in
   623             // CompositorOGL::RestoreState
   625             /* Draw any extra layers that were added (likely plugins) */
   626             if (mExtraLayers.size() > 0) {
   627                 for (Layer layer : mExtraLayers) {
   628                     layer.draw(mPageContext);
   629                 }
   630             }
   632             /* Draw the vertical scrollbar. */
   633             if (mPageRect.height() > mFrameMetrics.getHeight())
   634                 mVertScrollLayer.draw(mPageContext);
   636             /* Draw the horizontal scrollbar. */
   637             if (mPageRect.width() > mFrameMetrics.getWidth())
   638                 mHorizScrollLayer.draw(mPageContext);
   640             /* Measure how much of the screen is checkerboarding */
   641             Layer rootLayer = mView.getLayerClient().getRoot();
   642             if ((rootLayer != null) &&
   643                 (mProfileRender || PanningPerfAPI.isRecordingCheckerboard())) {
   644                 // Calculate the incompletely rendered area of the page
   645                 float checkerboard =  1.0f - GeckoAppShell.computeRenderIntegrity();
   647                 PanningPerfAPI.recordCheckerboard(checkerboard);
   648                 if (checkerboard < 0.0f || checkerboard > 1.0f) {
   649                     Log.e(LOGTAG, "Checkerboard value out of bounds: " + checkerboard);
   650                 }
   652                 mCompleteFramesRendered += 1.0f - checkerboard;
   653                 mFramesRendered ++;
   655                 if (mFrameStartTime - mProfileOutputTime > NANOS_PER_SECOND) {
   656                     mProfileOutputTime = mFrameStartTime;
   657                     printCheckerboardStats();
   658                 }
   659             }
   661             runRenderTasks(mTasks, true, mFrameStartTime);
   663             /* Draw the FPS. */
   664             if (mFrameRateLayer != null) {
   665                 updateDroppedFrames(mFrameStartTime);
   667                 GLES20.glEnable(GLES20.GL_BLEND);
   668                 GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
   669                 mFrameRateLayer.draw(mScreenContext);
   670             }
   671         }
   673         /** This function is invoked via JNI; be careful when modifying signature. */
   674         @JNITarget
   675         public void endDrawing() {
   676             // If a layer update requires further work, schedule another redraw
   677             if (!mUpdated)
   678                 mView.requestRender();
   680             PanningPerfAPI.recordFrameTime();
   682             /* Used by robocop for testing purposes */
   683             IntBuffer pixelBuffer = mPixelBuffer;
   684             if (mUpdated && pixelBuffer != null) {
   685                 synchronized (pixelBuffer) {
   686                     pixelBuffer.position(0);
   687                     GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(),
   688                                         (int)mScreenContext.viewport.height(), GLES20.GL_RGBA,
   689                                         GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
   690                     pixelBuffer.notify();
   691                 }
   692             }
   694             // Remove background color once we've painted. GeckoLayerClient is
   695             // responsible for setting this flag before current document is
   696             // composited.
   697             if (mView.getPaintState() == LayerView.PAINT_BEFORE_FIRST) {
   698                 mView.post(new Runnable() {
   699                     @Override
   700                     public void run() {
   701                         mView.getChildAt(0).setBackgroundColor(Color.TRANSPARENT);
   702                     }
   703                 });
   704                 mView.setPaintState(LayerView.PAINT_AFTER_FIRST);
   705             }
   706             mLastFrameTime = mFrameStartTime;
   707         }
   708     }
   710     @Override
   711     public void onTabChanged(final Tab tab, Tabs.TabEvents msg, Object data) {
   712         // Sets the background of the newly selected tab. This background color
   713         // gets cleared in endDrawing(). This function runs on the UI thread,
   714         // but other code that touches the paint state is run on the compositor
   715         // thread, so this may need to be changed if any problems appear.
   716         if (msg == Tabs.TabEvents.SELECTED) {
   717             if (mView != null) {
   718                 if (mView.getChildAt(0) != null) {
   719                     mView.getChildAt(0).setBackgroundColor(tab.getBackgroundColor());
   720                 }
   721                 mView.setPaintState(LayerView.PAINT_START);
   722             }
   723         }
   724     }
   725 }

mercurial