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