HardwareRenderer.java revision cabfcc1364eb7e4de0b15b3574fba45027b45cfc
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.graphics.Paint;
22import android.graphics.Rect;
23import android.os.SystemClock;
24import android.util.EventLog;
25import android.util.Log;
26
27import javax.microedition.khronos.egl.EGL10;
28import javax.microedition.khronos.egl.EGL11;
29import javax.microedition.khronos.egl.EGLConfig;
30import javax.microedition.khronos.egl.EGLContext;
31import javax.microedition.khronos.egl.EGLDisplay;
32import javax.microedition.khronos.egl.EGLSurface;
33import javax.microedition.khronos.opengles.GL;
34
35/**
36 * Interface for rendering a ViewRoot using hardware acceleration.
37 *
38 * @hide
39 */
40public abstract class HardwareRenderer {
41    static final String LOG_TAG = "HardwareRenderer";
42
43    /**
44     * Turn on to only refresh the parts of the screen that need updating.
45     * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY}
46     * must also have the value "true".
47     */
48    public static final boolean RENDER_DIRTY_REGIONS = true;
49
50    /**
51     * System property used to enable or disable dirty regions invalidation.
52     * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
53     * The default value of this property is assumed to be true.
54     *
55     * Possible values:
56     * "true", to enable partial invalidates
57     * "false", to disable partial invalidates
58     */
59    static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions";
60
61    /**
62     * Turn on to draw dirty regions every other frame.
63     */
64    private static final boolean DEBUG_DIRTY_REGION = false;
65
66    /**
67     * A process can set this flag to false to prevent the use of hardware
68     * rendering.
69     *
70     * @hide
71     */
72    public static boolean sRendererDisabled = false;
73
74    private boolean mEnabled;
75    private boolean mRequested = true;
76
77    /**
78     * Invoke this method to disable hardware rendering in the current process.
79     *
80     * @hide
81     */
82    public static void disable() {
83        sRendererDisabled = true;
84    }
85
86    /**
87     * Indicates whether hardware acceleration is available under any form for
88     * the view hierarchy.
89     *
90     * @return True if the view hierarchy can potentially be hardware accelerated,
91     *         false otherwise
92     */
93    public static boolean isAvailable() {
94        return GLES20Canvas.isAvailable();
95    }
96
97    /**
98     * Destroys the hardware rendering context.
99     *
100     * @param full If true, destroys all associated resources.
101     */
102    abstract void destroy(boolean full);
103
104    /**
105     * Initializes the hardware renderer for the specified surface.
106     *
107     * @param holder The holder for the surface to hardware accelerate.
108     *
109     * @return True if the initialization was successful, false otherwise.
110     */
111    abstract boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException;
112
113    /**
114     * Updates the hardware renderer for the specified surface.
115     *
116     * @param holder The holder for the surface to hardware accelerate.
117     */
118    abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException;
119
120    /**
121     * Setup the hardware renderer for drawing. This is called for every
122     * frame to draw.
123     *
124     * @param width Width of the drawing surface.
125     * @param height Height of the drawing surface.
126     */
127    abstract void setup(int width, int height);
128
129    /**
130     * Interface used to receive callbacks whenever a view is drawn by
131     * a hardware renderer instance.
132     */
133    interface HardwareDrawCallbacks {
134        /**
135         * Invoked before a view is drawn by a hardware renderer.
136         *
137         * @param canvas The Canvas used to render the view.
138         */
139        void onHardwarePreDraw(Canvas canvas);
140
141        /**
142         * Invoked after a view is drawn by a hardware renderer.
143         *
144         * @param canvas The Canvas used to render the view.
145         */
146        void onHardwarePostDraw(Canvas canvas);
147    }
148
149    /**
150     * Draws the specified view.
151     *
152     * @param view The view to draw.
153     * @param attachInfo AttachInfo tied to the specified view.
154     * @param callbacks Callbacks invoked when drawing happens.
155     * @param dirty The dirty rectangle to update, can be null.
156     */
157    abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
158            Rect dirty);
159
160    /**
161     * Creates a new display list that can be used to record batches of
162     * drawing operations.
163     *
164     * @return A new display list.
165     */
166    abstract DisplayList createDisplayList(View v);
167
168    /**
169     * Creates a new hardware layer.
170     *
171     * @param width The minimum width of the layer
172     * @param height The minimum height of the layer
173     * @param isOpaque Whether the layer should be opaque or not
174     *
175     * @return A hardware layer
176     */
177    abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
178
179    /**
180     * Initializes the hardware renderer for the specified surface and setup the
181     * renderer for drawing, if needed. This is invoked when the ViewRoot has
182     * potentially lost the hardware renderer. The hardware renderer should be
183     * reinitialized and setup when the render {@link #isRequested()} and
184     * {@link #isEnabled()}.
185     *
186     * @param width The width of the drawing surface.
187     * @param height The height of the drawing surface.
188     * @param attachInfo The
189     * @param holder
190     */
191    void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
192            SurfaceHolder holder) throws Surface.OutOfResourcesException {
193        if (isRequested()) {
194            // We lost the gl context, so recreate it.
195            if (!isEnabled()) {
196                if (initialize(holder)) {
197                    setup(width, height);
198                }
199            }
200        }
201    }
202
203    /**
204     * Creates a hardware renderer using OpenGL.
205     *
206     * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.)
207     * @param translucent True if the surface is translucent, false otherwise
208     *
209     * @return A hardware renderer backed by OpenGL.
210     */
211    static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
212        switch (glVersion) {
213            case 2:
214                return Gl20Renderer.create(translucent);
215        }
216        throw new IllegalArgumentException("Unknown GL version: " + glVersion);
217    }
218
219    /**
220     * Indicates whether hardware acceleration is currently enabled.
221     *
222     * @return True if hardware acceleration is in use, false otherwise.
223     */
224    boolean isEnabled() {
225        return mEnabled;
226    }
227
228    /**
229     * Indicates whether hardware acceleration is currently enabled.
230     *
231     * @param enabled True if the hardware renderer is in use, false otherwise.
232     */
233    void setEnabled(boolean enabled) {
234        mEnabled = enabled;
235    }
236
237    /**
238     * Indicates whether hardware acceleration is currently request but not
239     * necessarily enabled yet.
240     *
241     * @return True if requested, false otherwise.
242     */
243    boolean isRequested() {
244        return mRequested;
245    }
246
247    /**
248     * Indicates whether hardware acceleration is currently requested but not
249     * necessarily enabled yet.
250     *
251     * @return True to request hardware acceleration, false otherwise.
252     */
253    void setRequested(boolean requested) {
254        mRequested = requested;
255    }
256
257    @SuppressWarnings({"deprecation"})
258    static abstract class GlRenderer extends HardwareRenderer {
259        private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
260        private static final int EGL_SURFACE_TYPE = 0x3033;
261        private static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
262
263        private static final int SURFACE_STATE_ERROR = 0;
264        private static final int SURFACE_STATE_SUCCESS = 1;
265        private static final int SURFACE_STATE_UPDATED = 2;
266
267        static EGLContext sEglContext;
268        static EGL10 sEgl;
269        static EGLDisplay sEglDisplay;
270        static EGLConfig sEglConfig;
271
272        private static Thread sEglThread;
273
274        EGLSurface mEglSurface;
275
276        GL mGl;
277        HardwareCanvas mCanvas;
278        int mFrameCount;
279        Paint mDebugPaint;
280
281        boolean mDirtyRegions;
282
283        final int mGlVersion;
284        final boolean mTranslucent;
285
286        private boolean mDestroyed;
287
288        private final Rect mRedrawClip = new Rect();
289
290        GlRenderer(int glVersion, boolean translucent) {
291            mGlVersion = glVersion;
292            mTranslucent = translucent;
293            final String dirtyProperty = System.getProperty(RENDER_DIRTY_REGIONS_PROPERTY, "true");
294            //noinspection PointlessBooleanExpression,ConstantConditions
295            mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
296        }
297
298        /**
299         * Return a string for the EGL error code, or the hex representation
300         * if the error is unknown.
301         *
302         * @param error The EGL error to convert into a String.
303         *
304         * @return An error string correponding to the EGL error code.
305         */
306        static String getEGLErrorString(int error) {
307            switch (error) {
308                case EGL10.EGL_SUCCESS:
309                    return "EGL_SUCCESS";
310                case EGL10.EGL_NOT_INITIALIZED:
311                    return "EGL_NOT_INITIALIZED";
312                case EGL10.EGL_BAD_ACCESS:
313                    return "EGL_BAD_ACCESS";
314                case EGL10.EGL_BAD_ALLOC:
315                    return "EGL_BAD_ALLOC";
316                case EGL10.EGL_BAD_ATTRIBUTE:
317                    return "EGL_BAD_ATTRIBUTE";
318                case EGL10.EGL_BAD_CONFIG:
319                    return "EGL_BAD_CONFIG";
320                case EGL10.EGL_BAD_CONTEXT:
321                    return "EGL_BAD_CONTEXT";
322                case EGL10.EGL_BAD_CURRENT_SURFACE:
323                    return "EGL_BAD_CURRENT_SURFACE";
324                case EGL10.EGL_BAD_DISPLAY:
325                    return "EGL_BAD_DISPLAY";
326                case EGL10.EGL_BAD_MATCH:
327                    return "EGL_BAD_MATCH";
328                case EGL10.EGL_BAD_NATIVE_PIXMAP:
329                    return "EGL_BAD_NATIVE_PIXMAP";
330                case EGL10.EGL_BAD_NATIVE_WINDOW:
331                    return "EGL_BAD_NATIVE_WINDOW";
332                case EGL10.EGL_BAD_PARAMETER:
333                    return "EGL_BAD_PARAMETER";
334                case EGL10.EGL_BAD_SURFACE:
335                    return "EGL_BAD_SURFACE";
336                case EGL11.EGL_CONTEXT_LOST:
337                    return "EGL_CONTEXT_LOST";
338                default:
339                    return "0x" + Integer.toHexString(error);
340            }
341        }
342
343        /**
344         * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
345         * is invoked and the requested flag is turned off. The error code is
346         * also logged as a warning.
347         */
348        void checkEglErrors() {
349            if (isEnabled()) {
350                int error = sEgl.eglGetError();
351                if (error != EGL10.EGL_SUCCESS) {
352                    // something bad has happened revert to
353                    // normal rendering.
354                    fallback(error != EGL11.EGL_CONTEXT_LOST);
355                    Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error));
356                }
357            }
358        }
359
360        private void fallback(boolean fallback) {
361            destroy(true);
362            if (fallback) {
363                // we'll try again if it was context lost
364                setRequested(false);
365                Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
366                        + "Switching back to software rendering.");
367            }
368        }
369
370        @Override
371        boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException {
372            if (isRequested() && !isEnabled()) {
373                initializeEgl();
374                mGl = createEglSurface(holder);
375                mDestroyed = false;
376
377                if (mGl != null) {
378                    int err = sEgl.eglGetError();
379                    if (err != EGL10.EGL_SUCCESS) {
380                        destroy(true);
381                        setRequested(false);
382                    } else {
383                        if (mCanvas == null) {
384                            mCanvas = createCanvas();
385                        }
386                        if (mCanvas != null) {
387                            setEnabled(true);
388                        } else {
389                            Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
390                        }
391                    }
392
393                    return mCanvas != null;
394                }
395            }
396            return false;
397        }
398
399        @Override
400        void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
401            if (isRequested() && isEnabled()) {
402                createEglSurface(holder);
403            }
404        }
405
406        abstract GLES20Canvas createCanvas();
407
408        void initializeEgl() {
409            if (sEglContext != null) return;
410
411            sEglThread = Thread.currentThread();
412            sEgl = (EGL10) EGLContext.getEGL();
413
414            // Get to the default display.
415            sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
416
417            if (sEglDisplay == EGL10.EGL_NO_DISPLAY) {
418                throw new RuntimeException("eglGetDisplay failed "
419                        + getEGLErrorString(sEgl.eglGetError()));
420            }
421
422            // We can now initialize EGL for that display
423            int[] version = new int[2];
424            if (!sEgl.eglInitialize(sEglDisplay, version)) {
425                throw new RuntimeException("eglInitialize failed " +
426                        getEGLErrorString(sEgl.eglGetError()));
427            }
428
429            sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
430            if (sEglConfig == null) {
431                // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
432                if (mDirtyRegions) {
433                    mDirtyRegions = false;
434
435                    sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
436                    if (sEglConfig == null) {
437                        throw new RuntimeException("eglConfig not initialized");
438                    }
439                } else {
440                    throw new RuntimeException("eglConfig not initialized");
441                }
442            }
443
444            /*
445            * Create an EGL context. We want to do this as rarely as we can, because an
446            * EGL context is a somewhat heavy object.
447            */
448            sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
449        }
450
451        GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
452            // Check preconditions.
453            if (sEgl == null) {
454                throw new RuntimeException("egl not initialized");
455            }
456            if (sEglDisplay == null) {
457                throw new RuntimeException("eglDisplay not initialized");
458            }
459            if (sEglConfig == null) {
460                throw new RuntimeException("eglConfig not initialized");
461            }
462            if (Thread.currentThread() != sEglThread) {
463                throw new IllegalStateException("HardwareRenderer cannot be used "
464                        + "from multiple threads");
465            }
466
467            /*
468             *  The window size has changed, so we need to create a new
469             *  surface.
470             */
471            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
472                /*
473                 * Unbind and destroy the old EGL surface, if
474                 * there is one.
475                 */
476                sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
477                        EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
478                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
479            }
480
481            // Create an EGL surface we can render into.
482            mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
483
484            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
485                int error = sEgl.eglGetError();
486                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
487                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
488                    return null;
489                }
490                throw new RuntimeException("createWindowSurface failed "
491                        + getEGLErrorString(error));
492            }
493
494            /*
495             * Before we can issue GL commands, we need to make sure
496             * the context is current and bound to a surface.
497             */
498            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
499                throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
500                        + getEGLErrorString(sEgl.eglGetError()));
501            }
502
503            if (mDirtyRegions) {
504                if (!GLES20Canvas.preserveBackBuffer()) {
505                    Log.w(LOG_TAG, "Backbuffer cannot be preserved");
506                }
507            }
508
509            return sEglContext.getGL();
510        }
511
512        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
513            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL10.EGL_NONE };
514
515            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT,
516                    mGlVersion != 0 ? attrib_list : null);
517        }
518
519        @Override
520        void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
521                SurfaceHolder holder) throws Surface.OutOfResourcesException {
522            if (isRequested()) {
523                checkEglErrors();
524                super.initializeIfNeeded(width, height, attachInfo, holder);
525            }
526        }
527
528        @Override
529        void destroy(boolean full) {
530            if (full && mCanvas != null) {
531                mCanvas = null;
532            }
533
534            if (!isEnabled() || mDestroyed) return;
535
536            mDestroyed = true;
537
538            sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
539                    EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
540            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
541
542            mEglSurface = null;
543            mGl = null;
544
545            setEnabled(false);
546        }
547
548        @Override
549        void setup(int width, int height) {
550            mCanvas.setViewport(width, height);
551        }
552
553        boolean canDraw() {
554            return mGl != null && mCanvas != null;
555        }
556
557        void onPreDraw(Rect dirty) {
558        }
559
560        void onPostDraw() {
561        }
562
563        /**
564         * Defines the EGL configuration for this renderer.
565         *
566         * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}.
567         */
568        EglConfigChooser getConfigChooser(int glVersion) {
569            return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0, mDirtyRegions);
570        }
571
572        @Override
573        void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
574                Rect dirty) {
575            if (canDraw()) {
576                if (!mDirtyRegions) {
577                    dirty = null;
578                }
579
580                attachInfo.mDrawingTime = SystemClock.uptimeMillis();
581                attachInfo.mIgnoreDirtyState = true;
582                view.mPrivateFlags |= View.DRAWN;
583
584                long startTime;
585                if (ViewDebug.DEBUG_PROFILE_DRAWING) {
586                    startTime = SystemClock.elapsedRealtime();
587                }
588
589                final int surfaceState = checkCurrent();
590                if (surfaceState != SURFACE_STATE_ERROR) {
591                    // We had to change the current surface and/or context, redraw everything
592                    if (surfaceState == SURFACE_STATE_UPDATED) {
593                        dirty = null;
594                    }
595
596                    onPreDraw(dirty);
597
598                    HardwareCanvas canvas = mCanvas;
599                    attachInfo.mHardwareCanvas = canvas;
600
601                    int saveCount = canvas.save();
602                    callbacks.onHardwarePreDraw(canvas);
603
604                    try {
605                        view.mRecreateDisplayList =
606                                (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED;
607                        view.mPrivateFlags &= ~View.INVALIDATED;
608
609                        DisplayList displayList = view.getDisplayList();
610                        if (displayList != null) {
611                            if (canvas.drawDisplayList(displayList, mRedrawClip)) {
612                                if (mRedrawClip.isEmpty()) {
613                                    view.invalidate();
614                                } else {
615                                    view.getParent().invalidateChild(view, mRedrawClip);
616                                }
617                                mRedrawClip.setEmpty();
618                            }
619                        } else {
620                            // Shouldn't reach here
621                            view.draw(canvas);
622                        }
623
624                        if (DEBUG_DIRTY_REGION) {
625                            if (mDebugPaint == null) {
626                                mDebugPaint = new Paint();
627                                mDebugPaint.setColor(0x7fff0000);
628                            }
629                            if (dirty != null && (mFrameCount++ & 1) == 0) {
630                                canvas.drawRect(dirty, mDebugPaint);
631                            }
632                        }
633                    } finally {
634                        callbacks.onHardwarePostDraw(canvas);
635                        canvas.restoreToCount(saveCount);
636                        view.mRecreateDisplayList = false;
637                    }
638
639                    onPostDraw();
640
641                    if (ViewDebug.DEBUG_PROFILE_DRAWING) {
642                        EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
643                    }
644
645                    attachInfo.mIgnoreDirtyState = false;
646
647                    sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
648                    checkEglErrors();
649                }
650            }
651        }
652
653        private int checkCurrent() {
654            // TODO: Don't check the current context when we have one per UI thread
655            // TODO: Use a threadlocal flag to know whether the surface has changed
656            if (!sEglContext.equals(sEgl.eglGetCurrentContext()) ||
657                    !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
658                if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
659                    fallback(true);
660                    Log.e(LOG_TAG, "eglMakeCurrent failed " +
661                            getEGLErrorString(sEgl.eglGetError()));
662                    return SURFACE_STATE_ERROR;
663                } else {
664                    return SURFACE_STATE_UPDATED;
665                }
666            }
667            return SURFACE_STATE_SUCCESS;
668        }
669
670        static abstract class EglConfigChooser {
671            final int[] mConfigSpec;
672            private final int mGlVersion;
673
674            EglConfigChooser(int glVersion, int[] configSpec) {
675                mGlVersion = glVersion;
676                mConfigSpec = filterConfigSpec(configSpec);
677            }
678
679            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
680                int[] index = new int[1];
681                if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) {
682                    throw new IllegalArgumentException("eglChooseConfig failed "
683                            + getEGLErrorString(egl.eglGetError()));
684                }
685
686                int numConfigs = index[0];
687                if (numConfigs <= 0) {
688                    throw new IllegalArgumentException("No configs match configSpec");
689                }
690
691                EGLConfig[] configs = new EGLConfig[numConfigs];
692                if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) {
693                    throw new IllegalArgumentException("eglChooseConfig failed "
694                            + getEGLErrorString(egl.eglGetError()));
695                }
696
697                EGLConfig config = chooseConfig(egl, display, configs);
698                if (config == null) {
699                    throw new IllegalArgumentException("No config chosen");
700                }
701
702                return config;
703            }
704
705            abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
706
707            private int[] filterConfigSpec(int[] configSpec) {
708                if (mGlVersion != 2) {
709                    return configSpec;
710                }
711                /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
712                 * And we know the configSpec is well formed.
713                 */
714                int len = configSpec.length;
715                int[] newConfigSpec = new int[len + 2];
716                System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
717                newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
718                newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
719                newConfigSpec[len + 1] = EGL10.EGL_NONE;
720                return newConfigSpec;
721            }
722        }
723
724        /**
725         * Choose a configuration with exactly the specified r,g,b,a sizes,
726         * and at least the specified depth and stencil sizes.
727         */
728        static class ComponentSizeChooser extends EglConfigChooser {
729            private int[] mValue;
730
731            private final int mRedSize;
732            private final int mGreenSize;
733            private final int mBlueSize;
734            private final int mAlphaSize;
735            private final int mDepthSize;
736            private final int mStencilSize;
737            private final boolean mDirtyRegions;
738
739            ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize,
740                    int alphaSize, int depthSize, int stencilSize, boolean dirtyRegions) {
741                super(glVersion, new int[] {
742                        EGL10.EGL_RED_SIZE, redSize,
743                        EGL10.EGL_GREEN_SIZE, greenSize,
744                        EGL10.EGL_BLUE_SIZE, blueSize,
745                        EGL10.EGL_ALPHA_SIZE, alphaSize,
746                        EGL10.EGL_DEPTH_SIZE, depthSize,
747                        EGL10.EGL_STENCIL_SIZE, stencilSize,
748                        EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT |
749                                (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
750                        EGL10.EGL_NONE });
751                mValue = new int[1];
752                mRedSize = redSize;
753                mGreenSize = greenSize;
754                mBlueSize = blueSize;
755                mAlphaSize = alphaSize;
756                mDepthSize = depthSize;
757                mStencilSize = stencilSize;
758                mDirtyRegions = dirtyRegions;
759            }
760
761            @Override
762            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
763                for (EGLConfig config : configs) {
764                    int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
765                    int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
766                    if (d >= mDepthSize && s >= mStencilSize) {
767                        int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
768                        int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
769                        int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
770                        int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
771                        boolean backBuffer;
772                        if (mDirtyRegions) {
773                            int surfaceType = findConfigAttrib(egl, display, config,
774                                    EGL_SURFACE_TYPE, 0);
775                            backBuffer = (surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) != 0;
776                        } else {
777                            backBuffer = true;
778                        }
779                        if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize
780                                && backBuffer) {
781                            return config;
782                        }
783                    }
784                }
785                return null;
786            }
787
788            private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
789                    int attribute, int defaultValue) {
790                if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
791                    return mValue[0];
792                }
793
794                return defaultValue;
795            }
796        }
797    }
798
799    /**
800     * Hardware renderer using OpenGL ES 2.0.
801     */
802    static class Gl20Renderer extends GlRenderer {
803        private GLES20Canvas mGlCanvas;
804
805        Gl20Renderer(boolean translucent) {
806            super(2, translucent);
807        }
808
809        @Override
810        GLES20Canvas createCanvas() {
811            return mGlCanvas = new GLES20Canvas(mTranslucent);
812        }
813
814        @Override
815        boolean canDraw() {
816            return super.canDraw() && mGlCanvas != null;
817        }
818
819        @Override
820        void onPreDraw(Rect dirty) {
821            mGlCanvas.onPreDraw(dirty);
822        }
823
824        @Override
825        void onPostDraw() {
826            mGlCanvas.onPostDraw();
827        }
828
829        @Override
830        void destroy(boolean full) {
831            try {
832                super.destroy(full);
833            } finally {
834                if (full && mGlCanvas != null) {
835                    mGlCanvas = null;
836                }
837            }
838        }
839
840        @Override
841        DisplayList createDisplayList(View v) {
842            return new GLES20DisplayList(v);
843        }
844
845        @Override
846        HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
847            return new GLES20Layer(width, height, isOpaque);
848        }
849
850        static HardwareRenderer create(boolean translucent) {
851            if (GLES20Canvas.isAvailable()) {
852                return new Gl20Renderer(translucent);
853            }
854            return null;
855        }
856    }
857}
858