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