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