HardwareRenderer.java revision 648251710162cdaf7371012a1cbb79b9bc5bc0e4
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        GlRenderer(int glVersion, boolean translucent) {
289            mGlVersion = glVersion;
290            mTranslucent = translucent;
291            final String dirtyProperty = System.getProperty(RENDER_DIRTY_REGIONS_PROPERTY, "true");
292            //noinspection PointlessBooleanExpression,ConstantConditions
293            mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
294        }
295
296        /**
297         * Return a string for the EGL error code, or the hex representation
298         * if the error is unknown.
299         *
300         * @param error The EGL error to convert into a String.
301         *
302         * @return An error string correponding to the EGL error code.
303         */
304        static String getEGLErrorString(int error) {
305            switch (error) {
306                case EGL10.EGL_SUCCESS:
307                    return "EGL_SUCCESS";
308                case EGL10.EGL_NOT_INITIALIZED:
309                    return "EGL_NOT_INITIALIZED";
310                case EGL10.EGL_BAD_ACCESS:
311                    return "EGL_BAD_ACCESS";
312                case EGL10.EGL_BAD_ALLOC:
313                    return "EGL_BAD_ALLOC";
314                case EGL10.EGL_BAD_ATTRIBUTE:
315                    return "EGL_BAD_ATTRIBUTE";
316                case EGL10.EGL_BAD_CONFIG:
317                    return "EGL_BAD_CONFIG";
318                case EGL10.EGL_BAD_CONTEXT:
319                    return "EGL_BAD_CONTEXT";
320                case EGL10.EGL_BAD_CURRENT_SURFACE:
321                    return "EGL_BAD_CURRENT_SURFACE";
322                case EGL10.EGL_BAD_DISPLAY:
323                    return "EGL_BAD_DISPLAY";
324                case EGL10.EGL_BAD_MATCH:
325                    return "EGL_BAD_MATCH";
326                case EGL10.EGL_BAD_NATIVE_PIXMAP:
327                    return "EGL_BAD_NATIVE_PIXMAP";
328                case EGL10.EGL_BAD_NATIVE_WINDOW:
329                    return "EGL_BAD_NATIVE_WINDOW";
330                case EGL10.EGL_BAD_PARAMETER:
331                    return "EGL_BAD_PARAMETER";
332                case EGL10.EGL_BAD_SURFACE:
333                    return "EGL_BAD_SURFACE";
334                case EGL11.EGL_CONTEXT_LOST:
335                    return "EGL_CONTEXT_LOST";
336                default:
337                    return "0x" + Integer.toHexString(error);
338            }
339        }
340
341        /**
342         * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
343         * is invoked and the requested flag is turned off. The error code is
344         * also logged as a warning.
345         */
346        void checkEglErrors() {
347            if (isEnabled()) {
348                int error = sEgl.eglGetError();
349                if (error != EGL10.EGL_SUCCESS) {
350                    // something bad has happened revert to
351                    // normal rendering.
352                    fallback(error != EGL11.EGL_CONTEXT_LOST);
353                    Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error));
354                }
355            }
356        }
357
358        private void fallback(boolean fallback) {
359            destroy(true);
360            if (fallback) {
361                // we'll try again if it was context lost
362                setRequested(false);
363                Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
364                        + "Switching back to software rendering.");
365            }
366        }
367
368        @Override
369        boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException {
370            if (isRequested() && !isEnabled()) {
371                initializeEgl();
372                mGl = createEglSurface(holder);
373                mDestroyed = false;
374
375                if (mGl != null) {
376                    int err = sEgl.eglGetError();
377                    if (err != EGL10.EGL_SUCCESS) {
378                        destroy(true);
379                        setRequested(false);
380                    } else {
381                        if (mCanvas == null) {
382                            mCanvas = createCanvas();
383                        }
384                        if (mCanvas != null) {
385                            setEnabled(true);
386                        } else {
387                            Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
388                        }
389                    }
390
391                    return mCanvas != null;
392                }
393            }
394            return false;
395        }
396
397        @Override
398        void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
399            if (isRequested() && isEnabled()) {
400                createEglSurface(holder);
401            }
402        }
403
404        abstract GLES20Canvas createCanvas();
405
406        void initializeEgl() {
407            if (sEglContext != null) return;
408
409            sEglThread = Thread.currentThread();
410            sEgl = (EGL10) EGLContext.getEGL();
411
412            // Get to the default display.
413            sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
414
415            if (sEglDisplay == EGL10.EGL_NO_DISPLAY) {
416                throw new RuntimeException("eglGetDisplay failed "
417                        + getEGLErrorString(sEgl.eglGetError()));
418            }
419
420            // We can now initialize EGL for that display
421            int[] version = new int[2];
422            if (!sEgl.eglInitialize(sEglDisplay, version)) {
423                throw new RuntimeException("eglInitialize failed " +
424                        getEGLErrorString(sEgl.eglGetError()));
425            }
426
427            sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
428            if (sEglConfig == null) {
429                // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
430                if (mDirtyRegions) {
431                    mDirtyRegions = false;
432
433                    sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
434                    if (sEglConfig == null) {
435                        throw new RuntimeException("eglConfig not initialized");
436                    }
437                } else {
438                    throw new RuntimeException("eglConfig not initialized");
439                }
440            }
441
442            /*
443            * Create an EGL context. We want to do this as rarely as we can, because an
444            * EGL context is a somewhat heavy object.
445            */
446            sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
447        }
448
449        GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
450            // Check preconditions.
451            if (sEgl == null) {
452                throw new RuntimeException("egl not initialized");
453            }
454            if (sEglDisplay == null) {
455                throw new RuntimeException("eglDisplay not initialized");
456            }
457            if (sEglConfig == null) {
458                throw new RuntimeException("eglConfig not initialized");
459            }
460            if (Thread.currentThread() != sEglThread) {
461                throw new IllegalStateException("HardwareRenderer cannot be used "
462                        + "from multiple threads");
463            }
464
465            /*
466             *  The window size has changed, so we need to create a new
467             *  surface.
468             */
469            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
470                /*
471                 * Unbind and destroy the old EGL surface, if
472                 * there is one.
473                 */
474                sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
475                        EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
476                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
477            }
478
479            // Create an EGL surface we can render into.
480            mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
481
482            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
483                int error = sEgl.eglGetError();
484                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
485                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
486                    return null;
487                }
488                throw new RuntimeException("createWindowSurface failed "
489                        + getEGLErrorString(error));
490            }
491
492            /*
493             * Before we can issue GL commands, we need to make sure
494             * the context is current and bound to a surface.
495             */
496            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
497                throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
498                        + getEGLErrorString(sEgl.eglGetError()));
499            }
500
501            if (mDirtyRegions) {
502                if (!GLES20Canvas.preserveBackBuffer()) {
503                    Log.w(LOG_TAG, "Backbuffer cannot be preserved");
504                }
505            }
506
507            return sEglContext.getGL();
508        }
509
510        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
511            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL10.EGL_NONE };
512
513            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT,
514                    mGlVersion != 0 ? attrib_list : null);
515        }
516
517        @Override
518        void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
519                SurfaceHolder holder) throws Surface.OutOfResourcesException {
520            if (isRequested()) {
521                checkEglErrors();
522                super.initializeIfNeeded(width, height, attachInfo, holder);
523            }
524        }
525
526        @Override
527        void destroy(boolean full) {
528            if (full && mCanvas != null) {
529                mCanvas = null;
530            }
531
532            if (!isEnabled() || mDestroyed) return;
533
534            mDestroyed = true;
535
536            sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
537                    EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
538            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
539
540            mEglSurface = null;
541            mGl = null;
542
543            setEnabled(false);
544        }
545
546        @Override
547        void setup(int width, int height) {
548            mCanvas.setViewport(width, height);
549        }
550
551        boolean canDraw() {
552            return mGl != null && mCanvas != null;
553        }
554
555        void onPreDraw(Rect dirty) {
556        }
557
558        void onPostDraw() {
559        }
560
561        /**
562         * Defines the EGL configuration for this renderer.
563         *
564         * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}.
565         */
566        EglConfigChooser getConfigChooser(int glVersion) {
567            return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0, mDirtyRegions);
568        }
569
570        @Override
571        void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
572                Rect dirty) {
573            if (canDraw()) {
574                if (!mDirtyRegions) {
575                    dirty = null;
576                }
577
578                attachInfo.mDrawingTime = SystemClock.uptimeMillis();
579                attachInfo.mIgnoreDirtyState = true;
580                view.mPrivateFlags |= View.DRAWN;
581
582                long startTime;
583                if (ViewDebug.DEBUG_PROFILE_DRAWING) {
584                    startTime = SystemClock.elapsedRealtime();
585                }
586
587                final int surfaceState = checkCurrent();
588                if (surfaceState != SURFACE_STATE_ERROR) {
589                    // We had to change the current surface and/or context, redraw everything
590                    if (surfaceState == SURFACE_STATE_UPDATED) {
591                        dirty = null;
592                    }
593
594                    onPreDraw(dirty);
595
596                    HardwareCanvas canvas = mCanvas;
597                    attachInfo.mHardwareCanvas = canvas;
598
599                    int saveCount = canvas.save();
600                    callbacks.onHardwarePreDraw(canvas);
601
602                    try {
603                        view.mRecreateDisplayList =
604                                (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED;
605                        view.mPrivateFlags &= ~View.INVALIDATED;
606
607                        DisplayList displayList = view.getDisplayList();
608                        if (displayList != null) {
609                            if (canvas.drawDisplayList(displayList)) {
610                                view.invalidate();
611                            }
612                        } else {
613                            // Shouldn't reach here
614                            view.draw(canvas);
615                        }
616
617                        if (DEBUG_DIRTY_REGION) {
618                            if (mDebugPaint == null) {
619                                mDebugPaint = new Paint();
620                                mDebugPaint.setColor(0x7fff0000);
621                            }
622                            if (dirty != null && (mFrameCount++ & 1) == 0) {
623                                canvas.drawRect(dirty, mDebugPaint);
624                            }
625                        }
626                    } finally {
627                        callbacks.onHardwarePostDraw(canvas);
628                        canvas.restoreToCount(saveCount);
629                        view.mRecreateDisplayList = false;
630                    }
631
632                    onPostDraw();
633
634                    if (ViewDebug.DEBUG_PROFILE_DRAWING) {
635                        EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
636                    }
637
638                    attachInfo.mIgnoreDirtyState = false;
639
640                    sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
641                    checkEglErrors();
642                }
643            }
644        }
645
646        private int checkCurrent() {
647            // TODO: Don't check the current context when we have one per UI thread
648            // TODO: Use a threadlocal flag to know whether the surface has changed
649            if (sEgl.eglGetCurrentContext() != sEglContext ||
650                    sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
651                if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
652                    fallback(true);
653                    Log.e(LOG_TAG, "eglMakeCurrent failed " +
654                            getEGLErrorString(sEgl.eglGetError()));
655                    return SURFACE_STATE_ERROR;
656                } else {
657                    return SURFACE_STATE_UPDATED;
658                }
659            }
660            return SURFACE_STATE_SUCCESS;
661        }
662
663        static abstract class EglConfigChooser {
664            final int[] mConfigSpec;
665            private final int mGlVersion;
666
667            EglConfigChooser(int glVersion, int[] configSpec) {
668                mGlVersion = glVersion;
669                mConfigSpec = filterConfigSpec(configSpec);
670            }
671
672            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
673                int[] index = new int[1];
674                if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) {
675                    throw new IllegalArgumentException("eglChooseConfig failed "
676                            + getEGLErrorString(egl.eglGetError()));
677                }
678
679                int numConfigs = index[0];
680                if (numConfigs <= 0) {
681                    throw new IllegalArgumentException("No configs match configSpec");
682                }
683
684                EGLConfig[] configs = new EGLConfig[numConfigs];
685                if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) {
686                    throw new IllegalArgumentException("eglChooseConfig failed "
687                            + getEGLErrorString(egl.eglGetError()));
688                }
689
690                EGLConfig config = chooseConfig(egl, display, configs);
691                if (config == null) {
692                    throw new IllegalArgumentException("No config chosen");
693                }
694
695                return config;
696            }
697
698            abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
699
700            private int[] filterConfigSpec(int[] configSpec) {
701                if (mGlVersion != 2) {
702                    return configSpec;
703                }
704                /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
705                 * And we know the configSpec is well formed.
706                 */
707                int len = configSpec.length;
708                int[] newConfigSpec = new int[len + 2];
709                System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
710                newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
711                newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
712                newConfigSpec[len + 1] = EGL10.EGL_NONE;
713                return newConfigSpec;
714            }
715        }
716
717        /**
718         * Choose a configuration with exactly the specified r,g,b,a sizes,
719         * and at least the specified depth and stencil sizes.
720         */
721        static class ComponentSizeChooser extends EglConfigChooser {
722            private int[] mValue;
723
724            private final int mRedSize;
725            private final int mGreenSize;
726            private final int mBlueSize;
727            private final int mAlphaSize;
728            private final int mDepthSize;
729            private final int mStencilSize;
730            private final boolean mDirtyRegions;
731
732            ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize,
733                    int alphaSize, int depthSize, int stencilSize, boolean dirtyRegions) {
734                super(glVersion, new int[] {
735                        EGL10.EGL_RED_SIZE, redSize,
736                        EGL10.EGL_GREEN_SIZE, greenSize,
737                        EGL10.EGL_BLUE_SIZE, blueSize,
738                        EGL10.EGL_ALPHA_SIZE, alphaSize,
739                        EGL10.EGL_DEPTH_SIZE, depthSize,
740                        EGL10.EGL_STENCIL_SIZE, stencilSize,
741                        EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT |
742                                (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
743                        EGL10.EGL_NONE });
744                mValue = new int[1];
745                mRedSize = redSize;
746                mGreenSize = greenSize;
747                mBlueSize = blueSize;
748                mAlphaSize = alphaSize;
749                mDepthSize = depthSize;
750                mStencilSize = stencilSize;
751                mDirtyRegions = dirtyRegions;
752            }
753
754            @Override
755            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
756                for (EGLConfig config : configs) {
757                    int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
758                    int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
759                    if (d >= mDepthSize && s >= mStencilSize) {
760                        int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
761                        int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
762                        int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
763                        int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
764                        boolean backBuffer;
765                        if (mDirtyRegions) {
766                            int surfaceType = findConfigAttrib(egl, display, config,
767                                    EGL_SURFACE_TYPE, 0);
768                            backBuffer = (surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) != 0;
769                        } else {
770                            backBuffer = true;
771                        }
772                        if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize
773                                && backBuffer) {
774                            return config;
775                        }
776                    }
777                }
778                return null;
779            }
780
781            private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
782                    int attribute, int defaultValue) {
783                if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
784                    return mValue[0];
785                }
786
787                return defaultValue;
788            }
789        }
790    }
791
792    /**
793     * Hardware renderer using OpenGL ES 2.0.
794     */
795    static class Gl20Renderer extends GlRenderer {
796        private GLES20Canvas mGlCanvas;
797
798        Gl20Renderer(boolean translucent) {
799            super(2, translucent);
800        }
801
802        @Override
803        GLES20Canvas createCanvas() {
804            return mGlCanvas = new GLES20Canvas(mTranslucent);
805        }
806
807        @Override
808        boolean canDraw() {
809            return super.canDraw() && mGlCanvas != null;
810        }
811
812        @Override
813        void onPreDraw(Rect dirty) {
814            mGlCanvas.onPreDraw(dirty);
815        }
816
817        @Override
818        void onPostDraw() {
819            mGlCanvas.onPostDraw();
820        }
821
822        @Override
823        void destroy(boolean full) {
824            try {
825                super.destroy(full);
826            } finally {
827                if (full && mGlCanvas != null) {
828                    mGlCanvas = null;
829                }
830            }
831        }
832
833        @Override
834        DisplayList createDisplayList(View v) {
835            return new GLES20DisplayList(v);
836        }
837
838        @Override
839        HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
840            return new GLES20Layer(width, height, isOpaque);
841        }
842
843        static HardwareRenderer create(boolean translucent) {
844            if (GLES20Canvas.isAvailable()) {
845                return new Gl20Renderer(translucent);
846            }
847            return null;
848        }
849    }
850}
851