mobile/android/base/gfx/LayerRenderer.java

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:4d7ac38860b1
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/. */
5
6 package org.mozilla.gecko.gfx;
7
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;
15
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;
30
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;
36
37 import javax.microedition.khronos.egl.EGLConfig;
38
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";
45
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 */
51
52 private static final int FRAME_RATE_METER_WIDTH = 128;
53 private static final int FRAME_RATE_METER_HEIGHT = 32;
54
55 private static final long NANOS_PER_MS = 1000000;
56 private static final int NANOS_PER_SECOND = 1000000000;
57
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;
69
70 private long mLastFrameTime;
71 private final CopyOnWriteArrayList<RenderTask> mTasks;
72
73 private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
74
75 // Dropped frames display
76 private int[] mFrameTimings;
77 private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames;
78
79 // Render profiling output
80 private int mFramesRendered;
81 private float mCompleteFramesRendered;
82 private boolean mProfileRender;
83 private long mProfileOutputTime;
84
85 private IntBuffer mPixelBuffer;
86
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;
93
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 };
103
104 private static final int COORD_BUFFER_SIZE = 20;
105
106 // The shaders run on the GPU directly, the vertex shader is only applying the
107 // matrix transform detailed above
108
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.
112
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";
123
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";
136
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; }
142
143 Bitmap scrollbarImage = view.getScrollbarImage();
144 IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight());
145 scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size);
146
147 mTasks = new CopyOnWriteArrayList<RenderTask>();
148 mLastFrameTime = System.nanoTime();
149
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();
153
154 mFrameTimings = new int[60];
155 mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0;
156
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();
162
163 Tabs.registerOnTabsChangedListener(this);
164 }
165
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 }
176
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 }
183
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 }
195
196 void onSurfaceCreated(EGLConfig config) {
197 checkMonitoringEnabled();
198 createDefaultProgram();
199 activateDefaultProgram();
200 }
201
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);
205
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
210
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");
216
217 int maxTextureSizeResult[] = new int[1];
218 GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0);
219 mMaxTextureSize = maxTextureSizeResult[0];
220 }
221
222 // Activates the shader program.
223 public void activateDefaultProgram() {
224 // Add the program to the OpenGL environment
225 GLES20.glUseProgram(mProgram);
226
227 // Set the transformation matrix
228 GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false, DEFAULT_TEXTURE_MATRIX, 0);
229
230 // Enable the arrays from which we get the vertex and texture coordinates
231 GLES20.glEnableVertexAttribArray(mPositionHandle);
232 GLES20.glEnableVertexAttribArray(mTextureHandle);
233
234 GLES20.glUniform1i(mSampleHandle, 0);
235
236 // TODO: Move these calls into a separate deactivate() call that is called after the
237 // underlay and overlay are rendered.
238 }
239
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 }
247
248 public int getMaxTextureSize() {
249 return mMaxTextureSize;
250 }
251
252 public void postRenderTask(RenderTask aTask) {
253 mTasks.add(aTask);
254 mView.requestRender();
255 }
256
257 public void removeRenderTask(RenderTask aTask) {
258 mTasks.remove(aTask);
259 }
260
261 private void runRenderTasks(CopyOnWriteArrayList<RenderTask> tasks, boolean after, long frameStartTime) {
262 for (RenderTask task : tasks) {
263 if (task.runAfter != after) {
264 continue;
265 }
266
267 boolean stillRunning = task.run(frameStartTime - mLastFrameTime, frameStartTime);
268
269 // Remove the task from the list if its finished
270 if (!stillRunning) {
271 tasks.remove(task);
272 }
273 }
274 }
275
276 public void addLayer(Layer layer) {
277 synchronized (mExtraLayers) {
278 if (mExtraLayers.contains(layer)) {
279 mExtraLayers.remove(layer);
280 }
281
282 mExtraLayers.add(layer);
283 }
284 }
285
286 public void removeLayer(Layer layer) {
287 synchronized (mExtraLayers) {
288 mExtraLayers.remove(layer);
289 }
290 }
291
292 private void printCheckerboardStats() {
293 Log.d(PROFTAG, "Frames rendered over last 1000ms: " + mCompleteFramesRendered + "/" + mFramesRendered);
294 mFramesRendered = 0;
295 mCompleteFramesRendered = 0;
296 }
297
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 }
312
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();
316
317 return createContext(viewport, pageRect, 1.0f, offset);
318 }
319
320 private RenderContext createPageContext(ImmutableViewportMetrics metrics, PointF offset) {
321 RectF viewport = metrics.getViewport();
322 RectF pageRect = metrics.getPageRect();
323 float zoomFactor = metrics.zoomFactor;
324
325 return createContext(new RectF(RectUtils.round(viewport)), pageRect, zoomFactor, offset);
326 }
327
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 }
335
336 private void updateDroppedFrames(long frameStartTime) {
337 int frameElapsedTime = (int)((System.nanoTime() - frameStartTime) / NANOS_PER_MS);
338
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;
344
345 mFrameTimings[mCurrentFrame] = frameElapsedTime;
346 mCurrentFrame = (mCurrentFrame + 1) % mFrameTimings.length;
347
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 }
356
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 }
370
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 }
387
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 }
398
399 public Frame createFrame(ImmutableViewportMetrics metrics) {
400 return new Frame(metrics);
401 }
402
403 class FadeRunnable implements Runnable {
404 private boolean mStarted;
405 private long mRunAt;
406
407 void scheduleStartFade(long delay) {
408 mRunAt = SystemClock.elapsedRealtime() + delay;
409 if (!mStarted) {
410 mView.postDelayed(this, delay);
411 mStarted = true;
412 }
413 }
414
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 }
421
422 boolean timeToFade() {
423 return !mStarted;
424 }
425
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 }
439
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;
452
453 public Frame(ImmutableViewportMetrics metrics) {
454 mFrameMetrics = metrics;
455
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);
461
462 RectF pageRect = mFrameMetrics.getPageRect();
463 mAbsolutePageRect = RectUtils.round(pageRect);
464
465 PointF origin = mFrameMetrics.getOrigin();
466 pageRect.offset(-origin.x, -origin.y);
467 mPageRect = RectUtils.round(pageRect);
468 }
469
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 }
476
477 private Rect transformToScissorRect(Rect rect) {
478 IntSize screenSize = new IntSize(mFrameMetrics.getSize());
479
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);
484
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));
488
489 return scissorRect;
490 }
491
492 /** This function is invoked via JNI; be careful when modifying signature. */
493 @JNITarget
494 public void beginDrawing() {
495 mFrameStartTime = System.nanoTime();
496
497 TextureReaper.get().reap();
498 TextureGenerator.get().fill();
499
500 mUpdated = true;
501
502 Layer rootLayer = mView.getLayerClient().getRoot();
503
504 // Run through pre-render tasks
505 runRenderTasks(mTasks, false, mFrameStartTime);
506
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;
520
521 /* Update layers. */
522 if (rootLayer != null) {
523 // Called on compositor thread.
524 mUpdated &= rootLayer.update(mPageContext);
525 }
526
527 if (mFrameRateLayer != null) {
528 // Called on compositor thread.
529 mUpdated &= mFrameRateLayer.update(mScreenContext);
530 }
531
532 mUpdated &= mVertScrollLayer.update(mPageContext); // called on compositor thread
533 mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread
534
535 for (Layer layer : mExtraLayers) {
536 mUpdated &= layer.update(mPageContext); // called on compositor thread
537 }
538 }
539
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 }
550
551 RectF bounds = RectUtils.contract(layer.getBounds(mPageContext), 1.0f, 1.0f);
552 Rect mask = RectUtils.roundIn(bounds);
553
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 }
563
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();
568
569 if (mask.right >= pageRight - 2) {
570 mask.right = pageRight + 1;
571 }
572 if (mask.bottom >= pageBottom - 2) {
573 mask.bottom = pageBottom + 1;
574 }
575
576 return mask;
577 }
578
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 }
589
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
595
596 GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
597
598 // Draw the overscroll background area as a solid color
599 clear(mOverscrollColor);
600
601 // Update background color.
602 mBackgroundColor = mView.getBackgroundColor();
603
604 // Clear the page area to the page background colour.
605 setScissorRect();
606 clear(mBackgroundColor);
607 GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
608 }
609
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 }
616
617 rootLayer.draw(mPageContext);
618 }
619
620 @JNITarget
621 public void drawForeground() {
622 // Any GL state which is changed here must be restored in
623 // CompositorOGL::RestoreState
624
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 }
631
632 /* Draw the vertical scrollbar. */
633 if (mPageRect.height() > mFrameMetrics.getHeight())
634 mVertScrollLayer.draw(mPageContext);
635
636 /* Draw the horizontal scrollbar. */
637 if (mPageRect.width() > mFrameMetrics.getWidth())
638 mHorizScrollLayer.draw(mPageContext);
639
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();
646
647 PanningPerfAPI.recordCheckerboard(checkerboard);
648 if (checkerboard < 0.0f || checkerboard > 1.0f) {
649 Log.e(LOGTAG, "Checkerboard value out of bounds: " + checkerboard);
650 }
651
652 mCompleteFramesRendered += 1.0f - checkerboard;
653 mFramesRendered ++;
654
655 if (mFrameStartTime - mProfileOutputTime > NANOS_PER_SECOND) {
656 mProfileOutputTime = mFrameStartTime;
657 printCheckerboardStats();
658 }
659 }
660
661 runRenderTasks(mTasks, true, mFrameStartTime);
662
663 /* Draw the FPS. */
664 if (mFrameRateLayer != null) {
665 updateDroppedFrames(mFrameStartTime);
666
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 }
672
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();
679
680 PanningPerfAPI.recordFrameTime();
681
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 }
693
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 }
709
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