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