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