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