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