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