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