HardwareRenderer.java revision 7d70fbf0b5672bada8b25f065bc292796c3d4812
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.Paint;
21import android.graphics.Rect;
22import android.graphics.SurfaceTexture;
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 ViewAncestor 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(HardwareCanvas 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(HardwareCanvas 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. A hardware layer built by calling this
170     * method will be treated as a texture layer, instead of as a render target.
171     *
172     * @return A hardware layer
173     */
174    abstract HardwareLayer createHardwareLayer();
175
176    /**
177     * Creates a new hardware layer.
178     *
179     * @param width The minimum width of the layer
180     * @param height The minimum height of the layer
181     * @param isOpaque Whether the layer should be opaque or not
182     *
183     * @return A hardware layer
184     */
185    abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
186
187    /**
188     * Creates a new {@link SurfaceTexture} that can be used to render into the
189     * specified hardware layer.
190     *
191     *
192     * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
193     *
194     * @return A {@link SurfaceTexture}
195     */
196    abstract SurfaceTexture createSuraceTexture(HardwareLayer layer);
197
198    /**
199     * Updates the specified layer.
200     *
201     * @param layer The hardware layer to update
202     * @param width The layer's width
203     * @param height The layer's height
204     * @param surface The surface to update
205     */
206    abstract void updateTextureLayer(HardwareLayer layer, int width, int height,
207            SurfaceTexture surface);
208
209    /**
210     * Initializes the hardware renderer for the specified surface and setup the
211     * renderer for drawing, if needed. This is invoked when the ViewAncestor has
212     * potentially lost the hardware renderer. The hardware renderer should be
213     * reinitialized and setup when the render {@link #isRequested()} and
214     * {@link #isEnabled()}.
215     *
216     * @param width The width of the drawing surface.
217     * @param height The height of the drawing surface.
218     * @param attachInfo The
219     * @param holder
220     */
221    void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
222            SurfaceHolder holder) throws Surface.OutOfResourcesException {
223        if (isRequested()) {
224            // We lost the gl context, so recreate it.
225            if (!isEnabled()) {
226                if (initialize(holder)) {
227                    setup(width, height);
228                }
229            }
230        }
231    }
232
233    /**
234     * Creates a hardware renderer using OpenGL.
235     *
236     * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.)
237     * @param translucent True if the surface is translucent, false otherwise
238     *
239     * @return A hardware renderer backed by OpenGL.
240     */
241    static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
242        switch (glVersion) {
243            case 2:
244                return Gl20Renderer.create(translucent);
245        }
246        throw new IllegalArgumentException("Unknown GL version: " + glVersion);
247    }
248
249    /**
250     * Indicates whether hardware acceleration is currently enabled.
251     *
252     * @return True if hardware acceleration is in use, false otherwise.
253     */
254    boolean isEnabled() {
255        return mEnabled;
256    }
257
258    /**
259     * Indicates whether hardware acceleration is currently enabled.
260     *
261     * @param enabled True if the hardware renderer is in use, false otherwise.
262     */
263    void setEnabled(boolean enabled) {
264        mEnabled = enabled;
265    }
266
267    /**
268     * Indicates whether hardware acceleration is currently request but not
269     * necessarily enabled yet.
270     *
271     * @return True if requested, false otherwise.
272     */
273    boolean isRequested() {
274        return mRequested;
275    }
276
277    /**
278     * Indicates whether hardware acceleration is currently requested but not
279     * necessarily enabled yet.
280     *
281     * @return True to request hardware acceleration, false otherwise.
282     */
283    void setRequested(boolean requested) {
284        mRequested = requested;
285    }
286
287    @SuppressWarnings({"deprecation"})
288    static abstract class GlRenderer extends HardwareRenderer {
289        // These values are not exposed in our EGL APIs
290        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
291        static final int EGL_SURFACE_TYPE = 0x3033;
292        static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
293        static final int EGL_OPENGL_ES2_BIT = 4;
294
295        private static final int SURFACE_STATE_ERROR = 0;
296        private static final int SURFACE_STATE_SUCCESS = 1;
297        private static final int SURFACE_STATE_UPDATED = 2;
298
299        static EGLContext sEglContext;
300        static EGL10 sEgl;
301        static EGLDisplay sEglDisplay;
302        static EGLConfig sEglConfig;
303
304        private static Thread sEglThread;
305
306        EGLSurface mEglSurface;
307
308        GL mGl;
309        HardwareCanvas mCanvas;
310        int mFrameCount;
311        Paint mDebugPaint;
312
313        boolean mDirtyRegions;
314
315        final int mGlVersion;
316        final boolean mTranslucent;
317
318        private boolean mDestroyed;
319
320        private final Rect mRedrawClip = new Rect();
321
322        GlRenderer(int glVersion, boolean translucent) {
323            mGlVersion = glVersion;
324            mTranslucent = translucent;
325            final String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
326            //noinspection PointlessBooleanExpression,ConstantConditions
327            mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
328        }
329
330        /**
331         * Return a string for the EGL error code, or the hex representation
332         * if the error is unknown.
333         *
334         * @param error The EGL error to convert into a String.
335         *
336         * @return An error string correponding to the EGL error code.
337         */
338        static String getEGLErrorString(int error) {
339            switch (error) {
340                case EGL10.EGL_SUCCESS:
341                    return "EGL_SUCCESS";
342                case EGL10.EGL_NOT_INITIALIZED:
343                    return "EGL_NOT_INITIALIZED";
344                case EGL10.EGL_BAD_ACCESS:
345                    return "EGL_BAD_ACCESS";
346                case EGL10.EGL_BAD_ALLOC:
347                    return "EGL_BAD_ALLOC";
348                case EGL10.EGL_BAD_ATTRIBUTE:
349                    return "EGL_BAD_ATTRIBUTE";
350                case EGL10.EGL_BAD_CONFIG:
351                    return "EGL_BAD_CONFIG";
352                case EGL10.EGL_BAD_CONTEXT:
353                    return "EGL_BAD_CONTEXT";
354                case EGL10.EGL_BAD_CURRENT_SURFACE:
355                    return "EGL_BAD_CURRENT_SURFACE";
356                case EGL10.EGL_BAD_DISPLAY:
357                    return "EGL_BAD_DISPLAY";
358                case EGL10.EGL_BAD_MATCH:
359                    return "EGL_BAD_MATCH";
360                case EGL10.EGL_BAD_NATIVE_PIXMAP:
361                    return "EGL_BAD_NATIVE_PIXMAP";
362                case EGL10.EGL_BAD_NATIVE_WINDOW:
363                    return "EGL_BAD_NATIVE_WINDOW";
364                case EGL10.EGL_BAD_PARAMETER:
365                    return "EGL_BAD_PARAMETER";
366                case EGL10.EGL_BAD_SURFACE:
367                    return "EGL_BAD_SURFACE";
368                case EGL11.EGL_CONTEXT_LOST:
369                    return "EGL_CONTEXT_LOST";
370                default:
371                    return "0x" + Integer.toHexString(error);
372            }
373        }
374
375        /**
376         * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
377         * is invoked and the requested flag is turned off. The error code is
378         * also logged as a warning.
379         */
380        void checkEglErrors() {
381            if (isEnabled()) {
382                int error = sEgl.eglGetError();
383                if (error != EGL10.EGL_SUCCESS) {
384                    // something bad has happened revert to
385                    // normal rendering.
386                    fallback(error != EGL11.EGL_CONTEXT_LOST);
387                    Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error));
388                }
389            }
390        }
391
392        private void fallback(boolean fallback) {
393            destroy(true);
394            if (fallback) {
395                // we'll try again if it was context lost
396                setRequested(false);
397                Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
398                        + "Switching back to software rendering.");
399            }
400        }
401
402        @Override
403        boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException {
404            if (isRequested() && !isEnabled()) {
405                initializeEgl();
406                mGl = createEglSurface(holder);
407                mDestroyed = false;
408
409                if (mGl != null) {
410                    int err = sEgl.eglGetError();
411                    if (err != EGL10.EGL_SUCCESS) {
412                        destroy(true);
413                        setRequested(false);
414                    } else {
415                        if (mCanvas == null) {
416                            mCanvas = createCanvas();
417                        }
418                        if (mCanvas != null) {
419                            setEnabled(true);
420                        } else {
421                            Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
422                        }
423                    }
424
425                    return mCanvas != null;
426                }
427            }
428            return false;
429        }
430
431        @Override
432        void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
433            if (isRequested() && isEnabled()) {
434                createEglSurface(holder);
435            }
436        }
437
438        abstract GLES20Canvas createCanvas();
439
440        void initializeEgl() {
441            if (sEglContext != null) return;
442
443            sEglThread = Thread.currentThread();
444            sEgl = (EGL10) EGLContext.getEGL();
445
446            // Get to the default display.
447            sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
448
449            if (sEglDisplay == EGL10.EGL_NO_DISPLAY) {
450                throw new RuntimeException("eglGetDisplay failed "
451                        + getEGLErrorString(sEgl.eglGetError()));
452            }
453
454            // We can now initialize EGL for that display
455            int[] version = new int[2];
456            if (!sEgl.eglInitialize(sEglDisplay, version)) {
457                throw new RuntimeException("eglInitialize failed " +
458                        getEGLErrorString(sEgl.eglGetError()));
459            }
460
461            sEglConfig = chooseEglConfig();
462            if (sEglConfig == null) {
463                // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
464                if (mDirtyRegions) {
465                    mDirtyRegions = false;
466                    sEglConfig = chooseEglConfig();
467                    if (sEglConfig == null) {
468                        throw new RuntimeException("eglConfig not initialized");
469                    }
470                } else {
471                    throw new RuntimeException("eglConfig not initialized");
472                }
473            }
474
475            /*
476            * Create an EGL context. We want to do this as rarely as we can, because an
477            * EGL context is a somewhat heavy object.
478            */
479            sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
480        }
481
482        private EGLConfig chooseEglConfig() {
483            int[] configsCount = new int[1];
484            EGLConfig[] configs = new EGLConfig[1];
485            int[] configSpec = getConfig(mDirtyRegions);
486            if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
487                throw new IllegalArgumentException("eglChooseConfig failed " +
488                        getEGLErrorString(sEgl.eglGetError()));
489            } else if (configsCount[0] > 0) {
490                return configs[0];
491            }
492            return null;
493        }
494
495        abstract int[] getConfig(boolean dirtyRegions);
496
497        GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
498            // Check preconditions.
499            if (sEgl == null) {
500                throw new RuntimeException("egl not initialized");
501            }
502            if (sEglDisplay == null) {
503                throw new RuntimeException("eglDisplay not initialized");
504            }
505            if (sEglConfig == null) {
506                throw new RuntimeException("eglConfig not initialized");
507            }
508            if (Thread.currentThread() != sEglThread) {
509                throw new IllegalStateException("HardwareRenderer cannot be used "
510                        + "from multiple threads");
511            }
512
513            /*
514             *  The window size has changed, so we need to create a new
515             *  surface.
516             */
517            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
518                /*
519                 * Unbind and destroy the old EGL surface, if
520                 * there is one.
521                 */
522                sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
523                        EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
524                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
525            }
526
527            // Create an EGL surface we can render into.
528            mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
529
530            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
531                int error = sEgl.eglGetError();
532                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
533                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
534                    return null;
535                }
536                throw new RuntimeException("createWindowSurface failed "
537                        + getEGLErrorString(error));
538            }
539
540            /*
541             * Before we can issue GL commands, we need to make sure
542             * the context is current and bound to a surface.
543             */
544            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
545                throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
546                        + getEGLErrorString(sEgl.eglGetError()));
547            }
548
549            if (mDirtyRegions) {
550                if (!GLES20Canvas.preserveBackBuffer()) {
551                    Log.w(LOG_TAG, "Backbuffer cannot be preserved");
552                }
553            }
554
555            return sEglContext.getGL();
556        }
557
558        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
559            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL10.EGL_NONE };
560
561            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT,
562                    mGlVersion != 0 ? attrib_list : null);
563        }
564
565        @Override
566        void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
567                SurfaceHolder holder) throws Surface.OutOfResourcesException {
568            if (isRequested()) {
569                checkEglErrors();
570                super.initializeIfNeeded(width, height, attachInfo, holder);
571            }
572        }
573
574        @Override
575        void destroy(boolean full) {
576            if (full && mCanvas != null) {
577                mCanvas = null;
578            }
579
580            if (!isEnabled() || mDestroyed) return;
581
582            mDestroyed = true;
583
584            sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
585                    EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
586            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
587
588            mEglSurface = null;
589            mGl = null;
590
591            setEnabled(false);
592        }
593
594        @Override
595        void setup(int width, int height) {
596            mCanvas.setViewport(width, height);
597        }
598
599        boolean canDraw() {
600            return mGl != null && mCanvas != null;
601        }
602
603        void onPreDraw(Rect dirty) {
604        }
605
606        void onPostDraw() {
607        }
608
609        @Override
610        void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
611                Rect dirty) {
612            if (canDraw()) {
613                if (!mDirtyRegions) {
614                    dirty = null;
615                }
616
617                attachInfo.mDrawingTime = SystemClock.uptimeMillis();
618                attachInfo.mIgnoreDirtyState = true;
619                view.mPrivateFlags |= View.DRAWN;
620
621                long startTime;
622                if (ViewDebug.DEBUG_PROFILE_DRAWING) {
623                    startTime = SystemClock.elapsedRealtime();
624                }
625
626                final int surfaceState = checkCurrent();
627                if (surfaceState != SURFACE_STATE_ERROR) {
628                    // We had to change the current surface and/or context, redraw everything
629                    if (surfaceState == SURFACE_STATE_UPDATED) {
630                        dirty = null;
631                    }
632
633                    onPreDraw(dirty);
634
635                    HardwareCanvas canvas = mCanvas;
636                    attachInfo.mHardwareCanvas = canvas;
637
638                    int saveCount = canvas.save();
639                    callbacks.onHardwarePreDraw(canvas);
640
641                    try {
642                        view.mRecreateDisplayList =
643                                (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED;
644                        view.mPrivateFlags &= ~View.INVALIDATED;
645
646                        DisplayList displayList = view.getDisplayList();
647                        if (displayList != null) {
648                            if (canvas.drawDisplayList(displayList, view.getWidth(),
649                                    view.getHeight(), mRedrawClip)) {
650                                if (mRedrawClip.isEmpty() || view.getParent() == null) {
651                                    view.invalidate();
652                                } else {
653                                    view.getParent().invalidateChild(view, mRedrawClip);
654                                }
655                                mRedrawClip.setEmpty();
656                            }
657                        } else {
658                            // Shouldn't reach here
659                            view.draw(canvas);
660                        }
661
662                        if (DEBUG_DIRTY_REGION) {
663                            if (mDebugPaint == null) {
664                                mDebugPaint = new Paint();
665                                mDebugPaint.setColor(0x7fff0000);
666                            }
667                            if (dirty != null && (mFrameCount++ & 1) == 0) {
668                                canvas.drawRect(dirty, mDebugPaint);
669                            }
670                        }
671                    } finally {
672                        callbacks.onHardwarePostDraw(canvas);
673                        canvas.restoreToCount(saveCount);
674                        view.mRecreateDisplayList = false;
675                    }
676
677                    onPostDraw();
678
679                    if (ViewDebug.DEBUG_PROFILE_DRAWING) {
680                        EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
681                    }
682
683                    attachInfo.mIgnoreDirtyState = false;
684
685                    final long swapBuffersStartTime;
686                    if (ViewDebug.DEBUG_LATENCY) {
687                        swapBuffersStartTime = System.nanoTime();
688                    }
689
690                    sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
691
692                    if (ViewDebug.DEBUG_LATENCY) {
693                        long now = System.nanoTime();
694                        Log.d(LOG_TAG, "Latency: Spent "
695                                + ((now - swapBuffersStartTime) * 0.000001f)
696                                + "ms waiting for eglSwapBuffers()");
697                    }
698
699                    checkEglErrors();
700                }
701            }
702        }
703
704        private int checkCurrent() {
705            // TODO: Don't check the current context when we have one per UI thread
706            // TODO: Use a threadlocal flag to know whether the surface has changed
707            if (!sEglContext.equals(sEgl.eglGetCurrentContext()) ||
708                    !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
709                if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
710                    fallback(true);
711                    Log.e(LOG_TAG, "eglMakeCurrent failed " +
712                            getEGLErrorString(sEgl.eglGetError()));
713                    return SURFACE_STATE_ERROR;
714                } else {
715                    return SURFACE_STATE_UPDATED;
716                }
717            }
718            return SURFACE_STATE_SUCCESS;
719        }
720    }
721
722    /**
723     * Hardware renderer using OpenGL ES 2.0.
724     */
725    static class Gl20Renderer extends GlRenderer {
726        private GLES20Canvas mGlCanvas;
727
728        Gl20Renderer(boolean translucent) {
729            super(2, translucent);
730        }
731
732        @Override
733        GLES20Canvas createCanvas() {
734            return mGlCanvas = new GLES20Canvas(mTranslucent);
735        }
736
737        @Override
738        int[] getConfig(boolean dirtyRegions) {
739            return new int[] {
740                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
741                    EGL10.EGL_RED_SIZE, 8,
742                    EGL10.EGL_GREEN_SIZE, 8,
743                    EGL10.EGL_BLUE_SIZE, 8,
744                    EGL10.EGL_ALPHA_SIZE, 8,
745                    EGL10.EGL_DEPTH_SIZE, 0,
746                    EGL10.EGL_STENCIL_SIZE, 0,
747                    EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT |
748                            (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
749                    EGL10.EGL_NONE
750            };
751        }
752
753        @Override
754        boolean canDraw() {
755            return super.canDraw() && mGlCanvas != null;
756        }
757
758        @Override
759        void onPreDraw(Rect dirty) {
760            mGlCanvas.onPreDraw(dirty);
761        }
762
763        @Override
764        void onPostDraw() {
765            mGlCanvas.onPostDraw();
766        }
767
768        @Override
769        void destroy(boolean full) {
770            try {
771                super.destroy(full);
772            } finally {
773                if (full && mGlCanvas != null) {
774                    mGlCanvas = null;
775                }
776            }
777        }
778
779        @Override
780        DisplayList createDisplayList(View v) {
781            return new GLES20DisplayList(v);
782        }
783
784        @Override
785        HardwareLayer createHardwareLayer() {
786            return new GLES20TextureLayer();
787        }
788
789        @Override
790        HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
791            return new GLES20RenderLayer(width, height, isOpaque);
792        }
793
794        @Override
795        SurfaceTexture createSuraceTexture(HardwareLayer layer) {
796            return ((GLES20TextureLayer) layer).getSurfaceTexture();
797        }
798
799        @Override
800        void updateTextureLayer(HardwareLayer layer, int width, int height,
801                SurfaceTexture surface) {
802            ((GLES20TextureLayer) layer).update(width, height, surface.mSurfaceTexture);
803        }
804
805        static HardwareRenderer create(boolean translucent) {
806            if (GLES20Canvas.isAvailable()) {
807                return new Gl20Renderer(translucent);
808            }
809            return null;
810        }
811    }
812}
813