1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/gfx/LayerRenderer.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,725 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.gfx; 1.10 + 1.11 +import org.mozilla.gecko.GeckoAppShell; 1.12 +import org.mozilla.gecko.R; 1.13 +import org.mozilla.gecko.Tab; 1.14 +import org.mozilla.gecko.Tabs; 1.15 +import org.mozilla.gecko.gfx.Layer.RenderContext; 1.16 +import org.mozilla.gecko.gfx.RenderTask; 1.17 +import org.mozilla.gecko.mozglue.DirectBufferAllocator; 1.18 + 1.19 +import android.content.Context; 1.20 +import android.content.SharedPreferences; 1.21 +import android.content.res.Resources; 1.22 +import android.graphics.Bitmap; 1.23 +import android.graphics.Canvas; 1.24 +import android.graphics.Color; 1.25 +import android.graphics.Matrix; 1.26 +import android.graphics.PointF; 1.27 +import android.graphics.Rect; 1.28 +import android.graphics.RectF; 1.29 +import android.opengl.GLES20; 1.30 +import android.os.SystemClock; 1.31 +import android.util.Log; 1.32 +import org.mozilla.gecko.mozglue.JNITarget; 1.33 + 1.34 +import java.nio.ByteBuffer; 1.35 +import java.nio.ByteOrder; 1.36 +import java.nio.FloatBuffer; 1.37 +import java.nio.IntBuffer; 1.38 +import java.util.concurrent.CopyOnWriteArrayList; 1.39 + 1.40 +import javax.microedition.khronos.egl.EGLConfig; 1.41 + 1.42 +/** 1.43 + * The layer renderer implements the rendering logic for a layer view. 1.44 + */ 1.45 +public class LayerRenderer implements Tabs.OnTabsChangedListener { 1.46 + private static final String LOGTAG = "GeckoLayerRenderer"; 1.47 + private static final String PROFTAG = "GeckoLayerRendererProf"; 1.48 + 1.49 + /* 1.50 + * The amount of time a frame is allowed to take to render before we declare it a dropped 1.51 + * frame. 1.52 + */ 1.53 + private static final int MAX_FRAME_TIME = 16; /* 1000 ms / 60 FPS */ 1.54 + 1.55 + private static final int FRAME_RATE_METER_WIDTH = 128; 1.56 + private static final int FRAME_RATE_METER_HEIGHT = 32; 1.57 + 1.58 + private static final long NANOS_PER_MS = 1000000; 1.59 + private static final int NANOS_PER_SECOND = 1000000000; 1.60 + 1.61 + private final LayerView mView; 1.62 + private TextLayer mFrameRateLayer; 1.63 + private final ScrollbarLayer mHorizScrollLayer; 1.64 + private final ScrollbarLayer mVertScrollLayer; 1.65 + private final FadeRunnable mFadeRunnable; 1.66 + private ByteBuffer mCoordByteBuffer; 1.67 + private FloatBuffer mCoordBuffer; 1.68 + private RenderContext mLastPageContext; 1.69 + private int mMaxTextureSize; 1.70 + private int mBackgroundColor; 1.71 + private int mOverscrollColor; 1.72 + 1.73 + private long mLastFrameTime; 1.74 + private final CopyOnWriteArrayList<RenderTask> mTasks; 1.75 + 1.76 + private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>(); 1.77 + 1.78 + // Dropped frames display 1.79 + private int[] mFrameTimings; 1.80 + private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames; 1.81 + 1.82 + // Render profiling output 1.83 + private int mFramesRendered; 1.84 + private float mCompleteFramesRendered; 1.85 + private boolean mProfileRender; 1.86 + private long mProfileOutputTime; 1.87 + 1.88 + private IntBuffer mPixelBuffer; 1.89 + 1.90 + // Used by GLES 2.0 1.91 + private int mProgram; 1.92 + private int mPositionHandle; 1.93 + private int mTextureHandle; 1.94 + private int mSampleHandle; 1.95 + private int mTMatrixHandle; 1.96 + 1.97 + // column-major matrix applied to each vertex to shift the viewport from 1.98 + // one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by 1.99 + // a factor of 2 to fill up the screen 1.100 + public static final float[] DEFAULT_TEXTURE_MATRIX = { 1.101 + 2.0f, 0.0f, 0.0f, 0.0f, 1.102 + 0.0f, 2.0f, 0.0f, 0.0f, 1.103 + 0.0f, 0.0f, 2.0f, 0.0f, 1.104 + -1.0f, -1.0f, 0.0f, 1.0f 1.105 + }; 1.106 + 1.107 + private static final int COORD_BUFFER_SIZE = 20; 1.108 + 1.109 + // The shaders run on the GPU directly, the vertex shader is only applying the 1.110 + // matrix transform detailed above 1.111 + 1.112 + // Note we flip the y-coordinate in the vertex shader from a 1.113 + // coordinate system with (0,0) in the top left to one with (0,0) in 1.114 + // the bottom left. 1.115 + 1.116 + public static final String DEFAULT_VERTEX_SHADER = 1.117 + "uniform mat4 uTMatrix;\n" + 1.118 + "attribute vec4 vPosition;\n" + 1.119 + "attribute vec2 aTexCoord;\n" + 1.120 + "varying vec2 vTexCoord;\n" + 1.121 + "void main() {\n" + 1.122 + " gl_Position = uTMatrix * vPosition;\n" + 1.123 + " vTexCoord.x = aTexCoord.x;\n" + 1.124 + " vTexCoord.y = 1.0 - aTexCoord.y;\n" + 1.125 + "}\n"; 1.126 + 1.127 + // We use highp because the screenshot textures 1.128 + // we use are large and we stretch them alot 1.129 + // so we need all the precision we can get. 1.130 + // Unfortunately, highp is not required by ES 2.0 1.131 + // so on GPU's like Mali we end up getting mediump 1.132 + public static final String DEFAULT_FRAGMENT_SHADER = 1.133 + "precision highp float;\n" + 1.134 + "varying vec2 vTexCoord;\n" + 1.135 + "uniform sampler2D sTexture;\n" + 1.136 + "void main() {\n" + 1.137 + " gl_FragColor = texture2D(sTexture, vTexCoord);\n" + 1.138 + "}\n"; 1.139 + 1.140 + public LayerRenderer(LayerView view) { 1.141 + mView = view; 1.142 + try { 1.143 + mOverscrollColor = view.getContext().getResources().getColor(R.color.background_normal); 1.144 + } catch (Resources.NotFoundException nfe) { mOverscrollColor = Color.BLACK; } 1.145 + 1.146 + Bitmap scrollbarImage = view.getScrollbarImage(); 1.147 + IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight()); 1.148 + scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size); 1.149 + 1.150 + mTasks = new CopyOnWriteArrayList<RenderTask>(); 1.151 + mLastFrameTime = System.nanoTime(); 1.152 + 1.153 + mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true); 1.154 + mHorizScrollLayer = new ScrollbarLayer(this, diagonalFlip(scrollbarImage), new IntSize(size.height, size.width), false); 1.155 + mFadeRunnable = new FadeRunnable(); 1.156 + 1.157 + mFrameTimings = new int[60]; 1.158 + mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0; 1.159 + 1.160 + // Initialize the FloatBuffer that will be used to store all vertices and texture 1.161 + // coordinates in draw() commands. 1.162 + mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4); 1.163 + mCoordByteBuffer.order(ByteOrder.nativeOrder()); 1.164 + mCoordBuffer = mCoordByteBuffer.asFloatBuffer(); 1.165 + 1.166 + Tabs.registerOnTabsChangedListener(this); 1.167 + } 1.168 + 1.169 + private Bitmap expandCanvasToPowerOfTwo(Bitmap image, IntSize size) { 1.170 + IntSize potSize = size.nextPowerOfTwo(); 1.171 + if (size.equals(potSize)) { 1.172 + return image; 1.173 + } 1.174 + // make the bitmap size a power-of-two in both dimensions if it's not already. 1.175 + Bitmap potImage = Bitmap.createBitmap(potSize.width, potSize.height, image.getConfig()); 1.176 + new Canvas(potImage).drawBitmap(image, new Matrix(), null); 1.177 + return potImage; 1.178 + } 1.179 + 1.180 + private Bitmap diagonalFlip(Bitmap image) { 1.181 + Matrix rotation = new Matrix(); 1.182 + rotation.setValues(new float[] { 0, 1, 0, 1, 0, 0, 0, 0, 1 }); // transform (x,y) into (y,x) 1.183 + Bitmap rotated = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), rotation, true); 1.184 + return rotated; 1.185 + } 1.186 + 1.187 + public void destroy() { 1.188 + DirectBufferAllocator.free(mCoordByteBuffer); 1.189 + mCoordByteBuffer = null; 1.190 + mCoordBuffer = null; 1.191 + mHorizScrollLayer.destroy(); 1.192 + mVertScrollLayer.destroy(); 1.193 + if (mFrameRateLayer != null) { 1.194 + mFrameRateLayer.destroy(); 1.195 + } 1.196 + Tabs.unregisterOnTabsChangedListener(this); 1.197 + } 1.198 + 1.199 + void onSurfaceCreated(EGLConfig config) { 1.200 + checkMonitoringEnabled(); 1.201 + createDefaultProgram(); 1.202 + activateDefaultProgram(); 1.203 + } 1.204 + 1.205 + public void createDefaultProgram() { 1.206 + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DEFAULT_VERTEX_SHADER); 1.207 + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DEFAULT_FRAGMENT_SHADER); 1.208 + 1.209 + mProgram = GLES20.glCreateProgram(); 1.210 + GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program 1.211 + GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program 1.212 + GLES20.glLinkProgram(mProgram); // creates OpenGL program executables 1.213 + 1.214 + // Get handles to the vertex shader's vPosition, aTexCoord, sTexture, and uTMatrix members. 1.215 + mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); 1.216 + mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord"); 1.217 + mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture"); 1.218 + mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix"); 1.219 + 1.220 + int maxTextureSizeResult[] = new int[1]; 1.221 + GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0); 1.222 + mMaxTextureSize = maxTextureSizeResult[0]; 1.223 + } 1.224 + 1.225 + // Activates the shader program. 1.226 + public void activateDefaultProgram() { 1.227 + // Add the program to the OpenGL environment 1.228 + GLES20.glUseProgram(mProgram); 1.229 + 1.230 + // Set the transformation matrix 1.231 + GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false, DEFAULT_TEXTURE_MATRIX, 0); 1.232 + 1.233 + // Enable the arrays from which we get the vertex and texture coordinates 1.234 + GLES20.glEnableVertexAttribArray(mPositionHandle); 1.235 + GLES20.glEnableVertexAttribArray(mTextureHandle); 1.236 + 1.237 + GLES20.glUniform1i(mSampleHandle, 0); 1.238 + 1.239 + // TODO: Move these calls into a separate deactivate() call that is called after the 1.240 + // underlay and overlay are rendered. 1.241 + } 1.242 + 1.243 + // Deactivates the shader program. This must be done to avoid crashes after returning to the 1.244 + // Gecko C++ compositor from Java. 1.245 + public void deactivateDefaultProgram() { 1.246 + GLES20.glDisableVertexAttribArray(mTextureHandle); 1.247 + GLES20.glDisableVertexAttribArray(mPositionHandle); 1.248 + GLES20.glUseProgram(0); 1.249 + } 1.250 + 1.251 + public int getMaxTextureSize() { 1.252 + return mMaxTextureSize; 1.253 + } 1.254 + 1.255 + public void postRenderTask(RenderTask aTask) { 1.256 + mTasks.add(aTask); 1.257 + mView.requestRender(); 1.258 + } 1.259 + 1.260 + public void removeRenderTask(RenderTask aTask) { 1.261 + mTasks.remove(aTask); 1.262 + } 1.263 + 1.264 + private void runRenderTasks(CopyOnWriteArrayList<RenderTask> tasks, boolean after, long frameStartTime) { 1.265 + for (RenderTask task : tasks) { 1.266 + if (task.runAfter != after) { 1.267 + continue; 1.268 + } 1.269 + 1.270 + boolean stillRunning = task.run(frameStartTime - mLastFrameTime, frameStartTime); 1.271 + 1.272 + // Remove the task from the list if its finished 1.273 + if (!stillRunning) { 1.274 + tasks.remove(task); 1.275 + } 1.276 + } 1.277 + } 1.278 + 1.279 + public void addLayer(Layer layer) { 1.280 + synchronized (mExtraLayers) { 1.281 + if (mExtraLayers.contains(layer)) { 1.282 + mExtraLayers.remove(layer); 1.283 + } 1.284 + 1.285 + mExtraLayers.add(layer); 1.286 + } 1.287 + } 1.288 + 1.289 + public void removeLayer(Layer layer) { 1.290 + synchronized (mExtraLayers) { 1.291 + mExtraLayers.remove(layer); 1.292 + } 1.293 + } 1.294 + 1.295 + private void printCheckerboardStats() { 1.296 + Log.d(PROFTAG, "Frames rendered over last 1000ms: " + mCompleteFramesRendered + "/" + mFramesRendered); 1.297 + mFramesRendered = 0; 1.298 + mCompleteFramesRendered = 0; 1.299 + } 1.300 + 1.301 + /** Used by robocop for testing purposes. Not for production use! */ 1.302 + IntBuffer getPixels() { 1.303 + IntBuffer pixelBuffer = IntBuffer.allocate(mView.getWidth() * mView.getHeight()); 1.304 + synchronized (pixelBuffer) { 1.305 + mPixelBuffer = pixelBuffer; 1.306 + mView.requestRender(); 1.307 + try { 1.308 + pixelBuffer.wait(); 1.309 + } catch (InterruptedException ie) { 1.310 + } 1.311 + mPixelBuffer = null; 1.312 + } 1.313 + return pixelBuffer; 1.314 + } 1.315 + 1.316 + private RenderContext createScreenContext(ImmutableViewportMetrics metrics, PointF offset) { 1.317 + RectF viewport = new RectF(0.0f, 0.0f, metrics.getWidth(), metrics.getHeight()); 1.318 + RectF pageRect = metrics.getPageRect(); 1.319 + 1.320 + return createContext(viewport, pageRect, 1.0f, offset); 1.321 + } 1.322 + 1.323 + private RenderContext createPageContext(ImmutableViewportMetrics metrics, PointF offset) { 1.324 + RectF viewport = metrics.getViewport(); 1.325 + RectF pageRect = metrics.getPageRect(); 1.326 + float zoomFactor = metrics.zoomFactor; 1.327 + 1.328 + return createContext(new RectF(RectUtils.round(viewport)), pageRect, zoomFactor, offset); 1.329 + } 1.330 + 1.331 + private RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor, PointF offset) { 1.332 + if (mCoordBuffer == null) { 1.333 + throw new IllegalStateException(); 1.334 + } 1.335 + return new RenderContext(viewport, pageRect, zoomFactor, offset, mPositionHandle, mTextureHandle, 1.336 + mCoordBuffer); 1.337 + } 1.338 + 1.339 + private void updateDroppedFrames(long frameStartTime) { 1.340 + int frameElapsedTime = (int)((System.nanoTime() - frameStartTime) / NANOS_PER_MS); 1.341 + 1.342 + /* Update the running statistics. */ 1.343 + mFrameTimingsSum -= mFrameTimings[mCurrentFrame]; 1.344 + mFrameTimingsSum += frameElapsedTime; 1.345 + mDroppedFrames -= (mFrameTimings[mCurrentFrame] + 1) / MAX_FRAME_TIME; 1.346 + mDroppedFrames += (frameElapsedTime + 1) / MAX_FRAME_TIME; 1.347 + 1.348 + mFrameTimings[mCurrentFrame] = frameElapsedTime; 1.349 + mCurrentFrame = (mCurrentFrame + 1) % mFrameTimings.length; 1.350 + 1.351 + int averageTime = mFrameTimingsSum / mFrameTimings.length; 1.352 + mFrameRateLayer.beginTransaction(); // called on compositor thread 1.353 + try { 1.354 + mFrameRateLayer.setText(averageTime + " ms/" + mDroppedFrames); 1.355 + } finally { 1.356 + mFrameRateLayer.endTransaction(); 1.357 + } 1.358 + } 1.359 + 1.360 + /* Given the new dimensions for the surface, moves the frame rate layer appropriately. */ 1.361 + private void moveFrameRateLayer(int width, int height) { 1.362 + mFrameRateLayer.beginTransaction(); // called on compositor thread 1.363 + try { 1.364 + Rect position = new Rect(width - FRAME_RATE_METER_WIDTH - 8, 1.365 + height - FRAME_RATE_METER_HEIGHT + 8, 1.366 + width - 8, 1.367 + height + 8); 1.368 + mFrameRateLayer.setPosition(position); 1.369 + } finally { 1.370 + mFrameRateLayer.endTransaction(); 1.371 + } 1.372 + } 1.373 + 1.374 + void checkMonitoringEnabled() { 1.375 + /* Do this I/O off the main thread to minimize its impact on startup time. */ 1.376 + new Thread(new Runnable() { 1.377 + @Override 1.378 + public void run() { 1.379 + Context context = mView.getContext(); 1.380 + SharedPreferences preferences = context.getSharedPreferences("GeckoApp", 0); 1.381 + if (preferences.getBoolean("showFrameRate", false)) { 1.382 + IntSize frameRateLayerSize = new IntSize(FRAME_RATE_METER_WIDTH, FRAME_RATE_METER_HEIGHT); 1.383 + mFrameRateLayer = TextLayer.create(frameRateLayerSize, "-- ms/--"); 1.384 + moveFrameRateLayer(mView.getWidth(), mView.getHeight()); 1.385 + } 1.386 + mProfileRender = Log.isLoggable(PROFTAG, Log.DEBUG); 1.387 + } 1.388 + }).start(); 1.389 + } 1.390 + 1.391 + /* 1.392 + * create a vertex shader type (GLES20.GL_VERTEX_SHADER) 1.393 + * or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) 1.394 + */ 1.395 + public static int loadShader(int type, String shaderCode) { 1.396 + int shader = GLES20.glCreateShader(type); 1.397 + GLES20.glShaderSource(shader, shaderCode); 1.398 + GLES20.glCompileShader(shader); 1.399 + return shader; 1.400 + } 1.401 + 1.402 + public Frame createFrame(ImmutableViewportMetrics metrics) { 1.403 + return new Frame(metrics); 1.404 + } 1.405 + 1.406 + class FadeRunnable implements Runnable { 1.407 + private boolean mStarted; 1.408 + private long mRunAt; 1.409 + 1.410 + void scheduleStartFade(long delay) { 1.411 + mRunAt = SystemClock.elapsedRealtime() + delay; 1.412 + if (!mStarted) { 1.413 + mView.postDelayed(this, delay); 1.414 + mStarted = true; 1.415 + } 1.416 + } 1.417 + 1.418 + void scheduleNextFadeFrame() { 1.419 + if (mStarted) { 1.420 + Log.e(LOGTAG, "scheduleNextFadeFrame() called while scheduled for starting fade"); 1.421 + } 1.422 + mView.postDelayed(this, 1000L / 60L); // request another frame at 60fps 1.423 + } 1.424 + 1.425 + boolean timeToFade() { 1.426 + return !mStarted; 1.427 + } 1.428 + 1.429 + @Override 1.430 + public void run() { 1.431 + long timeDelta = mRunAt - SystemClock.elapsedRealtime(); 1.432 + if (timeDelta > 0) { 1.433 + // the run-at time was pushed back, so reschedule 1.434 + mView.postDelayed(this, timeDelta); 1.435 + } else { 1.436 + // reached the run-at time, execute 1.437 + mStarted = false; 1.438 + mView.requestRender(); 1.439 + } 1.440 + } 1.441 + } 1.442 + 1.443 + public class Frame { 1.444 + // The timestamp recording the start of this frame. 1.445 + private long mFrameStartTime; 1.446 + // A fixed snapshot of the viewport metrics that this frame is using to render content. 1.447 + private ImmutableViewportMetrics mFrameMetrics; 1.448 + // A rendering context for page-positioned layers, and one for screen-positioned layers. 1.449 + private RenderContext mPageContext, mScreenContext; 1.450 + // Whether a layer was updated. 1.451 + private boolean mUpdated; 1.452 + private final Rect mPageRect; 1.453 + private final Rect mAbsolutePageRect; 1.454 + private final PointF mRenderOffset; 1.455 + 1.456 + public Frame(ImmutableViewportMetrics metrics) { 1.457 + mFrameMetrics = metrics; 1.458 + 1.459 + // Work out the offset due to margins 1.460 + Layer rootLayer = mView.getLayerClient().getRoot(); 1.461 + mRenderOffset = mFrameMetrics.getMarginOffset(); 1.462 + mPageContext = createPageContext(metrics, mRenderOffset); 1.463 + mScreenContext = createScreenContext(metrics, mRenderOffset); 1.464 + 1.465 + RectF pageRect = mFrameMetrics.getPageRect(); 1.466 + mAbsolutePageRect = RectUtils.round(pageRect); 1.467 + 1.468 + PointF origin = mFrameMetrics.getOrigin(); 1.469 + pageRect.offset(-origin.x, -origin.y); 1.470 + mPageRect = RectUtils.round(pageRect); 1.471 + } 1.472 + 1.473 + private void setScissorRect() { 1.474 + Rect scissorRect = transformToScissorRect(mPageRect); 1.475 + GLES20.glEnable(GLES20.GL_SCISSOR_TEST); 1.476 + GLES20.glScissor(scissorRect.left, scissorRect.top, 1.477 + scissorRect.width(), scissorRect.height()); 1.478 + } 1.479 + 1.480 + private Rect transformToScissorRect(Rect rect) { 1.481 + IntSize screenSize = new IntSize(mFrameMetrics.getSize()); 1.482 + 1.483 + int left = Math.max(0, rect.left); 1.484 + int top = Math.max(0, rect.top); 1.485 + int right = Math.min(screenSize.width, rect.right); 1.486 + int bottom = Math.min(screenSize.height, rect.bottom); 1.487 + 1.488 + Rect scissorRect = new Rect(left, screenSize.height - bottom, right, 1.489 + (screenSize.height - bottom) + (bottom - top)); 1.490 + scissorRect.offset(Math.round(-mRenderOffset.x), Math.round(-mRenderOffset.y)); 1.491 + 1.492 + return scissorRect; 1.493 + } 1.494 + 1.495 + /** This function is invoked via JNI; be careful when modifying signature. */ 1.496 + @JNITarget 1.497 + public void beginDrawing() { 1.498 + mFrameStartTime = System.nanoTime(); 1.499 + 1.500 + TextureReaper.get().reap(); 1.501 + TextureGenerator.get().fill(); 1.502 + 1.503 + mUpdated = true; 1.504 + 1.505 + Layer rootLayer = mView.getLayerClient().getRoot(); 1.506 + 1.507 + // Run through pre-render tasks 1.508 + runRenderTasks(mTasks, false, mFrameStartTime); 1.509 + 1.510 + if (!mPageContext.fuzzyEquals(mLastPageContext) && !mView.isFullScreen()) { 1.511 + // The viewport or page changed, so show the scrollbars again 1.512 + // as per UX decision. Don't do this if we're in full-screen mode though. 1.513 + mVertScrollLayer.unfade(); 1.514 + mHorizScrollLayer.unfade(); 1.515 + mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY); 1.516 + } else if (mFadeRunnable.timeToFade()) { 1.517 + boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade(); 1.518 + if (stillFading) { 1.519 + mFadeRunnable.scheduleNextFadeFrame(); 1.520 + } 1.521 + } 1.522 + mLastPageContext = mPageContext; 1.523 + 1.524 + /* Update layers. */ 1.525 + if (rootLayer != null) { 1.526 + // Called on compositor thread. 1.527 + mUpdated &= rootLayer.update(mPageContext); 1.528 + } 1.529 + 1.530 + if (mFrameRateLayer != null) { 1.531 + // Called on compositor thread. 1.532 + mUpdated &= mFrameRateLayer.update(mScreenContext); 1.533 + } 1.534 + 1.535 + mUpdated &= mVertScrollLayer.update(mPageContext); // called on compositor thread 1.536 + mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread 1.537 + 1.538 + for (Layer layer : mExtraLayers) { 1.539 + mUpdated &= layer.update(mPageContext); // called on compositor thread 1.540 + } 1.541 + } 1.542 + 1.543 + /** Retrieves the bounds for the layer, rounded in such a way that it 1.544 + * can be used as a mask for something that will render underneath it. 1.545 + * This will round the bounds inwards, but stretch the mask towards any 1.546 + * near page edge, where near is considered to be 'within 2 pixels'. 1.547 + * Returns null if the given layer is null. 1.548 + */ 1.549 + private Rect getMaskForLayer(Layer layer) { 1.550 + if (layer == null) { 1.551 + return null; 1.552 + } 1.553 + 1.554 + RectF bounds = RectUtils.contract(layer.getBounds(mPageContext), 1.0f, 1.0f); 1.555 + Rect mask = RectUtils.roundIn(bounds); 1.556 + 1.557 + // If the mask is within two pixels of any page edge, stretch it over 1.558 + // that edge. This is to avoid drawing thin slivers when masking 1.559 + // layers. 1.560 + if (mask.top <= 2) { 1.561 + mask.top = -1; 1.562 + } 1.563 + if (mask.left <= 2) { 1.564 + mask.left = -1; 1.565 + } 1.566 + 1.567 + // Because we're drawing relative to the page-rect, we only need to 1.568 + // take into account its width and height (and not its origin) 1.569 + int pageRight = mPageRect.width(); 1.570 + int pageBottom = mPageRect.height(); 1.571 + 1.572 + if (mask.right >= pageRight - 2) { 1.573 + mask.right = pageRight + 1; 1.574 + } 1.575 + if (mask.bottom >= pageBottom - 2) { 1.576 + mask.bottom = pageBottom + 1; 1.577 + } 1.578 + 1.579 + return mask; 1.580 + } 1.581 + 1.582 + private void clear(int color) { 1.583 + GLES20.glClearColor(((color >> 16) & 0xFF) / 255.0f, 1.584 + ((color >> 8) & 0xFF) / 255.0f, 1.585 + (color & 0xFF) / 255.0f, 1.586 + 0.0f); 1.587 + // The bits set here need to match up with those used 1.588 + // in gfx/layers/opengl/LayerManagerOGL.cpp. 1.589 + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | 1.590 + GLES20.GL_DEPTH_BUFFER_BIT); 1.591 + } 1.592 + 1.593 + /** This function is invoked via JNI; be careful when modifying signature. */ 1.594 + @JNITarget 1.595 + public void drawBackground() { 1.596 + // Any GL state which is changed here must be restored in 1.597 + // CompositorOGL::RestoreState 1.598 + 1.599 + GLES20.glDisable(GLES20.GL_SCISSOR_TEST); 1.600 + 1.601 + // Draw the overscroll background area as a solid color 1.602 + clear(mOverscrollColor); 1.603 + 1.604 + // Update background color. 1.605 + mBackgroundColor = mView.getBackgroundColor(); 1.606 + 1.607 + // Clear the page area to the page background colour. 1.608 + setScissorRect(); 1.609 + clear(mBackgroundColor); 1.610 + GLES20.glDisable(GLES20.GL_SCISSOR_TEST); 1.611 + } 1.612 + 1.613 + // Draws the layer the client added to us. 1.614 + void drawRootLayer() { 1.615 + Layer rootLayer = mView.getLayerClient().getRoot(); 1.616 + if (rootLayer == null) { 1.617 + return; 1.618 + } 1.619 + 1.620 + rootLayer.draw(mPageContext); 1.621 + } 1.622 + 1.623 + @JNITarget 1.624 + public void drawForeground() { 1.625 + // Any GL state which is changed here must be restored in 1.626 + // CompositorOGL::RestoreState 1.627 + 1.628 + /* Draw any extra layers that were added (likely plugins) */ 1.629 + if (mExtraLayers.size() > 0) { 1.630 + for (Layer layer : mExtraLayers) { 1.631 + layer.draw(mPageContext); 1.632 + } 1.633 + } 1.634 + 1.635 + /* Draw the vertical scrollbar. */ 1.636 + if (mPageRect.height() > mFrameMetrics.getHeight()) 1.637 + mVertScrollLayer.draw(mPageContext); 1.638 + 1.639 + /* Draw the horizontal scrollbar. */ 1.640 + if (mPageRect.width() > mFrameMetrics.getWidth()) 1.641 + mHorizScrollLayer.draw(mPageContext); 1.642 + 1.643 + /* Measure how much of the screen is checkerboarding */ 1.644 + Layer rootLayer = mView.getLayerClient().getRoot(); 1.645 + if ((rootLayer != null) && 1.646 + (mProfileRender || PanningPerfAPI.isRecordingCheckerboard())) { 1.647 + // Calculate the incompletely rendered area of the page 1.648 + float checkerboard = 1.0f - GeckoAppShell.computeRenderIntegrity(); 1.649 + 1.650 + PanningPerfAPI.recordCheckerboard(checkerboard); 1.651 + if (checkerboard < 0.0f || checkerboard > 1.0f) { 1.652 + Log.e(LOGTAG, "Checkerboard value out of bounds: " + checkerboard); 1.653 + } 1.654 + 1.655 + mCompleteFramesRendered += 1.0f - checkerboard; 1.656 + mFramesRendered ++; 1.657 + 1.658 + if (mFrameStartTime - mProfileOutputTime > NANOS_PER_SECOND) { 1.659 + mProfileOutputTime = mFrameStartTime; 1.660 + printCheckerboardStats(); 1.661 + } 1.662 + } 1.663 + 1.664 + runRenderTasks(mTasks, true, mFrameStartTime); 1.665 + 1.666 + /* Draw the FPS. */ 1.667 + if (mFrameRateLayer != null) { 1.668 + updateDroppedFrames(mFrameStartTime); 1.669 + 1.670 + GLES20.glEnable(GLES20.GL_BLEND); 1.671 + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); 1.672 + mFrameRateLayer.draw(mScreenContext); 1.673 + } 1.674 + } 1.675 + 1.676 + /** This function is invoked via JNI; be careful when modifying signature. */ 1.677 + @JNITarget 1.678 + public void endDrawing() { 1.679 + // If a layer update requires further work, schedule another redraw 1.680 + if (!mUpdated) 1.681 + mView.requestRender(); 1.682 + 1.683 + PanningPerfAPI.recordFrameTime(); 1.684 + 1.685 + /* Used by robocop for testing purposes */ 1.686 + IntBuffer pixelBuffer = mPixelBuffer; 1.687 + if (mUpdated && pixelBuffer != null) { 1.688 + synchronized (pixelBuffer) { 1.689 + pixelBuffer.position(0); 1.690 + GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(), 1.691 + (int)mScreenContext.viewport.height(), GLES20.GL_RGBA, 1.692 + GLES20.GL_UNSIGNED_BYTE, pixelBuffer); 1.693 + pixelBuffer.notify(); 1.694 + } 1.695 + } 1.696 + 1.697 + // Remove background color once we've painted. GeckoLayerClient is 1.698 + // responsible for setting this flag before current document is 1.699 + // composited. 1.700 + if (mView.getPaintState() == LayerView.PAINT_BEFORE_FIRST) { 1.701 + mView.post(new Runnable() { 1.702 + @Override 1.703 + public void run() { 1.704 + mView.getChildAt(0).setBackgroundColor(Color.TRANSPARENT); 1.705 + } 1.706 + }); 1.707 + mView.setPaintState(LayerView.PAINT_AFTER_FIRST); 1.708 + } 1.709 + mLastFrameTime = mFrameStartTime; 1.710 + } 1.711 + } 1.712 + 1.713 + @Override 1.714 + public void onTabChanged(final Tab tab, Tabs.TabEvents msg, Object data) { 1.715 + // Sets the background of the newly selected tab. This background color 1.716 + // gets cleared in endDrawing(). This function runs on the UI thread, 1.717 + // but other code that touches the paint state is run on the compositor 1.718 + // thread, so this may need to be changed if any problems appear. 1.719 + if (msg == Tabs.TabEvents.SELECTED) { 1.720 + if (mView != null) { 1.721 + if (mView.getChildAt(0) != null) { 1.722 + mView.getChildAt(0).setBackgroundColor(tab.getBackgroundColor()); 1.723 + } 1.724 + mView.setPaintState(LayerView.PAINT_START); 1.725 + } 1.726 + } 1.727 + } 1.728 +}