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