Update git submodules
[LibreOffice.git] / android / source / src / java / org / mozilla / gecko / gfx / LayerRenderer.java
blobbe8f22c5348f67af3bfa0fe389c8b302726e8a1e
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 android.graphics.Color;
9 import android.graphics.Point;
10 import android.graphics.Rect;
11 import android.graphics.RectF;
12 import android.opengl.GLES20;
13 import android.opengl.GLSurfaceView;
14 import android.os.SystemClock;
15 import android.util.Log;
17 import org.libreoffice.kit.DirectBufferAllocator;
18 import org.mozilla.gecko.gfx.Layer.RenderContext;
20 import java.nio.ByteBuffer;
21 import java.nio.ByteOrder;
22 import java.nio.FloatBuffer;
23 import java.util.concurrent.CopyOnWriteArrayList;
25 import javax.microedition.khronos.egl.EGLConfig;
26 import javax.microedition.khronos.opengles.GL10;
28 /**
29 * The layer renderer implements the rendering logic for a layer view.
31 public class LayerRenderer implements GLSurfaceView.Renderer {
32 private static final String LOGTAG = "GeckoLayerRenderer";
35 * The amount of time a frame is allowed to take to render before we declare it a dropped
36 * frame.
38 private static final int MAX_FRAME_TIME = 16; /* 1000 ms / 60 FPS */
40 private final LayerView mView;
41 private final SingleTileLayer mBackgroundLayer;
42 private final NinePatchTileLayer mShadowLayer;
43 private final ScrollbarLayer mHorizScrollLayer;
44 private final ScrollbarLayer mVertScrollLayer;
45 private final FadeRunnable mFadeRunnable;
46 private ByteBuffer mCoordByteBuffer;
47 private FloatBuffer mCoordBuffer;
48 private RenderContext mLastPageContext;
49 private int mMaxTextureSize;
51 private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
53 // Used by GLES 2.0
54 private int mProgram;
55 private int mPositionHandle;
56 private int mTextureHandle;
57 private int mSampleHandle;
58 private int mTMatrixHandle;
60 // column-major matrix applied to each vertex to shift the viewport from
61 // one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by
62 // a factor of 2 to fill up the screen
63 public static final float[] DEFAULT_TEXTURE_MATRIX = {
64 2.0f, 0.0f, 0.0f, 0.0f,
65 0.0f, 2.0f, 0.0f, 0.0f,
66 0.0f, 0.0f, 2.0f, 0.0f,
67 -1.0f, -1.0f, 0.0f, 1.0f
70 private static final int COORD_BUFFER_SIZE = 20;
72 // The shaders run on the GPU directly, the vertex shader is only applying the
73 // matrix transform detailed above
75 // Note we flip the y-coordinate in the vertex shader from a
76 // coordinate system with (0,0) in the top left to one with (0,0) in
77 // the bottom left.
79 public static final String DEFAULT_VERTEX_SHADER =
80 "uniform mat4 uTMatrix;\n" +
81 "attribute vec4 vPosition;\n" +
82 "attribute vec2 aTexCoord;\n" +
83 "varying vec2 vTexCoord;\n" +
84 "void main() {\n" +
85 " gl_Position = uTMatrix * vPosition;\n" +
86 " vTexCoord.x = aTexCoord.x;\n" +
87 " vTexCoord.y = 1.0 - aTexCoord.y;\n" +
88 "}\n";
90 // We use highp because the screenshot textures
91 // we use are large and we stretch them a lot
92 // so we need all the precision we can get.
93 // Unfortunately, highp is not required by ES 2.0
94 // so on GPU's like Mali we end up getting mediump
95 public static final String DEFAULT_FRAGMENT_SHADER =
96 "precision highp float;\n" +
97 "varying vec2 vTexCoord;\n" +
98 "uniform sampler2D sTexture;\n" +
99 "void main() {\n" +
100 " gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
101 "}\n";
103 public LayerRenderer(LayerView view) {
104 mView = view;
106 CairoImage backgroundImage = new BufferedCairoImage(view.getBackgroundPattern());
107 mBackgroundLayer = new SingleTileLayer(true, backgroundImage);
109 CairoImage shadowImage = new BufferedCairoImage(view.getShadowPattern());
110 mShadowLayer = new NinePatchTileLayer(shadowImage);
112 mHorizScrollLayer = ScrollbarLayer.create(this, false);
113 mVertScrollLayer = ScrollbarLayer.create(this, true);
114 mFadeRunnable = new FadeRunnable();
116 // Initialize the FloatBuffer that will be used to store all vertices and texture
117 // coordinates in draw() commands.
118 mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4);
119 mCoordByteBuffer.order(ByteOrder.nativeOrder());
120 mCoordBuffer = mCoordByteBuffer.asFloatBuffer();
123 @Override
124 protected void finalize() throws Throwable {
125 try {
126 DirectBufferAllocator.free(mCoordByteBuffer);
127 mCoordByteBuffer = null;
128 mCoordBuffer = null;
129 } finally {
130 super.finalize();
134 public void destroy() {
135 DirectBufferAllocator.free(mCoordByteBuffer);
136 mCoordByteBuffer = null;
137 mCoordBuffer = null;
138 mBackgroundLayer.destroy();
139 mShadowLayer.destroy();
140 mHorizScrollLayer.destroy();
141 mVertScrollLayer.destroy();
144 @Override
145 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
146 createDefaultProgram();
147 activateDefaultProgram();
150 public void createDefaultProgram() {
151 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DEFAULT_VERTEX_SHADER);
152 int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DEFAULT_FRAGMENT_SHADER);
154 mProgram = GLES20.glCreateProgram();
155 GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
156 GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
157 GLES20.glLinkProgram(mProgram); // creates OpenGL program executables
159 // Get handles to the vertex shader's vPosition, aTexCoord, sTexture, and uTMatrix members.
160 mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
161 mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
162 mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
163 mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix");
165 int maxTextureSizeResult[] = new int[1];
166 GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0);
167 mMaxTextureSize = maxTextureSizeResult[0];
170 // Activates the shader program.
171 public void activateDefaultProgram() {
172 // Add the program to the OpenGL environment
173 GLES20.glUseProgram(mProgram);
175 // Set the transformation matrix
176 GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false, DEFAULT_TEXTURE_MATRIX, 0);
178 // Enable the arrays from which we get the vertex and texture coordinates
179 GLES20.glEnableVertexAttribArray(mPositionHandle);
180 GLES20.glEnableVertexAttribArray(mTextureHandle);
182 GLES20.glUniform1i(mSampleHandle, 0);
185 // Deactivates the shader program. This must be done to avoid crashes after returning to the
186 // Gecko C++ compositor from Java.
187 public void deactivateDefaultProgram() {
188 GLES20.glDisableVertexAttribArray(mTextureHandle);
189 GLES20.glDisableVertexAttribArray(mPositionHandle);
190 GLES20.glUseProgram(0);
193 public int getMaxTextureSize() {
194 return mMaxTextureSize;
197 public void addLayer(Layer layer) {
198 synchronized (mExtraLayers) {
199 if (mExtraLayers.contains(layer)) {
200 mExtraLayers.remove(layer);
203 mExtraLayers.add(layer);
207 public void removeLayer(Layer layer) {
208 synchronized (mExtraLayers) {
209 mExtraLayers.remove(layer);
214 * Called whenever a new frame is about to be drawn.
216 @Override
217 public void onDrawFrame(GL10 gl) {
218 Frame frame = new Frame(mView.getLayerClient().getViewportMetrics());
219 synchronized (mView.getLayerClient()) {
220 frame.beginDrawing();
221 frame.drawBackground();
222 frame.drawRootLayer();
223 frame.drawForeground();
224 frame.endDrawing();
228 private RenderContext createScreenContext(ImmutableViewportMetrics metrics) {
229 RectF viewport = new RectF(0.0f, 0.0f, metrics.getWidth(), metrics.getHeight());
230 RectF pageRect = new RectF(metrics.getPageRect());
231 return createContext(viewport, pageRect, 1.0f);
234 private RenderContext createPageContext(ImmutableViewportMetrics metrics) {
235 Rect viewport = RectUtils.round(metrics.getViewport());
236 RectF pageRect = metrics.getPageRect();
237 float zoomFactor = metrics.zoomFactor;
238 return createContext(new RectF(viewport), pageRect, zoomFactor);
241 private RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor) {
242 return new RenderContext(viewport, pageRect, zoomFactor, mPositionHandle, mTextureHandle,
243 mCoordBuffer);
246 @Override
247 public void onSurfaceChanged(GL10 gl, final int width, final int height) {
248 GLES20.glViewport(0, 0, width, height);
252 * create a vertex shader type (GLES20.GL_VERTEX_SHADER)
253 * or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
255 public static int loadShader(int type, String shaderCode) {
256 int shader = GLES20.glCreateShader(type);
257 GLES20.glShaderSource(shader, shaderCode);
258 GLES20.glCompileShader(shader);
259 return shader;
262 class FadeRunnable implements Runnable {
263 private boolean mStarted;
264 private long mRunAt;
266 void scheduleStartFade(long delay) {
267 mRunAt = SystemClock.elapsedRealtime() + delay;
268 if (!mStarted) {
269 mView.postDelayed(this, delay);
270 mStarted = true;
274 void scheduleNextFadeFrame() {
275 if (mStarted) {
276 Log.e(LOGTAG, "scheduleNextFadeFrame() called while scheduled for starting fade");
278 mView.postDelayed(this, 1000L / 60L); // request another frame at 60fps
281 boolean timeToFade() {
282 return !mStarted;
285 public void run() {
286 long timeDelta = mRunAt - SystemClock.elapsedRealtime();
287 if (timeDelta > 0) {
288 // the run-at time was pushed back, so reschedule
289 mView.postDelayed(this, timeDelta);
290 } else {
291 // reached the run-at time, execute
292 mStarted = false;
293 mView.requestRender();
298 public class Frame {
299 // A fixed snapshot of the viewport metrics that this frame is using to render content.
300 private ImmutableViewportMetrics mFrameMetrics;
301 // A rendering context for page-positioned layers, and one for screen-positioned layers.
302 private RenderContext mPageContext, mScreenContext;
303 // Whether a layer was updated.
304 private boolean mUpdated;
305 private final Rect mPageRect;
307 public Frame(ImmutableViewportMetrics metrics) {
308 mFrameMetrics = metrics;
309 mPageContext = createPageContext(metrics);
310 mScreenContext = createScreenContext(metrics);
311 mPageRect = getPageRect();
314 private void setScissorRect() {
315 Rect scissorRect = transformToScissorRect(mPageRect);
316 GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
317 GLES20.glScissor(scissorRect.left, scissorRect.top,
318 scissorRect.width(), scissorRect.height());
321 private Rect transformToScissorRect(Rect rect) {
322 IntSize screenSize = new IntSize(mFrameMetrics.getSize());
324 int left = Math.max(0, rect.left);
325 int top = Math.max(0, rect.top);
326 int right = Math.min(screenSize.width, rect.right);
327 int bottom = Math.min(screenSize.height, rect.bottom);
329 return new Rect(left, screenSize.height - bottom, right,
330 (screenSize.height - bottom) + (bottom - top));
333 private Rect getPageRect() {
334 Point origin = PointUtils.round(mFrameMetrics.getOrigin());
335 Rect pageRect = RectUtils.round(mFrameMetrics.getPageRect());
336 pageRect.offset(-origin.x, -origin.y);
337 return pageRect;
340 public void beginDrawing() {
341 TextureReaper.get().reap();
342 TextureGenerator.get().fill();
344 mUpdated = true;
346 Layer rootLayer = mView.getLayerClient().getRoot();
347 Layer lowResLayer = mView.getLayerClient().getLowResLayer();
349 if (!mPageContext.fuzzyEquals(mLastPageContext)) {
350 // the viewport or page changed, so show the scrollbars again
351 // as per UX decision
352 mVertScrollLayer.unfade();
353 mHorizScrollLayer.unfade();
354 mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
355 } else if (mFadeRunnable.timeToFade()) {
356 boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
357 if (stillFading) {
358 mFadeRunnable.scheduleNextFadeFrame();
361 mLastPageContext = mPageContext;
363 /* Update layers. */
364 if (rootLayer != null) mUpdated &= rootLayer.update(mPageContext); // called on compositor thread
365 if (lowResLayer != null) mUpdated &= lowResLayer.update(mPageContext); // called on compositor thread
366 mUpdated &= mBackgroundLayer.update(mScreenContext); // called on compositor thread
367 mUpdated &= mShadowLayer.update(mPageContext); // called on compositor thread
368 mUpdated &= mVertScrollLayer.update(mPageContext); // called on compositor thread
369 mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread
371 for (Layer layer : mExtraLayers)
372 mUpdated &= layer.update(mPageContext); // called on compositor thread
375 public void drawBackground() {
376 GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
378 /* Update background color. */
379 final int backgroundColor = Color.WHITE;
381 /* Clear to the page background colour. The bits set here need to
382 * match up with those used in gfx/layers/opengl/LayerManagerOGL.cpp.
384 GLES20.glClearColor(((backgroundColor >> 16) & 0xFF) / 255.0f,
385 ((backgroundColor >> 8) & 0xFF) / 255.0f,
386 (backgroundColor & 0xFF) / 255.0f,
387 0.0f);
388 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT |
389 GLES20.GL_DEPTH_BUFFER_BIT);
391 /* Draw the background. */
392 mBackgroundLayer.setMask(mPageRect);
393 mBackgroundLayer.draw(mScreenContext);
395 /* Draw the drop shadow, if we need to. */
396 RectF untransformedPageRect = new RectF(0.0f, 0.0f, mPageRect.width(),
397 mPageRect.height());
398 if (!untransformedPageRect.contains(mFrameMetrics.getViewport()))
399 mShadowLayer.draw(mPageContext);
401 /* Scissor around the page-rect, in case the page has shrunk
402 * since the screenshot layer was last updated.
404 setScissorRect(); // Calls glEnable(GL_SCISSOR_TEST))
407 // Draws the layer the client added to us.
408 void drawRootLayer() {
409 Layer lowResLayer = mView.getLayerClient().getLowResLayer();
410 if (lowResLayer == null) {
411 return;
413 lowResLayer.draw(mPageContext);
415 Layer rootLayer = mView.getLayerClient().getRoot();
416 if (rootLayer == null) {
417 return;
420 rootLayer.draw(mPageContext);
423 public void drawForeground() {
424 /* Draw any extra layers that were added (likely plugins) */
425 if (mExtraLayers.size() > 0) {
426 for (Layer layer : mExtraLayers) {
427 if (!layer.usesDefaultProgram())
428 deactivateDefaultProgram();
430 layer.draw(mPageContext);
432 if (!layer.usesDefaultProgram())
433 activateDefaultProgram();
437 /* Draw the vertical scrollbar. */
438 if (mPageRect.height() > mFrameMetrics.getHeight())
439 mVertScrollLayer.draw(mPageContext);
441 /* Draw the horizontal scrollbar. */
442 if (mPageRect.width() > mFrameMetrics.getWidth())
443 mHorizScrollLayer.draw(mPageContext);
446 public void endDrawing() {
447 // If a layer update requires further work, schedule another redraw
448 if (!mUpdated)
449 mView.requestRender();