HardwareRenderer.java revision dbd77cd444f89d94ec5333223c1bc17dbe0c90cd
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package android.view;
19
20import android.graphics.Canvas;
21import android.os.SystemClock;
22import android.util.Log;
23
24import javax.microedition.khronos.egl.EGL10;
25import javax.microedition.khronos.egl.EGL11;
26import javax.microedition.khronos.egl.EGLConfig;
27import javax.microedition.khronos.egl.EGLContext;
28import javax.microedition.khronos.egl.EGLDisplay;
29import javax.microedition.khronos.egl.EGLSurface;
30import javax.microedition.khronos.opengles.GL;
31import javax.microedition.khronos.opengles.GL11;
32
33import static javax.microedition.khronos.opengles.GL10.GL_COLOR_BUFFER_BIT;
34import static javax.microedition.khronos.opengles.GL10.GL_SCISSOR_TEST;
35
36/**
37 * Interface for rendering a ViewRoot using hardware acceleration.
38 *
39 * @hide
40 */
41abstract class HardwareRenderer {
42    private boolean mEnabled;
43    private boolean mRequested = true;
44    private static final String LOG_TAG = "HardwareRenderer";
45
46    /**
47     * Destroys the hardware rendering context.
48     */
49    abstract void destroy();
50
51    /**
52     * Initializes the hardware renderer for the specified surface.
53     *
54     * @param holder The holder for the surface to hardware accelerate.
55     *
56     * @return True if the initialization was successful, false otherwise.
57     */
58    abstract boolean initialize(SurfaceHolder holder);
59
60    /**
61     * Setup the hardware renderer for drawing. This is called for every
62     * frame to draw.
63     *
64     * @param width Width of the drawing surface.
65     * @param height Height of the drawing surface.
66     * @param attachInfo The AttachInfo used to render the ViewRoot.
67     */
68    abstract void setup(int width, int height, View.AttachInfo attachInfo);
69
70    /**
71     * Draws the specified view.
72     *
73     * @param view The view to draw.
74     * @param attachInfo AttachInfo tied to the specified view.
75     */
76    abstract void draw(View view, View.AttachInfo attachInfo, int yOffset);
77
78    /**
79     * Initializes the hardware renderer for the specified surface and setup the
80     * renderer for drawing, if needed. This is invoked when the ViewRoot has
81     * potentially lost the hardware renderer. The hardware renderer should be
82     * reinitialized and setup when the render {@link #isRequested()} and
83     * {@link #isEnabled()}.
84     *
85     * @param width The width of the drawing surface.
86     * @param height The height of the drawing surface.
87     * @param attachInfo The
88     * @param holder
89     */
90    void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
91            SurfaceHolder holder) {
92
93        if (isRequested()) {
94            // We lost the gl context, so recreate it.
95            if (!isEnabled()) {
96                if (initialize(holder)) {
97                    setup(width, height, attachInfo);
98                }
99            }
100        }
101    }
102
103    /**
104     * Creates a hardware renderer using OpenGL.
105     *
106     * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.)
107     * @param translucent True if the surface is translucent, false otherwise
108     *
109     * @return A hardware renderer backed by OpenGL.
110     */
111    static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
112        switch (glVersion) {
113            case 1:
114                return new Gl10Renderer(translucent);
115            case 2:
116                return new Gl20Renderer(translucent);
117        }
118        throw new IllegalArgumentException("Unknown GL version: " + glVersion);
119    }
120
121    /**
122     * Indicates whether hardware acceleration is currently enabled.
123     *
124     * @return True if hardware acceleration is in use, false otherwise.
125     */
126    boolean isEnabled() {
127        return mEnabled;
128    }
129
130    /**
131     * Indicates whether hardware acceleration is currently enabled.
132     *
133     * @param enabled True if the hardware renderer is in use, false otherwise.
134     */
135    void setEnabled(boolean enabled) {
136        mEnabled = enabled;
137    }
138
139    /**
140     * Indicates whether hardware acceleration is currently request but not
141     * necessarily enabled yet.
142     *
143     * @return True if requested, false otherwise.
144     */
145    boolean isRequested() {
146        return mRequested;
147    }
148
149    /**
150     * Indicates whether hardware acceleration is currently request but not
151     * necessarily enabled yet.
152     *
153     * @return True to request hardware acceleration, false otherwise.
154     */
155    void setRequested(boolean requested) {
156        mRequested = requested;
157    }
158
159    @SuppressWarnings({"deprecation"})
160    static abstract class GlRenderer extends HardwareRenderer {
161        private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
162
163        EGL10 mEgl;
164        EGLDisplay mEglDisplay;
165        EGLContext mEglContext;
166        EGLSurface mEglSurface;
167        EGLConfig mEglConfig;
168
169        GL mGl;
170        Canvas mCanvas;
171
172        final int mGlVersion;
173        final boolean mTranslucent;
174
175        GlRenderer(int glVersion, boolean translucent) {
176            mGlVersion = glVersion;
177            mTranslucent = translucent;
178        }
179
180        /**
181         * Checks for OpenGL errors. If an error has occured, {@link #destroy()}
182         * is invoked and the requested flag is turned off. The error code is
183         * also logged as a warning.
184         */
185        void checkErrors() {
186            if (isEnabled()) {
187                int error = mEgl.eglGetError();
188                if (error != EGL10.EGL_SUCCESS) {
189                    // something bad has happened revert to
190                    // normal rendering.
191                    destroy();
192                    if (error != EGL11.EGL_CONTEXT_LOST) {
193                        // we'll try again if it was context lost
194                        setRequested(false);
195                    }
196                    Log.w(LOG_TAG, "OpenGL error: " + error);
197                }
198            }
199        }
200
201        @Override
202        boolean initialize(SurfaceHolder holder) {
203            if (isRequested() && !isEnabled()) {
204                initializeEgl();
205                mGl = createEglSurface(holder);
206
207                if (mGl != null) {
208                    int err = mEgl.eglGetError();
209                    if (err != EGL10.EGL_SUCCESS) {
210                        destroy();
211                        setRequested(false);
212                    } else {
213                        mCanvas = createCanvas();
214                        if (mCanvas != null) {
215                            setEnabled(true);
216                        } else {
217                            Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
218                        }
219                    }
220
221                    return mCanvas != null;
222                }
223            }
224            return false;
225        }
226
227        abstract Canvas createCanvas();
228
229        void initializeEgl() {
230            mEgl = (EGL10) EGLContext.getEGL();
231
232            // Get to the default display.
233            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
234
235            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
236                throw new RuntimeException("eglGetDisplay failed");
237            }
238
239            // We can now initialize EGL for that display
240            int[] version = new int[2];
241            if (!mEgl.eglInitialize(mEglDisplay, version)) {
242                throw new RuntimeException("eglInitialize failed");
243            }
244            mEglConfig = getConfigChooser(mGlVersion).chooseConfig(mEgl, mEglDisplay);
245
246            /*
247            * Create an EGL context. We want to do this as rarely as we can, because an
248            * EGL context is a somewhat heavy object.
249            */
250            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
251        }
252
253        GL createEglSurface(SurfaceHolder holder) {
254            // Check preconditions.
255            if (mEgl == null) {
256                throw new RuntimeException("egl not initialized");
257            }
258            if (mEglDisplay == null) {
259                throw new RuntimeException("eglDisplay not initialized");
260            }
261            if (mEglConfig == null) {
262                throw new RuntimeException("mEglConfig not initialized");
263            }
264
265            /*
266             *  The window size has changed, so we need to create a new
267             *  surface.
268             */
269            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
270
271                /*
272                 * Unbind and destroy the old EGL surface, if
273                 * there is one.
274                 */
275                mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
276                        EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
277                mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
278            }
279
280            // Create an EGL surface we can render into.
281            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, null);
282
283            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
284                int error = mEgl.eglGetError();
285                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
286                    Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
287                    return null;
288                }
289                throw new RuntimeException("createWindowSurface failed");
290            }
291
292            /*
293             * Before we can issue GL commands, we need to make sure
294             * the context is current and bound to a surface.
295             */
296            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
297                throw new RuntimeException("eglMakeCurrent failed");
298
299            }
300
301            return mEglContext.getGL();
302        }
303
304        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
305            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL10.EGL_NONE };
306
307            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT,
308                    mGlVersion != 0 ? attrib_list : null);
309        }
310
311        @Override
312        void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
313                SurfaceHolder holder) {
314
315            if (isRequested()) {
316                checkErrors();
317                super.initializeIfNeeded(width, height, attachInfo, holder);
318            }
319        }
320
321        @Override
322        void destroy() {
323            if (!isEnabled()) return;
324
325            mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
326                    EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
327            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
328            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
329            mEgl.eglTerminate(mEglDisplay);
330
331            mEglContext = null;
332            mEglSurface = null;
333            mEglDisplay = null;
334            mEgl = null;
335            mGl = null;
336            mCanvas = null;
337
338            setEnabled(false);
339        }
340
341        @Override
342        void setup(int width, int height, View.AttachInfo attachInfo) {
343            final float scale = attachInfo.mApplicationScale;
344            mCanvas.setViewport((int) (width * scale + 0.5f), (int) (height * scale + 0.5f));
345        }
346
347        boolean canDraw() {
348            return mGl != null && mCanvas != null;
349        }
350
351        void onPreDraw() {
352        }
353
354        /**
355         * Defines the EGL configuration for this renderer. The default configuration
356         * is RGBX, no depth, no stencil.
357         *
358         * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}.
359         * @param glVersion
360         */
361        EglConfigChooser getConfigChooser(int glVersion) {
362            return new ComponentSizeChooser(glVersion, 8, 8, 8, mTranslucent ? 8 : 0, 0, 0);
363        }
364
365        @Override
366        void draw(View view, View.AttachInfo attachInfo, int yOffset) {
367            if (canDraw()) {
368                attachInfo.mDrawingTime = SystemClock.uptimeMillis();
369                attachInfo.mIgnoreDirtyState = true;
370                view.mPrivateFlags |= View.DRAWN;
371
372                onPreDraw();
373
374                Canvas canvas = mCanvas;
375                int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
376                canvas.translate(0, -yOffset);
377
378                try {
379                    view.draw(canvas);
380                } finally {
381                    canvas.restoreToCount(saveCount);
382                }
383
384                attachInfo.mIgnoreDirtyState = false;
385
386                mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
387                checkErrors();
388            }
389        }
390
391        static abstract class EglConfigChooser {
392            final int[] mConfigSpec;
393            private final int mGlVersion;
394
395            EglConfigChooser(int glVersion, int[] configSpec) {
396                mGlVersion = glVersion;
397                mConfigSpec = filterConfigSpec(configSpec);
398            }
399
400            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
401                int[] index = new int[1];
402                if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) {
403                    throw new IllegalArgumentException("eglChooseConfig failed");
404                }
405
406                int numConfigs = index[0];
407                if (numConfigs <= 0) {
408                    throw new IllegalArgumentException("No configs match configSpec");
409                }
410
411                EGLConfig[] configs = new EGLConfig[numConfigs];
412                if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) {
413                    throw new IllegalArgumentException("eglChooseConfig failed");
414                }
415
416                EGLConfig config = chooseConfig(egl, display, configs);
417                if (config == null) {
418                    throw new IllegalArgumentException("No config chosen");
419                }
420
421                return config;
422            }
423
424            abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
425
426            private int[] filterConfigSpec(int[] configSpec) {
427                if (mGlVersion != 2) {
428                    return configSpec;
429                }
430                /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
431                 * And we know the configSpec is well formed.
432                 */
433                int len = configSpec.length;
434                int[] newConfigSpec = new int[len + 2];
435                System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
436                newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
437                newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
438                newConfigSpec[len + 1] = EGL10.EGL_NONE;
439                return newConfigSpec;
440            }
441        }
442
443        /**
444         * Choose a configuration with exactly the specified r,g,b,a sizes,
445         * and at least the specified depth and stencil sizes.
446         */
447        static class ComponentSizeChooser extends EglConfigChooser {
448            private int[] mValue;
449
450            private int mRedSize;
451            private int mGreenSize;
452            private int mBlueSize;
453            private int mAlphaSize;
454            private int mDepthSize;
455            private int mStencilSize;
456
457            ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize,
458                    int alphaSize, int depthSize, int stencilSize) {
459                super(glVersion, new int[] {
460                        EGL10.EGL_RED_SIZE, redSize,
461                        EGL10.EGL_GREEN_SIZE, greenSize,
462                        EGL10.EGL_BLUE_SIZE, blueSize,
463                        EGL10.EGL_ALPHA_SIZE, alphaSize,
464                        EGL10.EGL_DEPTH_SIZE, depthSize,
465                        EGL10.EGL_STENCIL_SIZE, stencilSize,
466                        EGL10.EGL_NONE });
467                mValue = new int[1];
468                mRedSize = redSize;
469                mGreenSize = greenSize;
470                mBlueSize = blueSize;
471                mAlphaSize = alphaSize;
472                mDepthSize = depthSize;
473                mStencilSize = stencilSize;
474           }
475
476            @Override
477            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
478                for (EGLConfig config : configs) {
479                    int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
480                    int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
481                    if (d >= mDepthSize && s >= mStencilSize) {
482                        int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
483                        int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
484                        int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
485                        int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
486                        if (r == mRedSize && g == mGreenSize && b == mBlueSize && a >= mAlphaSize) {
487                            return config;
488                        }
489                    }
490                }
491                return null;
492            }
493
494            private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
495                    int attribute, int defaultValue) {
496                if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
497                    return mValue[0];
498                }
499
500                return defaultValue;
501            }
502        }
503    }
504
505    /**
506     * Hardware renderer using OpenGL ES 2.0.
507     */
508    static class Gl20Renderer extends GlRenderer {
509        private GLES20Canvas mGlCanvas;
510
511        Gl20Renderer(boolean translucent) {
512            super(2, translucent);
513        }
514
515        @Override
516        Canvas createCanvas() {
517            return mGlCanvas = new GLES20Canvas(mGl, mTranslucent);
518        }
519
520        @Override
521        void onPreDraw() {
522            mGlCanvas.onPreDraw();
523        }
524    }
525
526    /**
527     * Hardware renderer using OpenGL ES 1.0.
528     */
529    @SuppressWarnings({"deprecation"})
530    static class Gl10Renderer extends GlRenderer {
531        Gl10Renderer(boolean translucent) {
532            super(1, translucent);
533        }
534
535        @Override
536        Canvas createCanvas() {
537            return new Canvas(mGl);
538        }
539
540        @Override
541        void destroy() {
542            if (isEnabled()) {
543                nativeAbandonGlCaches();
544            }
545
546            super.destroy();
547        }
548
549        @Override
550        void onPreDraw() {
551            GL11 gl = (GL11) mGl;
552            gl.glDisable(GL_SCISSOR_TEST);
553            gl.glClearColor(0, 0, 0, 0);
554            gl.glClear(GL_COLOR_BUFFER_BIT);
555            gl.glEnable(GL_SCISSOR_TEST);
556        }
557    }
558
559    // Inform Skia to just abandon its texture cache IDs doesn't call glDeleteTextures
560    // Used only by the native Skia OpenGL ES 1.x implementation
561    private static native void nativeAbandonGlCaches();
562}
563