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