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