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