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