mobile/android/base/gfx/LayerRenderer.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial