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