HardwareRenderer.java revision 7c450aaa3caac2a05fcb20a177483d0e92378426
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.content.ComponentCallbacks2;
21import android.graphics.Paint;
22import android.graphics.Rect;
23import android.graphics.SurfaceTexture;
24import android.opengl.GLUtils;
25import android.opengl.ManagedEGLContext;
26import android.os.Handler;
27import android.os.Looper;
28import android.os.SystemClock;
29import android.os.SystemProperties;
30import android.os.Trace;
31import android.util.Log;
32import com.google.android.gles_jni.EGLImpl;
33
34import javax.microedition.khronos.egl.EGL10;
35import javax.microedition.khronos.egl.EGL11;
36import javax.microedition.khronos.egl.EGLConfig;
37import javax.microedition.khronos.egl.EGLContext;
38import javax.microedition.khronos.egl.EGLDisplay;
39import javax.microedition.khronos.egl.EGLSurface;
40import javax.microedition.khronos.opengles.GL;
41
42import java.io.File;
43import java.io.PrintWriter;
44import java.util.concurrent.locks.ReentrantLock;
45
46import static javax.microedition.khronos.egl.EGL10.*;
47
48/**
49 * Interface for rendering a ViewAncestor using hardware acceleration.
50 *
51 * @hide
52 */
53public abstract class HardwareRenderer {
54    static final String LOG_TAG = "HardwareRenderer";
55
56    /**
57     * Name of the file that holds the shaders cache.
58     */
59    private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
60
61    /**
62     * Turn on to only refresh the parts of the screen that need updating.
63     * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY}
64     * must also have the value "true".
65     */
66    public static final boolean RENDER_DIRTY_REGIONS = true;
67
68    /**
69     * System property used to enable or disable dirty regions invalidation.
70     * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
71     * The default value of this property is assumed to be true.
72     *
73     * Possible values:
74     * "true", to enable partial invalidates
75     * "false", to disable partial invalidates
76     */
77    static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions";
78
79    /**
80     * System property used to enable or disable vsync.
81     * The default value of this property is assumed to be false.
82     *
83     * Possible values:
84     * "true", to disable vsync
85     * "false", to enable vsync
86     */
87    static final String DISABLE_VSYNC_PROPERTY = "debug.hwui.disable_vsync";
88
89    /**
90     * System property used to enable or disable hardware rendering profiling.
91     * The default value of this property is assumed to be false.
92     *
93     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
94     * output extra information about the time taken to execute by the last
95     * frames.
96     *
97     * Possible values:
98     * "true", to enable profiling
99     * "false", to disable profiling
100     *
101     * @hide
102     */
103    public static final String PROFILE_PROPERTY = "debug.hwui.profile";
104
105    /**
106     * System property used to specify the number of frames to be used
107     * when doing hardware rendering profiling.
108     * The default value of this property is #PROFILE_MAX_FRAMES.
109     *
110     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
111     * output extra information about the time taken to execute by the last
112     * frames.
113     *
114     * Possible values:
115     * "60", to set the limit of frames to 60
116     */
117    static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes";
118
119    /**
120     * System property used to debug EGL configuration choice.
121     *
122     * Possible values:
123     * "choice", print the chosen configuration only
124     * "all", print all possible configurations
125     */
126    static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config";
127
128    /**
129     * Turn on to draw dirty regions every other frame.
130     *
131     * Possible values:
132     * "true", to enable dirty regions debugging
133     * "false", to disable dirty regions debugging
134     *
135     * @hide
136     */
137    public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions";
138
139    /**
140     * Turn on to flash hardware layers when they update.
141     *
142     * Possible values:
143     * "true", to enable hardware layers updates debugging
144     * "false", to disable hardware layers updates debugging
145     *
146     * @hide
147     */
148    public static final String DEBUG_SHOW_LAYERS_UPDATES_PROPERTY =
149            "debug.hwui.show_layers_updates";
150
151    /**
152     * Turn on to show overdraw level.
153     *
154     * Possible values:
155     * "true", to enable overdraw debugging
156     * "false", to disable overdraw debugging
157     *
158     * @hide
159     */
160    public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";
161
162    /**
163     * A process can set this flag to false to prevent the use of hardware
164     * rendering.
165     *
166     * @hide
167     */
168    public static boolean sRendererDisabled = false;
169
170    /**
171     * Further hardware renderer disabling for the system process.
172     *
173     * @hide
174     */
175    public static boolean sSystemRendererDisabled = false;
176
177    /**
178     * Number of frames to profile.
179     */
180    private static final int PROFILE_MAX_FRAMES = 128;
181
182    /**
183     * Number of floats per profiled frame.
184     */
185    private static final int PROFILE_FRAME_DATA_COUNT = 3;
186
187    private boolean mEnabled;
188    private boolean mRequested = true;
189
190    /**
191     * Invoke this method to disable hardware rendering in the current process.
192     *
193     * @hide
194     */
195    public static void disable(boolean system) {
196        sRendererDisabled = true;
197        if (system) {
198            sSystemRendererDisabled = true;
199        }
200    }
201
202    /**
203     * Indicates whether hardware acceleration is available under any form for
204     * the view hierarchy.
205     *
206     * @return True if the view hierarchy can potentially be hardware accelerated,
207     *         false otherwise
208     */
209    public static boolean isAvailable() {
210        return GLES20Canvas.isAvailable();
211    }
212
213    /**
214     * Destroys the hardware rendering context.
215     *
216     * @param full If true, destroys all associated resources.
217     */
218    abstract void destroy(boolean full);
219
220    /**
221     * Initializes the hardware renderer for the specified surface.
222     *
223     * @param surface The surface to hardware accelerate
224     *
225     * @return True if the initialization was successful, false otherwise.
226     */
227    abstract boolean initialize(Surface surface) throws Surface.OutOfResourcesException;
228
229    /**
230     * Updates the hardware renderer for the specified surface.
231     *
232     * @param surface The surface to hardware accelerate
233     */
234    abstract void updateSurface(Surface surface) throws Surface.OutOfResourcesException;
235
236    /**
237     * Destroys the layers used by the specified view hierarchy.
238     *
239     * @param view The root of the view hierarchy
240     */
241    abstract void destroyLayers(View view);
242
243    /**
244     * Destroys all hardware rendering resources associated with the specified
245     * view hierarchy.
246     *
247     * @param view The root of the view hierarchy
248     */
249    abstract void destroyHardwareResources(View view);
250
251    /**
252     * This method should be invoked whenever the current hardware renderer
253     * context should be reset.
254     *
255     * @param surface The surface to hardware accelerate
256     */
257    abstract void invalidate(Surface surface);
258
259    /**
260     * This method should be invoked to ensure the hardware renderer is in
261     * valid state (for instance, to ensure the correct EGL context is bound
262     * to the current thread.)
263     *
264     * @return true if the renderer is now valid, false otherwise
265     */
266    abstract boolean validate();
267
268    /**
269     * This method ensures the hardware renderer is in a valid state
270     * before executing the specified action.
271     *
272     * This method will attempt to set a valid state even if the window
273     * the renderer is attached to was destroyed.
274     *
275     * @return true if the action was run
276     */
277    abstract boolean safelyRun(Runnable action);
278
279    /**
280     * Setup the hardware renderer for drawing. This is called whenever the
281     * size of the target surface changes or when the surface is first created.
282     *
283     * @param width Width of the drawing surface.
284     * @param height Height of the drawing surface.
285     */
286    abstract void setup(int width, int height);
287
288    /**
289     * Gets the current width of the surface. This is the width that the surface
290     * was last set to in a call to {@link #setup(int, int)}.
291     *
292     * @return the current width of the surface
293     */
294    abstract int getWidth();
295
296    /**
297     * Gets the current height of the surface. This is the height that the surface
298     * was last set to in a call to {@link #setup(int, int)}.
299     *
300     * @return the current width of the surface
301     */
302    abstract int getHeight();
303
304    /**
305     * Gets the current canvas associated with this HardwareRenderer.
306     *
307     * @return the current HardwareCanvas
308     */
309    abstract HardwareCanvas getCanvas();
310
311    /**
312     * Outputs extra debugging information in the specified file descriptor.
313     * @param pw
314     */
315    abstract void dumpGfxInfo(PrintWriter pw);
316
317    /**
318     * Outputs the total number of frames rendered (used for fps calculations)
319     *
320     * @return the number of frames rendered
321     */
322    abstract long getFrameCount();
323
324    /**
325     * Sets the directory to use as a persistent storage for hardware rendering
326     * resources.
327     *
328     * @param cacheDir A directory the current process can write to
329     */
330    public static void setupDiskCache(File cacheDir) {
331        nSetupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
332    }
333
334    private static native void nSetupShadersDiskCache(String cacheFile);
335
336    /**
337     * Notifies EGL that the frame is about to be rendered.
338     * @param size
339     */
340    private static void beginFrame(int[] size) {
341        nBeginFrame(size);
342    }
343
344    private static native void nBeginFrame(int[] size);
345
346    /**
347     * Preserves the back buffer of the current surface after a buffer swap.
348     * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current
349     * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL
350     * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT.
351     *
352     * @return True if the swap behavior was successfully changed,
353     *         false otherwise.
354     */
355    static boolean preserveBackBuffer() {
356        return nPreserveBackBuffer();
357    }
358
359    private static native boolean nPreserveBackBuffer();
360
361    /**
362     * Indicates whether the current surface preserves its back buffer
363     * after a buffer swap.
364     *
365     * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED,
366     *         false otherwise
367     */
368    static boolean isBackBufferPreserved() {
369        return nIsBackBufferPreserved();
370    }
371
372    private static native boolean nIsBackBufferPreserved();
373
374    /**
375     * Disables v-sync. For performance testing only.
376     */
377    static void disableVsync() {
378        nDisableVsync();
379    }
380
381    private static native void nDisableVsync();
382
383    /**
384     * Indicates that the specified hardware layer needs to be updated
385     * as soon as possible.
386     *
387     * @param layer The hardware layer that needs an update
388     */
389    abstract void pushLayerUpdate(HardwareLayer layer);
390
391    /**
392     * Interface used to receive callbacks whenever a view is drawn by
393     * a hardware renderer instance.
394     */
395    interface HardwareDrawCallbacks {
396        /**
397         * Invoked before a view is drawn by a hardware renderer.
398         *
399         * @param canvas The Canvas used to render the view.
400         */
401        void onHardwarePreDraw(HardwareCanvas canvas);
402
403        /**
404         * Invoked after a view is drawn by a hardware renderer.
405         *
406         * @param canvas The Canvas used to render the view.
407         */
408        void onHardwarePostDraw(HardwareCanvas canvas);
409    }
410
411    /**
412     * Draws the specified view.
413     *
414     * @param view The view to draw.
415     * @param attachInfo AttachInfo tied to the specified view.
416     * @param callbacks Callbacks invoked when drawing happens.
417     * @param dirty The dirty rectangle to update, can be null.
418     *
419     * @return true if the dirty rect was ignored, false otherwise
420     */
421    abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
422            Rect dirty);
423
424    /**
425     * Creates a new display list that can be used to record batches of
426     * drawing operations.
427     *
428     * @param name The name of the display list, used for debugging purpose.
429     *             May be null
430     *
431     * @return A new display list.
432     */
433    public abstract DisplayList createDisplayList(String name);
434
435    /**
436     * Creates a new hardware layer. A hardware layer built by calling this
437     * method will be treated as a texture layer, instead of as a render target.
438     *
439     * @param isOpaque Whether the layer should be opaque or not
440     *
441     * @return A hardware layer
442     */
443    abstract HardwareLayer createHardwareLayer(boolean isOpaque);
444
445    /**
446     * Creates a new hardware layer.
447     *
448     * @param width The minimum width of the layer
449     * @param height The minimum height of the layer
450     * @param isOpaque Whether the layer should be opaque or not
451     *
452     * @return A hardware layer
453     */
454    abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
455
456    /**
457     * Creates a new {@link SurfaceTexture} that can be used to render into the
458     * specified hardware layer.
459     *
460     *
461     * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
462     *
463     * @return A {@link SurfaceTexture}
464     */
465    abstract SurfaceTexture createSurfaceTexture(HardwareLayer layer);
466
467    /**
468     * Sets the {@link android.graphics.SurfaceTexture} that will be used to
469     * render into the specified hardware layer.
470     *
471     * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
472     * @param surfaceTexture The {@link android.graphics.SurfaceTexture} to use for the layer
473     */
474    abstract void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture);
475
476    /**
477     * Detaches the specified functor from the current functor execution queue.
478     *
479     * @param functor The native functor to remove from the execution queue.
480     *
481     * @see HardwareCanvas#callDrawGLFunction(int)
482     * @see #attachFunctor(android.view.View.AttachInfo, int)
483     */
484    abstract void detachFunctor(int functor);
485
486    /**
487     * Schedules the specified functor in the functors execution queue.
488     *
489     * @param attachInfo AttachInfo tied to this renderer.
490     * @param functor The native functor to insert in the execution queue.
491     *
492     * @see HardwareCanvas#callDrawGLFunction(int)
493     * @see #detachFunctor(int)
494     *
495     * @return true if the functor was attached successfully
496     */
497    abstract boolean attachFunctor(View.AttachInfo attachInfo, int functor);
498
499    /**
500     * Initializes the hardware renderer for the specified surface and setup the
501     * renderer for drawing, if needed. This is invoked when the ViewAncestor has
502     * potentially lost the hardware renderer. The hardware renderer should be
503     * reinitialized and setup when the render {@link #isRequested()} and
504     * {@link #isEnabled()}.
505     *
506     * @param width The width of the drawing surface.
507     * @param height The height of the drawing surface.
508     * @param surface The surface to hardware accelerate
509     */
510    void initializeIfNeeded(int width, int height, Surface surface)
511            throws Surface.OutOfResourcesException {
512        if (isRequested()) {
513            // We lost the gl context, so recreate it.
514            if (!isEnabled()) {
515                if (initialize(surface)) {
516                    setup(width, height);
517                }
518            }
519        }
520    }
521
522    /**
523     * Creates a hardware renderer using OpenGL.
524     *
525     * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.)
526     * @param translucent True if the surface is translucent, false otherwise
527     *
528     * @return A hardware renderer backed by OpenGL.
529     */
530    static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
531        switch (glVersion) {
532            case 2:
533                return Gl20Renderer.create(translucent);
534        }
535        throw new IllegalArgumentException("Unknown GL version: " + glVersion);
536    }
537
538    /**
539     * Invoke this method when the system is running out of memory. This
540     * method will attempt to recover as much memory as possible, based on
541     * the specified hint.
542     *
543     * @param level Hint about the amount of memory that should be trimmed,
544     *              see {@link android.content.ComponentCallbacks}
545     */
546    static void trimMemory(int level) {
547        startTrimMemory(level);
548        endTrimMemory();
549    }
550
551    /**
552     * Starts the process of trimming memory. Usually this call will setup
553     * hardware rendering context and reclaim memory.Extra cleanup might
554     * be required by calling {@link #endTrimMemory()}.
555     *
556     * @param level Hint about the amount of memory that should be trimmed,
557     *              see {@link android.content.ComponentCallbacks}
558     */
559    static void startTrimMemory(int level) {
560        Gl20Renderer.startTrimMemory(level);
561    }
562
563    /**
564     * Finishes the process of trimming memory. This method will usually
565     * cleanup special resources used by the memory trimming process.
566     */
567    static void endTrimMemory() {
568        Gl20Renderer.endTrimMemory();
569    }
570
571    /**
572     * Indicates whether hardware acceleration is currently enabled.
573     *
574     * @return True if hardware acceleration is in use, false otherwise.
575     */
576    boolean isEnabled() {
577        return mEnabled;
578    }
579
580    /**
581     * Indicates whether hardware acceleration is currently enabled.
582     *
583     * @param enabled True if the hardware renderer is in use, false otherwise.
584     */
585    void setEnabled(boolean enabled) {
586        mEnabled = enabled;
587    }
588
589    /**
590     * Indicates whether hardware acceleration is currently request but not
591     * necessarily enabled yet.
592     *
593     * @return True if requested, false otherwise.
594     */
595    boolean isRequested() {
596        return mRequested;
597    }
598
599    /**
600     * Indicates whether hardware acceleration is currently requested but not
601     * necessarily enabled yet.
602     *
603     * @return True to request hardware acceleration, false otherwise.
604     */
605    void setRequested(boolean requested) {
606        mRequested = requested;
607    }
608
609    @SuppressWarnings({"deprecation"})
610    static abstract class GlRenderer extends HardwareRenderer {
611        // These values are not exposed in our EGL APIs
612        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
613        static final int EGL_OPENGL_ES2_BIT = 4;
614        static final int EGL_SURFACE_TYPE = 0x3033;
615        static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
616
617        static final int SURFACE_STATE_ERROR = 0;
618        static final int SURFACE_STATE_SUCCESS = 1;
619        static final int SURFACE_STATE_UPDATED = 2;
620
621        static final int FUNCTOR_PROCESS_DELAY = 4;
622
623        static EGL10 sEgl;
624        static EGLDisplay sEglDisplay;
625        static EGLConfig sEglConfig;
626        static final Object[] sEglLock = new Object[0];
627        int mWidth = -1, mHeight = -1;
628
629        static final ThreadLocal<ManagedEGLContext> sEglContextStorage
630                = new ThreadLocal<ManagedEGLContext>();
631
632        EGLContext mEglContext;
633        Thread mEglThread;
634
635        EGLSurface mEglSurface;
636
637        GL mGl;
638        HardwareCanvas mCanvas;
639
640        long mFrameCount;
641        Paint mDebugPaint;
642
643        static boolean sDirtyRegions;
644        static final boolean sDirtyRegionsRequested;
645        static {
646            String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
647            //noinspection PointlessBooleanExpression,ConstantConditions
648            sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
649            sDirtyRegionsRequested = sDirtyRegions;
650        }
651
652        boolean mDirtyRegionsEnabled;
653        boolean mUpdateDirtyRegions;
654
655        final boolean mVsyncDisabled;
656
657        final boolean mProfileEnabled;
658        final float[] mProfileData;
659        final ReentrantLock mProfileLock;
660        int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
661
662        final boolean mDebugDirtyRegions;
663        final boolean mShowOverdraw;
664
665        final int mGlVersion;
666        final boolean mTranslucent;
667
668        private boolean mDestroyed;
669
670        private final Rect mRedrawClip = new Rect();
671
672        private final int[] mSurfaceSize = new int[2];
673        private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
674
675        GlRenderer(int glVersion, boolean translucent) {
676            mGlVersion = glVersion;
677            mTranslucent = translucent;
678
679            String property;
680
681            property = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false");
682            mVsyncDisabled = "true".equalsIgnoreCase(property);
683            if (mVsyncDisabled) {
684                Log.d(LOG_TAG, "Disabling v-sync");
685            }
686
687            property = SystemProperties.get(PROFILE_PROPERTY, "false");
688            mProfileEnabled = "true".equalsIgnoreCase(property);
689            if (mProfileEnabled) {
690                Log.d(LOG_TAG, "Profiling hardware renderer");
691            }
692
693            if (mProfileEnabled) {
694                property = SystemProperties.get(PROFILE_MAXFRAMES_PROPERTY,
695                        Integer.toString(PROFILE_MAX_FRAMES));
696                int maxProfileFrames = Integer.valueOf(property);
697                mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
698                for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
699                    mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
700                }
701
702                mProfileLock = new ReentrantLock();
703            } else {
704                mProfileData = null;
705                mProfileLock = null;
706            }
707
708            property = SystemProperties.get(DEBUG_DIRTY_REGIONS_PROPERTY, "false");
709            mDebugDirtyRegions = "true".equalsIgnoreCase(property);
710            if (mDebugDirtyRegions) {
711                Log.d(LOG_TAG, "Debugging dirty regions");
712            }
713
714            mShowOverdraw = SystemProperties.getBoolean(
715                    HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY, false);
716        }
717
718        @Override
719        void dumpGfxInfo(PrintWriter pw) {
720            if (mProfileEnabled) {
721                pw.printf("\n\tDraw\tProcess\tExecute\n");
722
723                mProfileLock.lock();
724                try {
725                    for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
726                        if (mProfileData[i] < 0) {
727                            break;
728                        }
729                        pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
730                                mProfileData[i + 2]);
731                        mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
732                    }
733                    mProfileCurrentFrame = mProfileData.length;
734                } finally {
735                    mProfileLock.unlock();
736                }
737            }
738        }
739
740        @Override
741        long getFrameCount() {
742            return mFrameCount;
743        }
744
745        /**
746         * Indicates whether this renderer instance can track and update dirty regions.
747         */
748        boolean hasDirtyRegions() {
749            return mDirtyRegionsEnabled;
750        }
751
752        /**
753         * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
754         * is invoked and the requested flag is turned off. The error code is
755         * also logged as a warning.
756         */
757        void checkEglErrors() {
758            if (isEnabled()) {
759                checkEglErrorsForced();
760            }
761        }
762
763        private void checkEglErrorsForced() {
764            int error = sEgl.eglGetError();
765            if (error != EGL_SUCCESS) {
766                // something bad has happened revert to
767                // normal rendering.
768                Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
769                fallback(error != EGL11.EGL_CONTEXT_LOST);
770            }
771        }
772
773        private void fallback(boolean fallback) {
774            destroy(true);
775            if (fallback) {
776                // we'll try again if it was context lost
777                setRequested(false);
778                Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
779                        + "Switching back to software rendering.");
780            }
781        }
782
783        @Override
784        boolean initialize(Surface surface) throws Surface.OutOfResourcesException {
785            if (isRequested() && !isEnabled()) {
786                initializeEgl();
787                mGl = createEglSurface(surface);
788                mDestroyed = false;
789
790                if (mGl != null) {
791                    int err = sEgl.eglGetError();
792                    if (err != EGL_SUCCESS) {
793                        destroy(true);
794                        setRequested(false);
795                    } else {
796                        if (mCanvas == null) {
797                            mCanvas = createCanvas();
798                        }
799                        if (mCanvas != null) {
800                            setEnabled(true);
801                        } else {
802                            Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
803                        }
804                    }
805
806                    return mCanvas != null;
807                }
808            }
809            return false;
810        }
811
812        @Override
813        void updateSurface(Surface surface) throws Surface.OutOfResourcesException {
814            if (isRequested() && isEnabled()) {
815                createEglSurface(surface);
816            }
817        }
818
819        abstract HardwareCanvas createCanvas();
820
821        abstract int[] getConfig(boolean dirtyRegions);
822
823        void initializeEgl() {
824            synchronized (sEglLock) {
825                if (sEgl == null && sEglConfig == null) {
826                    sEgl = (EGL10) EGLContext.getEGL();
827
828                    // Get to the default display.
829                    sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
830
831                    if (sEglDisplay == EGL_NO_DISPLAY) {
832                        throw new RuntimeException("eglGetDisplay failed "
833                                + GLUtils.getEGLErrorString(sEgl.eglGetError()));
834                    }
835
836                    // We can now initialize EGL for that display
837                    int[] version = new int[2];
838                    if (!sEgl.eglInitialize(sEglDisplay, version)) {
839                        throw new RuntimeException("eglInitialize failed " +
840                                GLUtils.getEGLErrorString(sEgl.eglGetError()));
841                    }
842
843                    checkEglErrorsForced();
844
845                    sEglConfig = chooseEglConfig();
846                    if (sEglConfig == null) {
847                        // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
848                        if (sDirtyRegions) {
849                            sDirtyRegions = false;
850                            sEglConfig = chooseEglConfig();
851                            if (sEglConfig == null) {
852                                throw new RuntimeException("eglConfig not initialized");
853                            }
854                        } else {
855                            throw new RuntimeException("eglConfig not initialized");
856                        }
857                    }
858                }
859            }
860
861            ManagedEGLContext managedContext = sEglContextStorage.get();
862            mEglContext = managedContext != null ? managedContext.getContext() : null;
863            mEglThread = Thread.currentThread();
864
865            if (mEglContext == null) {
866                mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
867                sEglContextStorage.set(createManagedContext(mEglContext));
868            }
869        }
870
871        abstract ManagedEGLContext createManagedContext(EGLContext eglContext);
872
873        private EGLConfig chooseEglConfig() {
874            EGLConfig[] configs = new EGLConfig[1];
875            int[] configsCount = new int[1];
876            int[] configSpec = getConfig(sDirtyRegions);
877
878            // Debug
879            final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
880            if ("all".equalsIgnoreCase(debug)) {
881                sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
882
883                EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
884                sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
885                        configsCount[0], configsCount);
886
887                for (EGLConfig config : debugConfigs) {
888                    printConfig(config);
889                }
890            }
891
892            if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
893                throw new IllegalArgumentException("eglChooseConfig failed " +
894                        GLUtils.getEGLErrorString(sEgl.eglGetError()));
895            } else if (configsCount[0] > 0) {
896                if ("choice".equalsIgnoreCase(debug)) {
897                    printConfig(configs[0]);
898                }
899                return configs[0];
900            }
901
902            return null;
903        }
904
905        private static void printConfig(EGLConfig config) {
906            int[] value = new int[1];
907
908            Log.d(LOG_TAG, "EGL configuration " + config + ":");
909
910            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
911            Log.d(LOG_TAG, "  RED_SIZE = " + value[0]);
912
913            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
914            Log.d(LOG_TAG, "  GREEN_SIZE = " + value[0]);
915
916            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
917            Log.d(LOG_TAG, "  BLUE_SIZE = " + value[0]);
918
919            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
920            Log.d(LOG_TAG, "  ALPHA_SIZE = " + value[0]);
921
922            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
923            Log.d(LOG_TAG, "  DEPTH_SIZE = " + value[0]);
924
925            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
926            Log.d(LOG_TAG, "  STENCIL_SIZE = " + value[0]);
927
928            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
929            Log.d(LOG_TAG, "  SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
930        }
931
932        GL createEglSurface(Surface surface) throws Surface.OutOfResourcesException {
933            // Check preconditions.
934            if (sEgl == null) {
935                throw new RuntimeException("egl not initialized");
936            }
937            if (sEglDisplay == null) {
938                throw new RuntimeException("eglDisplay not initialized");
939            }
940            if (sEglConfig == null) {
941                throw new RuntimeException("eglConfig not initialized");
942            }
943            if (Thread.currentThread() != mEglThread) {
944                throw new IllegalStateException("HardwareRenderer cannot be used "
945                        + "from multiple threads");
946            }
947
948            // In case we need to destroy an existing surface
949            destroySurface();
950
951            // Create an EGL surface we can render into.
952            if (!createSurface(surface)) {
953                return null;
954            }
955
956            /*
957             * Before we can issue GL commands, we need to make sure
958             * the context is current and bound to a surface.
959             */
960            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
961                throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
962                        + GLUtils.getEGLErrorString(sEgl.eglGetError()));
963            }
964
965            initCaches();
966
967            enableDirtyRegions();
968
969            return mEglContext.getGL();
970        }
971
972        private void enableDirtyRegions() {
973            // If mDirtyRegions is set, this means we have an EGL configuration
974            // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
975            if (sDirtyRegions) {
976                if (!(mDirtyRegionsEnabled = preserveBackBuffer())) {
977                    Log.w(LOG_TAG, "Backbuffer cannot be preserved");
978                }
979            } else if (sDirtyRegionsRequested) {
980                // If mDirtyRegions is not set, our EGL configuration does not
981                // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
982                // swap behavior might be EGL_BUFFER_PRESERVED, which means we
983                // want to set mDirtyRegions. We try to do this only if dirty
984                // regions were initially requested as part of the device
985                // configuration (see RENDER_DIRTY_REGIONS)
986                mDirtyRegionsEnabled = isBackBufferPreserved();
987            }
988        }
989
990        abstract void initCaches();
991
992        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
993            int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
994
995            return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
996                    mGlVersion != 0 ? attribs : null);
997        }
998
999        @Override
1000        void destroy(boolean full) {
1001            if (full && mCanvas != null) {
1002                mCanvas = null;
1003            }
1004
1005            if (!isEnabled() || mDestroyed) {
1006                setEnabled(false);
1007                return;
1008            }
1009
1010            destroySurface();
1011            setEnabled(false);
1012
1013            mDestroyed = true;
1014            mGl = null;
1015        }
1016
1017        void destroySurface() {
1018            if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
1019                sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1020                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
1021                mEglSurface = null;
1022            }
1023        }
1024
1025        @Override
1026        void invalidate(Surface surface) {
1027            // Cancels any existing buffer to ensure we'll get a buffer
1028            // of the right size before we call eglSwapBuffers
1029            sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1030
1031            if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
1032                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
1033                mEglSurface = null;
1034                setEnabled(false);
1035            }
1036
1037            if (surface.isValid()) {
1038                if (!createSurface(surface)) {
1039                    return;
1040                }
1041
1042                mUpdateDirtyRegions = true;
1043
1044                if (mCanvas != null) {
1045                    setEnabled(true);
1046                }
1047            }
1048        }
1049
1050        private boolean createSurface(Surface surface) {
1051            mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null);
1052
1053            if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
1054                int error = sEgl.eglGetError();
1055                if (error == EGL_BAD_NATIVE_WINDOW) {
1056                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
1057                    return false;
1058                }
1059                throw new RuntimeException("createWindowSurface failed "
1060                        + GLUtils.getEGLErrorString(error));
1061            }
1062            return true;
1063        }
1064
1065        @Override
1066        boolean validate() {
1067            return checkCurrent() != SURFACE_STATE_ERROR;
1068        }
1069
1070        @Override
1071        void setup(int width, int height) {
1072            if (validate()) {
1073                mCanvas.setViewport(width, height);
1074                mWidth = width;
1075                mHeight = height;
1076            }
1077        }
1078
1079        @Override
1080        int getWidth() {
1081            return mWidth;
1082        }
1083
1084        @Override
1085        int getHeight() {
1086            return mHeight;
1087        }
1088
1089        @Override
1090        HardwareCanvas getCanvas() {
1091            return mCanvas;
1092        }
1093
1094        boolean canDraw() {
1095            return mGl != null && mCanvas != null;
1096        }
1097
1098        int onPreDraw(Rect dirty) {
1099            return DisplayList.STATUS_DONE;
1100        }
1101
1102        void onPostDraw() {
1103        }
1104
1105        class FunctorsRunnable implements Runnable {
1106            View.AttachInfo attachInfo;
1107
1108            @Override
1109            public void run() {
1110                final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
1111                if (renderer == null || !renderer.isEnabled() || renderer != GlRenderer.this) {
1112                    return;
1113                }
1114
1115                final int surfaceState = checkCurrent();
1116                if (surfaceState != SURFACE_STATE_ERROR) {
1117                    int status = mCanvas.invokeFunctors(mRedrawClip);
1118                    handleFunctorStatus(attachInfo, status);
1119                }
1120            }
1121        }
1122
1123        @Override
1124        boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
1125                Rect dirty) {
1126            if (canDraw()) {
1127                if (!hasDirtyRegions()) {
1128                    dirty = null;
1129                }
1130                attachInfo.mIgnoreDirtyState = true;
1131                attachInfo.mDrawingTime = SystemClock.uptimeMillis();
1132
1133                view.mPrivateFlags |= View.PFLAG_DRAWN;
1134
1135                final int surfaceState = checkCurrent();
1136                if (surfaceState != SURFACE_STATE_ERROR) {
1137                    HardwareCanvas canvas = mCanvas;
1138                    attachInfo.mHardwareCanvas = canvas;
1139
1140                    if (mProfileEnabled) {
1141                        mProfileLock.lock();
1142                    }
1143
1144                    // We had to change the current surface and/or context, redraw everything
1145                    if (surfaceState == SURFACE_STATE_UPDATED) {
1146                        dirty = null;
1147                        beginFrame(null);
1148                    } else {
1149                        int[] size = mSurfaceSize;
1150                        beginFrame(size);
1151
1152                        if (size[1] != mHeight || size[0] != mWidth) {
1153                            mWidth = size[0];
1154                            mHeight = size[1];
1155
1156                            canvas.setViewport(mWidth, mHeight);
1157
1158                            dirty = null;
1159                        }
1160                    }
1161
1162                    int saveCount = 0;
1163                    int status = DisplayList.STATUS_DONE;
1164
1165                    try {
1166                        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
1167                                == View.PFLAG_INVALIDATED;
1168                        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
1169
1170                        long getDisplayListStartTime = 0;
1171                        if (mProfileEnabled) {
1172                            mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
1173                            if (mProfileCurrentFrame >= mProfileData.length) {
1174                                mProfileCurrentFrame = 0;
1175                            }
1176
1177                            getDisplayListStartTime = System.nanoTime();
1178                        }
1179
1180                        canvas.clearLayerUpdates();
1181
1182                        DisplayList displayList;
1183                        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
1184                        try {
1185                            displayList = view.getDisplayList();
1186                        } finally {
1187                            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1188                        }
1189
1190                        status = onPreDraw(dirty);
1191                        saveCount = canvas.save();
1192                        callbacks.onHardwarePreDraw(canvas);
1193
1194                        if (mProfileEnabled) {
1195                            long now = System.nanoTime();
1196                            float total = (now - getDisplayListStartTime) * 0.000001f;
1197                            //noinspection PointlessArithmeticExpression
1198                            mProfileData[mProfileCurrentFrame] = total;
1199                        }
1200
1201                        if (displayList != null) {
1202                            long drawDisplayListStartTime = 0;
1203                            if (mProfileEnabled) {
1204                                drawDisplayListStartTime = System.nanoTime();
1205                            }
1206
1207                            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
1208                            try {
1209                                status |= canvas.drawDisplayList(displayList, mRedrawClip,
1210                                        DisplayList.FLAG_CLIP_CHILDREN);
1211                            } finally {
1212                                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1213                            }
1214
1215                            if (mProfileEnabled) {
1216                                long now = System.nanoTime();
1217                                float total = (now - drawDisplayListStartTime) * 0.000001f;
1218                                mProfileData[mProfileCurrentFrame + 1] = total;
1219                            }
1220
1221                            handleFunctorStatus(attachInfo, status);
1222                        } else {
1223                            // Shouldn't reach here
1224                            view.draw(canvas);
1225                        }
1226                    } finally {
1227                        callbacks.onHardwarePostDraw(canvas);
1228                        canvas.restoreToCount(saveCount);
1229                        view.mRecreateDisplayList = false;
1230
1231                        mFrameCount++;
1232
1233                        if (mDebugDirtyRegions) {
1234                            if (mDebugPaint == null) {
1235                                mDebugPaint = new Paint();
1236                                mDebugPaint.setColor(0x7fff0000);
1237                            }
1238
1239                            if (dirty != null && (mFrameCount & 1) == 0) {
1240                                canvas.drawRect(dirty, mDebugPaint);
1241                            }
1242                        }
1243                    }
1244
1245                    onPostDraw();
1246
1247                    attachInfo.mIgnoreDirtyState = false;
1248
1249                    if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
1250                        long eglSwapBuffersStartTime = 0;
1251                        if (mProfileEnabled) {
1252                            eglSwapBuffersStartTime = System.nanoTime();
1253                        }
1254
1255                        sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
1256
1257                        if (mProfileEnabled) {
1258                            long now = System.nanoTime();
1259                            float total = (now - eglSwapBuffersStartTime) * 0.000001f;
1260                            mProfileData[mProfileCurrentFrame + 2] = total;
1261                        }
1262
1263                        checkEglErrors();
1264                    }
1265
1266                    if (mProfileEnabled) {
1267                        mProfileLock.unlock();
1268                    }
1269
1270                    return dirty == null;
1271                }
1272            }
1273
1274            return false;
1275        }
1276
1277        private void handleFunctorStatus(View.AttachInfo attachInfo, int status) {
1278            // If the draw flag is set, functors will be invoked while executing
1279            // the tree of display lists
1280            if ((status & DisplayList.STATUS_DRAW) != 0) {
1281                if (mRedrawClip.isEmpty()) {
1282                    attachInfo.mViewRootImpl.invalidate();
1283                } else {
1284                    attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip);
1285                    mRedrawClip.setEmpty();
1286                }
1287            }
1288
1289            if ((status & DisplayList.STATUS_INVOKE) != 0) {
1290                scheduleFunctors(attachInfo, true);
1291            }
1292        }
1293
1294        private void scheduleFunctors(View.AttachInfo attachInfo, boolean delayed) {
1295            mFunctorsRunnable.attachInfo = attachInfo;
1296            if (!attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) {
1297                // delay the functor callback by a few ms so it isn't polled constantly
1298                attachInfo.mHandler.postDelayed(mFunctorsRunnable,
1299                                                delayed ? FUNCTOR_PROCESS_DELAY : 0);
1300            }
1301        }
1302
1303        @Override
1304        void detachFunctor(int functor) {
1305            if (mCanvas != null) {
1306                mCanvas.detachFunctor(functor);
1307            }
1308        }
1309
1310        @Override
1311        boolean attachFunctor(View.AttachInfo attachInfo, int functor) {
1312            if (mCanvas != null) {
1313                mCanvas.attachFunctor(functor);
1314                scheduleFunctors(attachInfo, false);
1315                return true;
1316            }
1317            return false;
1318        }
1319
1320        /**
1321         * Ensures the current EGL context is the one we expect.
1322         *
1323         * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
1324         *         {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
1325         *         {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
1326         */
1327        int checkCurrent() {
1328            if (mEglThread != Thread.currentThread()) {
1329                throw new IllegalStateException("Hardware acceleration can only be used with a " +
1330                        "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
1331                        "Current thread: " + Thread.currentThread());
1332            }
1333
1334            if (!mEglContext.equals(sEgl.eglGetCurrentContext()) ||
1335                    !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
1336                if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
1337                    Log.e(LOG_TAG, "eglMakeCurrent failed " +
1338                            GLUtils.getEGLErrorString(sEgl.eglGetError()));
1339                    fallback(true);
1340                    return SURFACE_STATE_ERROR;
1341                } else {
1342                    if (mUpdateDirtyRegions) {
1343                        enableDirtyRegions();
1344                        mUpdateDirtyRegions = false;
1345                    }
1346                    return SURFACE_STATE_UPDATED;
1347                }
1348            }
1349            return SURFACE_STATE_SUCCESS;
1350        }
1351    }
1352
1353    /**
1354     * Hardware renderer using OpenGL ES 2.0.
1355     */
1356    static class Gl20Renderer extends GlRenderer {
1357        private GLES20Canvas mGlCanvas;
1358
1359        private static EGLSurface sPbuffer;
1360        private static final Object[] sPbufferLock = new Object[0];
1361
1362        static class Gl20RendererEglContext extends ManagedEGLContext {
1363            final Handler mHandler = new Handler();
1364
1365            public Gl20RendererEglContext(EGLContext context) {
1366                super(context);
1367            }
1368
1369            @Override
1370            public void onTerminate(final EGLContext eglContext) {
1371                // Make sure we do this on the correct thread.
1372                if (mHandler.getLooper() != Looper.myLooper()) {
1373                    mHandler.post(new Runnable() {
1374                        @Override
1375                        public void run() {
1376                            onTerminate(eglContext);
1377                        }
1378                    });
1379                    return;
1380                }
1381
1382                synchronized (sEglLock) {
1383                    if (sEgl == null) return;
1384
1385                    if (EGLImpl.getInitCount(sEglDisplay) == 1) {
1386                        usePbufferSurface(eglContext);
1387                        GLES20Canvas.terminateCaches();
1388
1389                        sEgl.eglDestroyContext(sEglDisplay, eglContext);
1390                        sEglContextStorage.set(null);
1391                        sEglContextStorage.remove();
1392
1393                        sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
1394                        sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
1395                                EGL_NO_SURFACE, EGL_NO_CONTEXT);
1396
1397                        sEgl.eglReleaseThread();
1398                        sEgl.eglTerminate(sEglDisplay);
1399
1400                        sEgl = null;
1401                        sEglDisplay = null;
1402                        sEglConfig = null;
1403                        sPbuffer = null;
1404                    }
1405                }
1406            }
1407        }
1408
1409        Gl20Renderer(boolean translucent) {
1410            super(2, translucent);
1411        }
1412
1413        @Override
1414        HardwareCanvas createCanvas() {
1415            return mGlCanvas = new GLES20Canvas(mTranslucent);
1416        }
1417
1418        @Override
1419        ManagedEGLContext createManagedContext(EGLContext eglContext) {
1420            return new Gl20Renderer.Gl20RendererEglContext(mEglContext);
1421        }
1422
1423        @Override
1424        int[] getConfig(boolean dirtyRegions) {
1425            return new int[] {
1426                    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
1427                    EGL_RED_SIZE, 8,
1428                    EGL_GREEN_SIZE, 8,
1429                    EGL_BLUE_SIZE, 8,
1430                    EGL_ALPHA_SIZE, 8,
1431                    EGL_DEPTH_SIZE, 0,
1432                    // TODO: Find a better way to choose the stencil size
1433                    EGL_STENCIL_SIZE, mShowOverdraw ? GLES20Canvas.getStencilSize() : 0,
1434                    EGL_SURFACE_TYPE, EGL_WINDOW_BIT |
1435                            (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
1436                    EGL_NONE
1437            };
1438        }
1439
1440        @Override
1441        void initCaches() {
1442            GLES20Canvas.initCaches();
1443        }
1444
1445        @Override
1446        boolean canDraw() {
1447            return super.canDraw() && mGlCanvas != null;
1448        }
1449
1450        @Override
1451        int onPreDraw(Rect dirty) {
1452            return mGlCanvas.onPreDraw(dirty);
1453        }
1454
1455        @Override
1456        void onPostDraw() {
1457            mGlCanvas.onPostDraw();
1458        }
1459
1460        @Override
1461        void destroy(boolean full) {
1462            try {
1463                super.destroy(full);
1464            } finally {
1465                if (full && mGlCanvas != null) {
1466                    mGlCanvas = null;
1467                }
1468            }
1469        }
1470
1471        @Override
1472        void setup(int width, int height) {
1473            super.setup(width, height);
1474            if (mVsyncDisabled) {
1475                disableVsync();
1476            }
1477        }
1478
1479        @Override
1480        void pushLayerUpdate(HardwareLayer layer) {
1481            mGlCanvas.pushLayerUpdate(layer);
1482        }
1483
1484        @Override
1485        public DisplayList createDisplayList(String name) {
1486            return new GLES20DisplayList(name);
1487        }
1488
1489        @Override
1490        HardwareLayer createHardwareLayer(boolean isOpaque) {
1491            return new GLES20TextureLayer(isOpaque);
1492        }
1493
1494        @Override
1495        HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
1496            return new GLES20RenderLayer(width, height, isOpaque);
1497        }
1498
1499        @Override
1500        SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
1501            return ((GLES20TextureLayer) layer).getSurfaceTexture();
1502        }
1503
1504        @Override
1505        void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
1506            ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture);
1507        }
1508
1509        @Override
1510        void destroyLayers(View view) {
1511            if (view != null && isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) {
1512                destroyHardwareLayer(view);
1513                GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
1514            }
1515        }
1516
1517        private static void destroyHardwareLayer(View view) {
1518            view.destroyLayer(true);
1519
1520            if (view instanceof ViewGroup) {
1521                ViewGroup group = (ViewGroup) view;
1522
1523                int count = group.getChildCount();
1524                for (int i = 0; i < count; i++) {
1525                    destroyHardwareLayer(group.getChildAt(i));
1526                }
1527            }
1528        }
1529
1530        @Override
1531        boolean safelyRun(Runnable action) {
1532            boolean needsContext = true;
1533            if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false;
1534
1535            if (needsContext) {
1536                Gl20RendererEglContext managedContext =
1537                        (Gl20RendererEglContext) sEglContextStorage.get();
1538                if (managedContext == null) return false;
1539                usePbufferSurface(managedContext.getContext());
1540            }
1541
1542            try {
1543                action.run();
1544            } finally {
1545                if (needsContext) {
1546                    sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
1547                            EGL_NO_SURFACE, EGL_NO_CONTEXT);
1548                }
1549            }
1550
1551            return true;
1552        }
1553
1554        @Override
1555        void destroyHardwareResources(final View view) {
1556            if (view != null) {
1557                safelyRun(new Runnable() {
1558                    @Override
1559                    public void run() {
1560                        destroyResources(view);
1561                        GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
1562                    }
1563                });
1564            }
1565        }
1566
1567        private static void destroyResources(View view) {
1568            view.destroyHardwareResources();
1569
1570            if (view instanceof ViewGroup) {
1571                ViewGroup group = (ViewGroup) view;
1572
1573                int count = group.getChildCount();
1574                for (int i = 0; i < count; i++) {
1575                    destroyResources(group.getChildAt(i));
1576                }
1577            }
1578        }
1579
1580        static HardwareRenderer create(boolean translucent) {
1581            if (GLES20Canvas.isAvailable()) {
1582                return new Gl20Renderer(translucent);
1583            }
1584            return null;
1585        }
1586
1587        static void startTrimMemory(int level) {
1588            if (sEgl == null || sEglConfig == null) return;
1589
1590            Gl20RendererEglContext managedContext =
1591                    (Gl20RendererEglContext) sEglContextStorage.get();
1592            // We do not have OpenGL objects
1593            if (managedContext == null) {
1594                return;
1595            } else {
1596                usePbufferSurface(managedContext.getContext());
1597            }
1598
1599            if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
1600                GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
1601            } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
1602                GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
1603            }
1604        }
1605
1606        static void endTrimMemory() {
1607            if (sEgl != null && sEglDisplay != null) {
1608                sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1609            }
1610        }
1611
1612        private static void usePbufferSurface(EGLContext eglContext) {
1613            synchronized (sPbufferLock) {
1614                // Create a temporary 1x1 pbuffer so we have a context
1615                // to clear our OpenGL objects
1616                if (sPbuffer == null) {
1617                    sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
1618                            EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
1619                    });
1620                }
1621            }
1622            sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
1623        }
1624    }
1625}
1626