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