HardwareRenderer.java revision 735738c4ddf3229caa5f6e634bf591953ac29944
1/*
2 * Copyright (C) 2013 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
17package android.view;
18
19import android.content.ComponentCallbacks2;
20import android.graphics.Paint;
21import android.graphics.Rect;
22import android.graphics.SurfaceTexture;
23import android.opengl.EGL14;
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.os.Trace;
31import android.util.DisplayMetrics;
32import android.util.Log;
33import com.google.android.gles_jni.EGLImpl;
34
35import javax.microedition.khronos.egl.EGL10;
36import javax.microedition.khronos.egl.EGL11;
37import javax.microedition.khronos.egl.EGLConfig;
38import javax.microedition.khronos.egl.EGLContext;
39import javax.microedition.khronos.egl.EGLDisplay;
40import javax.microedition.khronos.egl.EGLSurface;
41import javax.microedition.khronos.opengles.GL;
42
43import java.io.File;
44import java.io.PrintWriter;
45import java.util.Arrays;
46import java.util.concurrent.locks.ReentrantLock;
47
48import static javax.microedition.khronos.egl.EGL10.*;
49
50/**
51 * Interface for rendering a ViewAncestor using hardware acceleration.
52 *
53 * @hide
54 */
55public abstract class HardwareRenderer {
56    static final String LOG_TAG = "HardwareRenderer";
57
58    /**
59     * Name of the file that holds the shaders cache.
60     */
61    private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
62
63    /**
64     * Turn on to only refresh the parts of the screen that need updating.
65     * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY}
66     * must also have the value "true".
67     */
68    public static final boolean RENDER_DIRTY_REGIONS = true;
69
70    /**
71     * System property used to enable or disable dirty regions invalidation.
72     * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
73     * The default value of this property is assumed to be true.
74     *
75     * Possible values:
76     * "true", to enable partial invalidates
77     * "false", to disable partial invalidates
78     */
79    static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions";
80
81    /**
82     * System property used to enable or disable hardware rendering profiling.
83     * The default value of this property is assumed to be false.
84     *
85     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
86     * output extra information about the time taken to execute by the last
87     * frames.
88     *
89     * Possible values:
90     * "true", to enable profiling
91     * "visual_bars", to enable profiling and visualize the results on screen
92     * "visual_lines", to enable profiling and visualize the results on screen
93     * "false", to disable profiling
94     *
95     * @see #PROFILE_PROPERTY_VISUALIZE_BARS
96     * @see #PROFILE_PROPERTY_VISUALIZE_LINES
97     *
98     * @hide
99     */
100    public static final String PROFILE_PROPERTY = "debug.hwui.profile";
101
102    /**
103     * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
104     * value, profiling data will be visualized on screen as a bar chart.
105     *
106     * @hide
107     */
108    public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
109
110    /**
111     * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
112     * value, profiling data will be visualized on screen as a line chart.
113     *
114     * @hide
115     */
116    public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines";
117
118    /**
119     * System property used to specify the number of frames to be used
120     * when doing hardware rendering profiling.
121     * The default value of this property is #PROFILE_MAX_FRAMES.
122     *
123     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
124     * output extra information about the time taken to execute by the last
125     * frames.
126     *
127     * Possible values:
128     * "60", to set the limit of frames to 60
129     */
130    static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes";
131
132    /**
133     * System property used to debug EGL configuration choice.
134     *
135     * Possible values:
136     * "choice", print the chosen configuration only
137     * "all", print all possible configurations
138     */
139    static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config";
140
141    /**
142     * Turn on to draw dirty regions every other frame.
143     *
144     * Possible values:
145     * "true", to enable dirty regions debugging
146     * "false", to disable dirty regions debugging
147     *
148     * @hide
149     */
150    public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions";
151
152    /**
153     * Turn on to flash hardware layers when they update.
154     *
155     * Possible values:
156     * "true", to enable hardware layers updates debugging
157     * "false", to disable hardware layers updates debugging
158     *
159     * @hide
160     */
161    public static final String DEBUG_SHOW_LAYERS_UPDATES_PROPERTY =
162            "debug.hwui.show_layers_updates";
163
164    /**
165     * Turn on to show overdraw level.
166     *
167     * Possible values:
168     * "true", to enable overdraw debugging
169     * "false", to disable overdraw debugging
170     *
171     * @hide
172     */
173    public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";
174
175    /**
176     * Turn on to allow region clipping (see
177     * {@link android.graphics.Canvas#clipPath(android.graphics.Path)} and
178     * {@link android.graphics.Canvas#clipRegion(android.graphics.Region)}.
179     *
180     * When this option is turned on a stencil buffer is always required.
181     * If this option is off a stencil buffer is only created when the overdraw
182     * debugging mode is turned on.
183     */
184    private static final boolean REGION_CLIPPING_ENABLED = false;
185
186    /**
187     * A process can set this flag to false to prevent the use of hardware
188     * rendering.
189     *
190     * @hide
191     */
192    public static boolean sRendererDisabled = false;
193
194    /**
195     * Further hardware renderer disabling for the system process.
196     *
197     * @hide
198     */
199    public static boolean sSystemRendererDisabled = false;
200
201    /**
202     * Number of frames to profile.
203     */
204    private static final int PROFILE_MAX_FRAMES = 128;
205
206    /**
207     * Number of floats per profiled frame.
208     */
209    private static final int PROFILE_FRAME_DATA_COUNT = 3;
210
211    private boolean mEnabled;
212    private boolean mRequested = true;
213
214    /**
215     * Invoke this method to disable hardware rendering in the current process.
216     *
217     * @hide
218     */
219    public static void disable(boolean system) {
220        sRendererDisabled = true;
221        if (system) {
222            sSystemRendererDisabled = true;
223        }
224    }
225
226    /**
227     * Indicates whether hardware acceleration is available under any form for
228     * the view hierarchy.
229     *
230     * @return True if the view hierarchy can potentially be hardware accelerated,
231     *         false otherwise
232     */
233    public static boolean isAvailable() {
234        return GLES20Canvas.isAvailable();
235    }
236
237    /**
238     * Destroys the hardware rendering context.
239     *
240     * @param full If true, destroys all associated resources.
241     */
242    abstract void destroy(boolean full);
243
244    /**
245     * Initializes the hardware renderer for the specified surface.
246     *
247     * @param surface The surface to hardware accelerate
248     *
249     * @return True if the initialization was successful, false otherwise.
250     */
251    abstract boolean initialize(Surface surface) throws Surface.OutOfResourcesException;
252
253    /**
254     * Updates the hardware renderer for the specified surface.
255     *
256     * @param surface The surface to hardware accelerate
257     */
258    abstract void updateSurface(Surface surface) throws Surface.OutOfResourcesException;
259
260    /**
261     * Destroys the layers used by the specified view hierarchy.
262     *
263     * @param view The root of the view hierarchy
264     */
265    abstract void destroyLayers(View view);
266
267    /**
268     * Destroys all hardware rendering resources associated with the specified
269     * view hierarchy.
270     *
271     * @param view The root of the view hierarchy
272     */
273    abstract void destroyHardwareResources(View view);
274
275    /**
276     * This method should be invoked whenever the current hardware renderer
277     * context should be reset.
278     *
279     * @param surface The surface to hardware accelerate
280     */
281    abstract void invalidate(Surface surface);
282
283    /**
284     * This method should be invoked to ensure the hardware renderer is in
285     * valid state (for instance, to ensure the correct EGL context is bound
286     * to the current thread.)
287     *
288     * @return true if the renderer is now valid, false otherwise
289     */
290    abstract boolean validate();
291
292    /**
293     * This method ensures the hardware renderer is in a valid state
294     * before executing the specified action.
295     *
296     * This method will attempt to set a valid state even if the window
297     * the renderer is attached to was destroyed.
298     *
299     * @return true if the action was run
300     */
301    abstract boolean safelyRun(Runnable action);
302
303    /**
304     * Setup the hardware renderer for drawing. This is called whenever the
305     * size of the target surface changes or when the surface is first created.
306     *
307     * @param width Width of the drawing surface.
308     * @param height Height of the drawing surface.
309     */
310    abstract void setup(int width, int height);
311
312    /**
313     * Gets the current width of the surface. This is the width that the surface
314     * was last set to in a call to {@link #setup(int, int)}.
315     *
316     * @return the current width of the surface
317     */
318    abstract int getWidth();
319
320    /**
321     * Gets the current height of the surface. This is the height that the surface
322     * was last set to in a call to {@link #setup(int, int)}.
323     *
324     * @return the current width of the surface
325     */
326    abstract int getHeight();
327
328    /**
329     * Gets the current canvas associated with this HardwareRenderer.
330     *
331     * @return the current HardwareCanvas
332     */
333    abstract HardwareCanvas getCanvas();
334
335    /**
336     * Outputs extra debugging information in the specified file descriptor.
337     * @param pw
338     */
339    abstract void dumpGfxInfo(PrintWriter pw);
340
341    /**
342     * Outputs the total number of frames rendered (used for fps calculations)
343     *
344     * @return the number of frames rendered
345     */
346    abstract long getFrameCount();
347
348    /**
349     * Loads system properties used by the renderer. This method is invoked
350     * whenever system properties are modified. Implementations can use this
351     * to trigger live updates of the renderer based on properties.
352     *
353     * @param surface The surface to update with the new properties.
354     *                Can be null.
355     *
356     * @return True if a property has changed.
357     */
358    abstract boolean loadSystemProperties(Surface surface);
359
360    private static native boolean nLoadProperties();
361
362    /**
363     * Sets the directory to use as a persistent storage for hardware rendering
364     * resources.
365     *
366     * @param cacheDir A directory the current process can write to
367     */
368    public static void setupDiskCache(File cacheDir) {
369        nSetupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
370    }
371
372    private static native void nSetupShadersDiskCache(String cacheFile);
373
374    /**
375     * Notifies EGL that the frame is about to be rendered.
376     * @param size
377     */
378    static void beginFrame(int[] size) {
379        nBeginFrame(size);
380    }
381
382    private static native void nBeginFrame(int[] size);
383
384    /**
385     * Preserves the back buffer of the current surface after a buffer swap.
386     * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current
387     * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL
388     * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT.
389     *
390     * @return True if the swap behavior was successfully changed,
391     *         false otherwise.
392     */
393    static boolean preserveBackBuffer() {
394        return nPreserveBackBuffer();
395    }
396
397    private static native boolean nPreserveBackBuffer();
398
399    /**
400     * Indicates whether the current surface preserves its back buffer
401     * after a buffer swap.
402     *
403     * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED,
404     *         false otherwise
405     */
406    static boolean isBackBufferPreserved() {
407        return nIsBackBufferPreserved();
408    }
409
410    private static native boolean nIsBackBufferPreserved();
411
412    /**
413     * Indicates that the specified hardware layer needs to be updated
414     * as soon as possible.
415     *
416     * @param layer The hardware layer that needs an update
417     */
418    abstract void pushLayerUpdate(HardwareLayer layer);
419
420    /**
421     * Interface used to receive callbacks whenever a view is drawn by
422     * a hardware renderer instance.
423     */
424    interface HardwareDrawCallbacks {
425        /**
426         * Invoked before a view is drawn by a hardware renderer.
427         *
428         * @param canvas The Canvas used to render the view.
429         */
430        void onHardwarePreDraw(HardwareCanvas canvas);
431
432        /**
433         * Invoked after a view is drawn by a hardware renderer.
434         *
435         * @param canvas The Canvas used to render the view.
436         */
437        void onHardwarePostDraw(HardwareCanvas canvas);
438    }
439
440    /**
441     * Draws the specified view.
442     *
443     * @param view The view to draw.
444     * @param attachInfo AttachInfo tied to the specified view.
445     * @param callbacks Callbacks invoked when drawing happens.
446     * @param dirty The dirty rectangle to update, can be null.
447     *
448     * @return true if the dirty rect was ignored, false otherwise
449     */
450    abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
451            Rect dirty);
452
453    /**
454     * Creates a new display list that can be used to record batches of
455     * drawing operations.
456     *
457     * @param name The name of the display list, used for debugging purpose.
458     *             May be null
459     *
460     * @return A new display list.
461     */
462    public abstract DisplayList createDisplayList(String name);
463
464    /**
465     * Creates a new hardware layer. A hardware layer built by calling this
466     * method will be treated as a texture layer, instead of as a render target.
467     *
468     * @param isOpaque Whether the layer should be opaque or not
469     *
470     * @return A hardware layer
471     */
472    abstract HardwareLayer createHardwareLayer(boolean isOpaque);
473
474    /**
475     * Creates a new hardware layer.
476     *
477     * @param width The minimum width of the layer
478     * @param height The minimum height of the layer
479     * @param isOpaque Whether the layer should be opaque or not
480     *
481     * @return A hardware layer
482     */
483    abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
484
485    /**
486     * Creates a new {@link SurfaceTexture} that can be used to render into the
487     * specified hardware layer.
488     *
489     *
490     * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
491     *
492     * @return A {@link SurfaceTexture}
493     */
494    abstract SurfaceTexture createSurfaceTexture(HardwareLayer layer);
495
496    /**
497     * Sets the {@link android.graphics.SurfaceTexture} that will be used to
498     * render into the specified hardware layer.
499     *
500     * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
501     * @param surfaceTexture The {@link android.graphics.SurfaceTexture} to use for the layer
502     */
503    abstract void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture);
504
505    /**
506     * Detaches the specified functor from the current functor execution queue.
507     *
508     * @param functor The native functor to remove from the execution queue.
509     *
510     * @see HardwareCanvas#callDrawGLFunction(int)
511     * @see #attachFunctor(android.view.View.AttachInfo, int)
512     */
513    abstract void detachFunctor(int functor);
514
515    /**
516     * Schedules the specified functor in the functors execution queue.
517     *
518     * @param attachInfo AttachInfo tied to this renderer.
519     * @param functor The native functor to insert in the execution queue.
520     *
521     * @see HardwareCanvas#callDrawGLFunction(int)
522     * @see #detachFunctor(int)
523     *
524     * @return true if the functor was attached successfully
525     */
526    abstract boolean attachFunctor(View.AttachInfo attachInfo, int functor);
527
528    /**
529     * Initializes the hardware renderer for the specified surface and setup the
530     * renderer for drawing, if needed. This is invoked when the ViewAncestor has
531     * potentially lost the hardware renderer. The hardware renderer should be
532     * reinitialized and setup when the render {@link #isRequested()} and
533     * {@link #isEnabled()}.
534     *
535     * @param width The width of the drawing surface.
536     * @param height The height of the drawing surface.
537     * @param surface The surface to hardware accelerate
538     *
539     * @return true if the surface was initialized, false otherwise. Returning
540     *         false might mean that the surface was already initialized.
541     */
542    boolean initializeIfNeeded(int width, int height, Surface surface)
543            throws Surface.OutOfResourcesException {
544        if (isRequested()) {
545            // We lost the gl context, so recreate it.
546            if (!isEnabled()) {
547                if (initialize(surface)) {
548                    setup(width, height);
549                    return true;
550                }
551            }
552        }
553        return false;
554    }
555
556    /**
557     * Creates a hardware renderer using OpenGL.
558     *
559     * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.)
560     * @param translucent True if the surface is translucent, false otherwise
561     *
562     * @return A hardware renderer backed by OpenGL.
563     */
564    static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
565        switch (glVersion) {
566            case 2:
567                return Gl20Renderer.create(translucent);
568        }
569        throw new IllegalArgumentException("Unknown GL version: " + glVersion);
570    }
571
572    /**
573     * Invoke this method when the system is running out of memory. This
574     * method will attempt to recover as much memory as possible, based on
575     * the specified hint.
576     *
577     * @param level Hint about the amount of memory that should be trimmed,
578     *              see {@link android.content.ComponentCallbacks}
579     */
580    static void trimMemory(int level) {
581        startTrimMemory(level);
582        endTrimMemory();
583    }
584
585    /**
586     * Starts the process of trimming memory. Usually this call will setup
587     * hardware rendering context and reclaim memory.Extra cleanup might
588     * be required by calling {@link #endTrimMemory()}.
589     *
590     * @param level Hint about the amount of memory that should be trimmed,
591     *              see {@link android.content.ComponentCallbacks}
592     */
593    static void startTrimMemory(int level) {
594        Gl20Renderer.startTrimMemory(level);
595    }
596
597    /**
598     * Finishes the process of trimming memory. This method will usually
599     * cleanup special resources used by the memory trimming process.
600     */
601    static void endTrimMemory() {
602        Gl20Renderer.endTrimMemory();
603    }
604
605    /**
606     * Indicates whether hardware acceleration is currently enabled.
607     *
608     * @return True if hardware acceleration is in use, false otherwise.
609     */
610    boolean isEnabled() {
611        return mEnabled;
612    }
613
614    /**
615     * Indicates whether hardware acceleration is currently enabled.
616     *
617     * @param enabled True if the hardware renderer is in use, false otherwise.
618     */
619    void setEnabled(boolean enabled) {
620        mEnabled = enabled;
621    }
622
623    /**
624     * Indicates whether hardware acceleration is currently request but not
625     * necessarily enabled yet.
626     *
627     * @return True if requested, false otherwise.
628     */
629    boolean isRequested() {
630        return mRequested;
631    }
632
633    /**
634     * Indicates whether hardware acceleration is currently requested but not
635     * necessarily enabled yet.
636     *
637     * @return True to request hardware acceleration, false otherwise.
638     */
639    void setRequested(boolean requested) {
640        mRequested = requested;
641    }
642
643    /**
644     * Describes a series of frames that should be drawn on screen as a graph.
645     * Each frame is composed of 1 or more elements.
646     */
647    abstract class GraphDataProvider {
648        /**
649         * Draws the graph as bars. Frame elements are stacked on top of
650         * each other.
651         */
652        public static final int GRAPH_TYPE_BARS = 0;
653        /**
654         * Draws the graph as lines. The number of series drawn corresponds
655         * to the number of elements.
656         */
657        public static final int GRAPH_TYPE_LINES = 1;
658
659        /**
660         * Returns the type of graph to render.
661         *
662         * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES}
663         */
664        abstract int getGraphType();
665
666        /**
667         * This method is invoked before the graph is drawn. This method
668         * can be used to compute sizes, etc.
669         *
670         * @param metrics The display metrics
671         */
672        abstract void prepare(DisplayMetrics metrics);
673
674        /**
675         * @return The size in pixels of a vertical unit.
676         */
677        abstract int getVerticalUnitSize();
678
679        /**
680         * @return The size in pixels of a horizontal unit.
681         */
682        abstract int getHorizontalUnitSize();
683
684        /**
685         * @return The size in pixels of the margin between horizontal units.
686         */
687        abstract int getHorizontaUnitMargin();
688
689        /**
690         * An optional threshold value.
691         *
692         * @return A value >= 0 to draw the threshold, a negative value
693         *         to ignore it.
694         */
695        abstract float getThreshold();
696
697        /**
698         * The data to draw in the graph. The number of elements in the
699         * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}.
700         * If a value is negative the following values will be ignored.
701         */
702        abstract float[] getData();
703
704        /**
705         * Returns the number of frames to render in the graph.
706         */
707        abstract int getFrameCount();
708
709        /**
710         * Returns the number of elements in each frame. This directly affects
711         * the number of series drawn in the graph.
712         */
713        abstract int getElementCount();
714
715        /**
716         * Returns the current frame, if any. If the returned value is negative
717         * the current frame is ignored.
718         */
719        abstract int getCurrentFrame();
720
721        /**
722         * Prepares the paint to draw the specified element (or series.)
723         */
724        abstract void setupGraphPaint(Paint paint, int elementIndex);
725
726        /**
727         * Prepares the paint to draw the threshold.
728         */
729        abstract void setupThresholdPaint(Paint paint);
730
731        /**
732         * Prepares the paint to draw the current frame indicator.
733         */
734        abstract void setupCurrentFramePaint(Paint paint);
735    }
736
737    @SuppressWarnings({"deprecation"})
738    static abstract class GlRenderer extends HardwareRenderer {
739        static final int SURFACE_STATE_ERROR = 0;
740        static final int SURFACE_STATE_SUCCESS = 1;
741        static final int SURFACE_STATE_UPDATED = 2;
742
743        static final int FUNCTOR_PROCESS_DELAY = 4;
744
745        private static final int PROFILE_DRAW_MARGIN = 0;
746        private static final int PROFILE_DRAW_WIDTH = 3;
747        private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
748        private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
749        private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
750        private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
751        private static final int PROFILE_DRAW_DP_PER_MS = 7;
752
753        static EGL10 sEgl;
754        static EGLDisplay sEglDisplay;
755        static EGLConfig sEglConfig;
756        static final Object[] sEglLock = new Object[0];
757        int mWidth = -1, mHeight = -1;
758
759        static final ThreadLocal<ManagedEGLContext> sEglContextStorage
760                = new ThreadLocal<ManagedEGLContext>();
761
762        EGLContext mEglContext;
763        Thread mEglThread;
764
765        EGLSurface mEglSurface;
766
767        GL mGl;
768        HardwareCanvas mCanvas;
769
770        long mFrameCount;
771        Paint mDebugPaint;
772
773        static boolean sDirtyRegions;
774        static final boolean sDirtyRegionsRequested;
775        static {
776            String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
777            //noinspection PointlessBooleanExpression,ConstantConditions
778            sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
779            sDirtyRegionsRequested = sDirtyRegions;
780        }
781
782        boolean mDirtyRegionsEnabled;
783        boolean mUpdateDirtyRegions;
784
785        boolean mProfileEnabled;
786        int mProfileVisualizerType = -1;
787        float[] mProfileData;
788        ReentrantLock mProfileLock;
789        int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
790
791        GraphDataProvider mDebugDataProvider;
792        float[][] mProfileShapes;
793        Paint mProfilePaint;
794
795        boolean mDebugDirtyRegions;
796        boolean mShowOverdraw;
797
798        final int mGlVersion;
799        final boolean mTranslucent;
800
801        private boolean mDestroyed;
802
803        private final Rect mRedrawClip = new Rect();
804
805        private final int[] mSurfaceSize = new int[2];
806        private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
807
808        GlRenderer(int glVersion, boolean translucent) {
809            mGlVersion = glVersion;
810            mTranslucent = translucent;
811
812            loadSystemProperties(null);
813        }
814
815        private static final String[] VISUALIZERS = {
816                PROFILE_PROPERTY_VISUALIZE_BARS,
817                PROFILE_PROPERTY_VISUALIZE_LINES
818        };
819
820        @Override
821        boolean loadSystemProperties(Surface surface) {
822            boolean value;
823            boolean changed = false;
824
825            String profiling = SystemProperties.get(PROFILE_PROPERTY);
826            int graphType = Arrays.binarySearch(VISUALIZERS, profiling);
827            value = graphType >= 0;
828
829            if (graphType != mProfileVisualizerType) {
830                changed = true;
831                mProfileVisualizerType = graphType;
832
833                mProfileShapes = null;
834                mProfilePaint = null;
835
836                if (value) {
837                    mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
838                } else {
839                    mDebugDataProvider = null;
840                }
841            }
842
843            // If on-screen profiling is not enabled, we need to check whether
844            // console profiling only is enabled
845            if (!value) {
846                value = Boolean.parseBoolean(profiling);
847            }
848
849            if (value != mProfileEnabled) {
850                changed = true;
851                mProfileEnabled = value;
852
853                if (mProfileEnabled) {
854                    Log.d(LOG_TAG, "Profiling hardware renderer");
855
856                    int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
857                            PROFILE_MAX_FRAMES);
858                    mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
859                    for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
860                        mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
861                    }
862
863                    mProfileLock = new ReentrantLock();
864                } else {
865                    mProfileData = null;
866                    mProfileLock = null;
867                    mProfileVisualizerType = -1;
868                }
869
870                mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
871            }
872
873            value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
874            if (value != mDebugDirtyRegions) {
875                changed = true;
876                mDebugDirtyRegions = value;
877
878                if (mDebugDirtyRegions) {
879                    Log.d(LOG_TAG, "Debugging dirty regions");
880                }
881            }
882
883            value = SystemProperties.getBoolean(
884                    HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY, false);
885            if (value != mShowOverdraw) {
886                changed = true;
887                mShowOverdraw = value;
888
889                if (!REGION_CLIPPING_ENABLED) {
890                    if (surface != null && isEnabled()) {
891                        if (validate()) {
892                            sEglConfig = loadEglConfig();
893                            invalidate(surface);
894                        }
895                    }
896                }
897            }
898
899            if (nLoadProperties()) {
900                changed = true;
901            }
902
903            return changed;
904        }
905
906        @Override
907        void dumpGfxInfo(PrintWriter pw) {
908            if (mProfileEnabled) {
909                pw.printf("\n\tDraw\tProcess\tExecute\n");
910
911                mProfileLock.lock();
912                try {
913                    for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
914                        if (mProfileData[i] < 0) {
915                            break;
916                        }
917                        pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
918                                mProfileData[i + 2]);
919                        mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
920                    }
921                    mProfileCurrentFrame = mProfileData.length;
922                } finally {
923                    mProfileLock.unlock();
924                }
925            }
926        }
927
928        @Override
929        long getFrameCount() {
930            return mFrameCount;
931        }
932
933        /**
934         * Indicates whether this renderer instance can track and update dirty regions.
935         */
936        boolean hasDirtyRegions() {
937            return mDirtyRegionsEnabled;
938        }
939
940        /**
941         * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
942         * is invoked and the requested flag is turned off. The error code is
943         * also logged as a warning.
944         */
945        void checkEglErrors() {
946            if (isEnabled()) {
947                checkEglErrorsForced();
948            }
949        }
950
951        private void checkEglErrorsForced() {
952            int error = sEgl.eglGetError();
953            if (error != EGL_SUCCESS) {
954                // something bad has happened revert to
955                // normal rendering.
956                Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
957                fallback(error != EGL11.EGL_CONTEXT_LOST);
958            }
959        }
960
961        private void fallback(boolean fallback) {
962            destroy(true);
963            if (fallback) {
964                // we'll try again if it was context lost
965                setRequested(false);
966                Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
967                        + "Switching back to software rendering.");
968            }
969        }
970
971        @Override
972        boolean initialize(Surface surface) throws Surface.OutOfResourcesException {
973            if (isRequested() && !isEnabled()) {
974                initializeEgl();
975                mGl = createEglSurface(surface);
976                mDestroyed = false;
977
978                if (mGl != null) {
979                    int err = sEgl.eglGetError();
980                    if (err != EGL_SUCCESS) {
981                        destroy(true);
982                        setRequested(false);
983                    } else {
984                        if (mCanvas == null) {
985                            mCanvas = createCanvas();
986                        }
987                        if (mCanvas != null) {
988                            setEnabled(true);
989                        } else {
990                            Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
991                        }
992                    }
993
994                    return mCanvas != null;
995                }
996            }
997            return false;
998        }
999
1000        @Override
1001        void updateSurface(Surface surface) throws Surface.OutOfResourcesException {
1002            if (isRequested() && isEnabled()) {
1003                createEglSurface(surface);
1004            }
1005        }
1006
1007        abstract HardwareCanvas createCanvas();
1008
1009        abstract int[] getConfig(boolean dirtyRegions);
1010
1011        void initializeEgl() {
1012            synchronized (sEglLock) {
1013                if (sEgl == null && sEglConfig == null) {
1014                    sEgl = (EGL10) EGLContext.getEGL();
1015
1016                    // Get to the default display.
1017                    sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
1018
1019                    if (sEglDisplay == EGL_NO_DISPLAY) {
1020                        throw new RuntimeException("eglGetDisplay failed "
1021                                + GLUtils.getEGLErrorString(sEgl.eglGetError()));
1022                    }
1023
1024                    // We can now initialize EGL for that display
1025                    int[] version = new int[2];
1026                    if (!sEgl.eglInitialize(sEglDisplay, version)) {
1027                        throw new RuntimeException("eglInitialize failed " +
1028                                GLUtils.getEGLErrorString(sEgl.eglGetError()));
1029                    }
1030
1031                    checkEglErrorsForced();
1032
1033                    sEglConfig = loadEglConfig();
1034                }
1035            }
1036
1037            ManagedEGLContext managedContext = sEglContextStorage.get();
1038            mEglContext = managedContext != null ? managedContext.getContext() : null;
1039            mEglThread = Thread.currentThread();
1040
1041            if (mEglContext == null) {
1042                mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
1043                sEglContextStorage.set(createManagedContext(mEglContext));
1044            }
1045        }
1046
1047        private EGLConfig loadEglConfig() {
1048            EGLConfig eglConfig = chooseEglConfig();
1049            if (eglConfig == null) {
1050                // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
1051                if (sDirtyRegions) {
1052                    sDirtyRegions = false;
1053                    eglConfig = chooseEglConfig();
1054                    if (eglConfig == null) {
1055                        throw new RuntimeException("eglConfig not initialized");
1056                    }
1057                } else {
1058                    throw new RuntimeException("eglConfig not initialized");
1059                }
1060            }
1061            return eglConfig;
1062        }
1063
1064        abstract ManagedEGLContext createManagedContext(EGLContext eglContext);
1065
1066        private EGLConfig chooseEglConfig() {
1067            EGLConfig[] configs = new EGLConfig[1];
1068            int[] configsCount = new int[1];
1069            int[] configSpec = getConfig(sDirtyRegions);
1070
1071            // Debug
1072            final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
1073            if ("all".equalsIgnoreCase(debug)) {
1074                sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
1075
1076                EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
1077                sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
1078                        configsCount[0], configsCount);
1079
1080                for (EGLConfig config : debugConfigs) {
1081                    printConfig(config);
1082                }
1083            }
1084
1085            if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
1086                throw new IllegalArgumentException("eglChooseConfig failed " +
1087                        GLUtils.getEGLErrorString(sEgl.eglGetError()));
1088            } else if (configsCount[0] > 0) {
1089                if ("choice".equalsIgnoreCase(debug)) {
1090                    printConfig(configs[0]);
1091                }
1092                return configs[0];
1093            }
1094
1095            return null;
1096        }
1097
1098        private static void printConfig(EGLConfig config) {
1099            int[] value = new int[1];
1100
1101            Log.d(LOG_TAG, "EGL configuration " + config + ":");
1102
1103            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
1104            Log.d(LOG_TAG, "  RED_SIZE = " + value[0]);
1105
1106            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
1107            Log.d(LOG_TAG, "  GREEN_SIZE = " + value[0]);
1108
1109            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
1110            Log.d(LOG_TAG, "  BLUE_SIZE = " + value[0]);
1111
1112            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
1113            Log.d(LOG_TAG, "  ALPHA_SIZE = " + value[0]);
1114
1115            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
1116            Log.d(LOG_TAG, "  DEPTH_SIZE = " + value[0]);
1117
1118            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
1119            Log.d(LOG_TAG, "  STENCIL_SIZE = " + value[0]);
1120
1121            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value);
1122            Log.d(LOG_TAG, "  SAMPLE_BUFFERS = " + value[0]);
1123
1124            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value);
1125            Log.d(LOG_TAG, "  SAMPLES = " + value[0]);
1126
1127            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
1128            Log.d(LOG_TAG, "  SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
1129
1130            sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value);
1131            Log.d(LOG_TAG, "  CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0]));
1132        }
1133
1134        GL createEglSurface(Surface surface) throws Surface.OutOfResourcesException {
1135            // Check preconditions.
1136            if (sEgl == null) {
1137                throw new RuntimeException("egl not initialized");
1138            }
1139            if (sEglDisplay == null) {
1140                throw new RuntimeException("eglDisplay not initialized");
1141            }
1142            if (sEglConfig == null) {
1143                throw new RuntimeException("eglConfig not initialized");
1144            }
1145            if (Thread.currentThread() != mEglThread) {
1146                throw new IllegalStateException("HardwareRenderer cannot be used "
1147                        + "from multiple threads");
1148            }
1149
1150            // In case we need to destroy an existing surface
1151            destroySurface();
1152
1153            // Create an EGL surface we can render into.
1154            if (!createSurface(surface)) {
1155                return null;
1156            }
1157
1158            initCaches();
1159
1160            return mEglContext.getGL();
1161        }
1162
1163        private void enableDirtyRegions() {
1164            // If mDirtyRegions is set, this means we have an EGL configuration
1165            // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
1166            if (sDirtyRegions) {
1167                if (!(mDirtyRegionsEnabled = preserveBackBuffer())) {
1168                    Log.w(LOG_TAG, "Backbuffer cannot be preserved");
1169                }
1170            } else if (sDirtyRegionsRequested) {
1171                // If mDirtyRegions is not set, our EGL configuration does not
1172                // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
1173                // swap behavior might be EGL_BUFFER_PRESERVED, which means we
1174                // want to set mDirtyRegions. We try to do this only if dirty
1175                // regions were initially requested as part of the device
1176                // configuration (see RENDER_DIRTY_REGIONS)
1177                mDirtyRegionsEnabled = isBackBufferPreserved();
1178            }
1179        }
1180
1181        abstract void initCaches();
1182
1183        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
1184            int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
1185
1186            EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
1187                    mGlVersion != 0 ? attribs : null);
1188            if (context == null || context == EGL_NO_CONTEXT) {
1189                //noinspection ConstantConditions
1190                throw new IllegalStateException(
1191                        "Could not create an EGL context. eglCreateContext failed with error: " +
1192                        GLUtils.getEGLErrorString(sEgl.eglGetError()));
1193            }
1194            return context;
1195        }
1196
1197        @Override
1198        void destroy(boolean full) {
1199            if (full && mCanvas != null) {
1200                mCanvas = null;
1201            }
1202
1203            if (!isEnabled() || mDestroyed) {
1204                setEnabled(false);
1205                return;
1206            }
1207
1208            destroySurface();
1209            setEnabled(false);
1210
1211            mDestroyed = true;
1212            mGl = null;
1213        }
1214
1215        void destroySurface() {
1216            if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
1217                sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1218                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
1219                mEglSurface = null;
1220            }
1221        }
1222
1223        @Override
1224        void invalidate(Surface surface) {
1225            // Cancels any existing buffer to ensure we'll get a buffer
1226            // of the right size before we call eglSwapBuffers
1227            sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1228
1229            if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
1230                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
1231                mEglSurface = null;
1232                setEnabled(false);
1233            }
1234
1235            if (surface.isValid()) {
1236                if (!createSurface(surface)) {
1237                    return;
1238                }
1239
1240                mUpdateDirtyRegions = true;
1241
1242                if (mCanvas != null) {
1243                    setEnabled(true);
1244                }
1245            }
1246        }
1247
1248        private boolean createSurface(Surface surface) {
1249            mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null);
1250
1251            if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
1252                int error = sEgl.eglGetError();
1253                if (error == EGL_BAD_NATIVE_WINDOW) {
1254                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
1255                    return false;
1256                }
1257                throw new RuntimeException("createWindowSurface failed "
1258                        + GLUtils.getEGLErrorString(error));
1259            }
1260
1261            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
1262                throw new IllegalStateException("eglMakeCurrent failed " +
1263                        GLUtils.getEGLErrorString(sEgl.eglGetError()));
1264            }
1265
1266            enableDirtyRegions();
1267
1268            return true;
1269        }
1270
1271        @Override
1272        boolean validate() {
1273            return checkCurrent() != SURFACE_STATE_ERROR;
1274        }
1275
1276        @Override
1277        void setup(int width, int height) {
1278            if (validate()) {
1279                mCanvas.setViewport(width, height);
1280                mWidth = width;
1281                mHeight = height;
1282            }
1283        }
1284
1285        @Override
1286        int getWidth() {
1287            return mWidth;
1288        }
1289
1290        @Override
1291        int getHeight() {
1292            return mHeight;
1293        }
1294
1295        @Override
1296        HardwareCanvas getCanvas() {
1297            return mCanvas;
1298        }
1299
1300        boolean canDraw() {
1301            return mGl != null && mCanvas != null;
1302        }
1303
1304        int onPreDraw(Rect dirty) {
1305            return DisplayList.STATUS_DONE;
1306        }
1307
1308        void onPostDraw() {
1309        }
1310
1311        class FunctorsRunnable implements Runnable {
1312            View.AttachInfo attachInfo;
1313
1314            @Override
1315            public void run() {
1316                final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
1317                if (renderer == null || !renderer.isEnabled() || renderer != GlRenderer.this) {
1318                    return;
1319                }
1320
1321                final int surfaceState = checkCurrent();
1322                if (surfaceState != SURFACE_STATE_ERROR) {
1323                    int status = mCanvas.invokeFunctors(mRedrawClip);
1324                    handleFunctorStatus(attachInfo, status);
1325                }
1326            }
1327        }
1328
1329        @Override
1330        boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
1331                Rect dirty) {
1332            if (canDraw()) {
1333                if (!hasDirtyRegions()) {
1334                    dirty = null;
1335                }
1336                attachInfo.mIgnoreDirtyState = true;
1337                attachInfo.mDrawingTime = SystemClock.uptimeMillis();
1338
1339                view.mPrivateFlags |= View.PFLAG_DRAWN;
1340
1341                final int surfaceState = checkCurrent();
1342                if (surfaceState != SURFACE_STATE_ERROR) {
1343                    HardwareCanvas canvas = mCanvas;
1344                    attachInfo.mHardwareCanvas = canvas;
1345
1346                    if (mProfileEnabled) {
1347                        mProfileLock.lock();
1348                    }
1349
1350                    dirty = beginFrame(canvas, dirty, surfaceState);
1351
1352                    int saveCount = 0;
1353                    int status = DisplayList.STATUS_DONE;
1354
1355                    try {
1356                        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
1357                                == View.PFLAG_INVALIDATED;
1358                        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
1359
1360                        long buildDisplayListStartTime = startBuildDisplayListProfiling();
1361                        canvas.clearLayerUpdates();
1362
1363                        DisplayList displayList = buildDisplayList(view);
1364                        status = prepareFrame(dirty);
1365
1366                        saveCount = canvas.save();
1367                        callbacks.onHardwarePreDraw(canvas);
1368
1369                        endBuildDisplayListProfiling(buildDisplayListStartTime);
1370
1371                        if (displayList != null) {
1372                            status = drawDisplayList(attachInfo, canvas, displayList, status);
1373                        } else {
1374                            // Shouldn't reach here
1375                            view.draw(canvas);
1376                        }
1377                    } finally {
1378                        callbacks.onHardwarePostDraw(canvas);
1379                        canvas.restoreToCount(saveCount);
1380                        view.mRecreateDisplayList = false;
1381
1382                        mFrameCount++;
1383
1384                        debugDirtyRegions(dirty, canvas);
1385                        drawProfileData(attachInfo);
1386                    }
1387
1388                    onPostDraw();
1389
1390                    swapBuffers(status);
1391
1392                    if (mProfileEnabled) {
1393                        mProfileLock.unlock();
1394                    }
1395
1396                    attachInfo.mIgnoreDirtyState = false;
1397                    return dirty == null;
1398                }
1399            }
1400
1401            return false;
1402        }
1403
1404        abstract void drawProfileData(View.AttachInfo attachInfo);
1405
1406        private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
1407            // We had to change the current surface and/or context, redraw everything
1408            if (surfaceState == SURFACE_STATE_UPDATED) {
1409                dirty = null;
1410                beginFrame(null);
1411            } else {
1412                int[] size = mSurfaceSize;
1413                beginFrame(size);
1414
1415                if (size[1] != mHeight || size[0] != mWidth) {
1416                    mWidth = size[0];
1417                    mHeight = size[1];
1418
1419                    canvas.setViewport(mWidth, mHeight);
1420
1421                    dirty = null;
1422                }
1423            }
1424
1425            if (mDebugDataProvider != null) dirty = null;
1426
1427            return dirty;
1428        }
1429
1430        private long startBuildDisplayListProfiling() {
1431            if (mProfileEnabled) {
1432                mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
1433                if (mProfileCurrentFrame >= mProfileData.length) {
1434                    mProfileCurrentFrame = 0;
1435                }
1436
1437                return System.nanoTime();
1438            }
1439            return 0;
1440        }
1441
1442        private void endBuildDisplayListProfiling(long getDisplayListStartTime) {
1443            if (mProfileEnabled) {
1444                long now = System.nanoTime();
1445                float total = (now - getDisplayListStartTime) * 0.000001f;
1446                //noinspection PointlessArithmeticExpression
1447                mProfileData[mProfileCurrentFrame] = total;
1448            }
1449        }
1450
1451        private static DisplayList buildDisplayList(View view) {
1452            DisplayList displayList;
1453            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
1454            try {
1455                displayList = view.getDisplayList();
1456            } finally {
1457                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1458            }
1459            return displayList;
1460        }
1461
1462        private int prepareFrame(Rect dirty) {
1463            int status;
1464            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
1465            try {
1466                status = onPreDraw(dirty);
1467            } finally {
1468                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1469            }
1470            return status;
1471        }
1472
1473        private int drawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas,
1474                DisplayList displayList, int status) {
1475
1476            long drawDisplayListStartTime = 0;
1477            if (mProfileEnabled) {
1478                drawDisplayListStartTime = System.nanoTime();
1479            }
1480
1481            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
1482            try {
1483                status |= canvas.drawDisplayList(displayList, mRedrawClip,
1484                        DisplayList.FLAG_CLIP_CHILDREN);
1485            } finally {
1486                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1487            }
1488
1489            if (mProfileEnabled) {
1490                long now = System.nanoTime();
1491                float total = (now - drawDisplayListStartTime) * 0.000001f;
1492                mProfileData[mProfileCurrentFrame + 1] = total;
1493            }
1494
1495            handleFunctorStatus(attachInfo, status);
1496            return status;
1497        }
1498
1499        private void swapBuffers(int status) {
1500            if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
1501                long eglSwapBuffersStartTime = 0;
1502                if (mProfileEnabled) {
1503                    eglSwapBuffersStartTime = System.nanoTime();
1504                }
1505
1506                sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
1507
1508                if (mProfileEnabled) {
1509                    long now = System.nanoTime();
1510                    float total = (now - eglSwapBuffersStartTime) * 0.000001f;
1511                    mProfileData[mProfileCurrentFrame + 2] = total;
1512                }
1513
1514                checkEglErrors();
1515            }
1516        }
1517
1518        private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
1519            if (mDebugDirtyRegions) {
1520                if (mDebugPaint == null) {
1521                    mDebugPaint = new Paint();
1522                    mDebugPaint.setColor(0x7fff0000);
1523                }
1524
1525                if (dirty != null && (mFrameCount & 1) == 0) {
1526                    canvas.drawRect(dirty, mDebugPaint);
1527                }
1528            }
1529        }
1530
1531        private void handleFunctorStatus(View.AttachInfo attachInfo, int status) {
1532            // If the draw flag is set, functors will be invoked while executing
1533            // the tree of display lists
1534            if ((status & DisplayList.STATUS_DRAW) != 0) {
1535                if (mRedrawClip.isEmpty()) {
1536                    attachInfo.mViewRootImpl.invalidate();
1537                } else {
1538                    attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip);
1539                    mRedrawClip.setEmpty();
1540                }
1541            }
1542
1543            if ((status & DisplayList.STATUS_INVOKE) != 0 ||
1544                    attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) {
1545                attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
1546                mFunctorsRunnable.attachInfo = attachInfo;
1547                attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY);
1548            }
1549        }
1550
1551        @Override
1552        void detachFunctor(int functor) {
1553            if (mCanvas != null) {
1554                mCanvas.detachFunctor(functor);
1555            }
1556        }
1557
1558        @Override
1559        boolean attachFunctor(View.AttachInfo attachInfo, int functor) {
1560            if (mCanvas != null) {
1561                mCanvas.attachFunctor(functor);
1562                mFunctorsRunnable.attachInfo = attachInfo;
1563                attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
1564                attachInfo.mHandler.postDelayed(mFunctorsRunnable,  0);
1565                return true;
1566            }
1567            return false;
1568        }
1569
1570        /**
1571         * Ensures the current EGL context is the one we expect.
1572         *
1573         * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
1574         *         {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
1575         *         {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
1576         */
1577        int checkCurrent() {
1578            if (mEglThread != Thread.currentThread()) {
1579                throw new IllegalStateException("Hardware acceleration can only be used with a " +
1580                        "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
1581                        "Current thread: " + Thread.currentThread());
1582            }
1583
1584            if (!mEglContext.equals(sEgl.eglGetCurrentContext()) ||
1585                    !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
1586                if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
1587                    Log.e(LOG_TAG, "eglMakeCurrent failed " +
1588                            GLUtils.getEGLErrorString(sEgl.eglGetError()));
1589                    fallback(true);
1590                    return SURFACE_STATE_ERROR;
1591                } else {
1592                    if (mUpdateDirtyRegions) {
1593                        enableDirtyRegions();
1594                        mUpdateDirtyRegions = false;
1595                    }
1596                    return SURFACE_STATE_UPDATED;
1597                }
1598            }
1599            return SURFACE_STATE_SUCCESS;
1600        }
1601
1602        private static int dpToPx(int dp, float density) {
1603            return (int) (dp * density + 0.5f);
1604        }
1605
1606        class DrawPerformanceDataProvider extends GraphDataProvider {
1607            private final int mGraphType;
1608
1609            private int mVerticalUnit;
1610            private int mHorizontalUnit;
1611            private int mHorizontalMargin;
1612            private int mThresholdStroke;
1613
1614            DrawPerformanceDataProvider(int graphType) {
1615                mGraphType = graphType;
1616            }
1617
1618            @Override
1619            void prepare(DisplayMetrics metrics) {
1620                final float density = metrics.density;
1621
1622                mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
1623                mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
1624                mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
1625                mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
1626            }
1627
1628            @Override
1629            int getGraphType() {
1630                return mGraphType;
1631            }
1632
1633            @Override
1634            int getVerticalUnitSize() {
1635                return mVerticalUnit;
1636            }
1637
1638            @Override
1639            int getHorizontalUnitSize() {
1640                return mHorizontalUnit;
1641            }
1642
1643            @Override
1644            int getHorizontaUnitMargin() {
1645                return mHorizontalMargin;
1646            }
1647
1648            @Override
1649            float[] getData() {
1650                return mProfileData;
1651            }
1652
1653            @Override
1654            float getThreshold() {
1655                return 16;
1656            }
1657
1658            @Override
1659            int getFrameCount() {
1660                return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
1661            }
1662
1663            @Override
1664            int getElementCount() {
1665                return PROFILE_FRAME_DATA_COUNT;
1666            }
1667
1668            @Override
1669            int getCurrentFrame() {
1670                return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
1671            }
1672
1673            @Override
1674            void setupGraphPaint(Paint paint, int elementIndex) {
1675                paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
1676                if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
1677            }
1678
1679            @Override
1680            void setupThresholdPaint(Paint paint) {
1681                paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
1682                paint.setStrokeWidth(mThresholdStroke);
1683            }
1684
1685            @Override
1686            void setupCurrentFramePaint(Paint paint) {
1687                paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
1688                if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
1689            }
1690        }
1691    }
1692
1693    /**
1694     * Hardware renderer using OpenGL ES 2.0.
1695     */
1696    static class Gl20Renderer extends GlRenderer {
1697        private GLES20Canvas mGlCanvas;
1698
1699        private DisplayMetrics mDisplayMetrics;
1700
1701        private static EGLSurface sPbuffer;
1702        private static final Object[] sPbufferLock = new Object[0];
1703
1704        static class Gl20RendererEglContext extends ManagedEGLContext {
1705            final Handler mHandler = new Handler();
1706
1707            public Gl20RendererEglContext(EGLContext context) {
1708                super(context);
1709            }
1710
1711            @Override
1712            public void onTerminate(final EGLContext eglContext) {
1713                // Make sure we do this on the correct thread.
1714                if (mHandler.getLooper() != Looper.myLooper()) {
1715                    mHandler.post(new Runnable() {
1716                        @Override
1717                        public void run() {
1718                            onTerminate(eglContext);
1719                        }
1720                    });
1721                    return;
1722                }
1723
1724                synchronized (sEglLock) {
1725                    if (sEgl == null) return;
1726
1727                    if (EGLImpl.getInitCount(sEglDisplay) == 1) {
1728                        usePbufferSurface(eglContext);
1729                        GLES20Canvas.terminateCaches();
1730
1731                        sEgl.eglDestroyContext(sEglDisplay, eglContext);
1732                        sEglContextStorage.set(null);
1733                        sEglContextStorage.remove();
1734
1735                        sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
1736                        sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
1737                                EGL_NO_SURFACE, EGL_NO_CONTEXT);
1738
1739                        sEgl.eglReleaseThread();
1740                        sEgl.eglTerminate(sEglDisplay);
1741
1742                        sEgl = null;
1743                        sEglDisplay = null;
1744                        sEglConfig = null;
1745                        sPbuffer = null;
1746                    }
1747                }
1748            }
1749        }
1750
1751        Gl20Renderer(boolean translucent) {
1752            super(2, translucent);
1753        }
1754
1755        @Override
1756        HardwareCanvas createCanvas() {
1757            return mGlCanvas = new GLES20Canvas(mTranslucent);
1758        }
1759
1760        @Override
1761        ManagedEGLContext createManagedContext(EGLContext eglContext) {
1762            return new Gl20Renderer.Gl20RendererEglContext(mEglContext);
1763        }
1764
1765        @Override
1766        int[] getConfig(boolean dirtyRegions) {
1767            //noinspection PointlessBooleanExpression
1768            final int stencilSize = mShowOverdraw || REGION_CLIPPING_ENABLED ?
1769                    GLES20Canvas.getStencilSize() : 0;
1770            final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
1771
1772            return new int[] {
1773                    EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
1774                    EGL_RED_SIZE, 8,
1775                    EGL_GREEN_SIZE, 8,
1776                    EGL_BLUE_SIZE, 8,
1777                    EGL_ALPHA_SIZE, 8,
1778                    EGL_DEPTH_SIZE, 0,
1779                    EGL_CONFIG_CAVEAT, EGL_NONE,
1780                    EGL_STENCIL_SIZE, stencilSize,
1781                    EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
1782                    EGL_NONE
1783            };
1784        }
1785
1786        @Override
1787        void initCaches() {
1788            GLES20Canvas.initCaches();
1789        }
1790
1791        @Override
1792        boolean canDraw() {
1793            return super.canDraw() && mGlCanvas != null;
1794        }
1795
1796        @Override
1797        int onPreDraw(Rect dirty) {
1798            return mGlCanvas.onPreDraw(dirty);
1799        }
1800
1801        @Override
1802        void onPostDraw() {
1803            mGlCanvas.onPostDraw();
1804        }
1805
1806        @Override
1807        void drawProfileData(View.AttachInfo attachInfo) {
1808            if (mDebugDataProvider != null) {
1809                final GraphDataProvider provider = mDebugDataProvider;
1810                initProfileDrawData(attachInfo, provider);
1811
1812                final int height = provider.getVerticalUnitSize();
1813                final int margin = provider.getHorizontaUnitMargin();
1814                final int width = provider.getHorizontalUnitSize();
1815
1816                int x = 0;
1817                int count = 0;
1818                int current = 0;
1819
1820                final float[] data = provider.getData();
1821                final int elementCount = provider.getElementCount();
1822                final int graphType = provider.getGraphType();
1823
1824                int totalCount = provider.getFrameCount() * elementCount;
1825                if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
1826                    totalCount -= elementCount;
1827                }
1828
1829                for (int i = 0; i < totalCount; i += elementCount) {
1830                    if (data[i] < 0.0f) break;
1831
1832                    int index = count * 4;
1833                    if (i == provider.getCurrentFrame() * elementCount) current = index;
1834
1835                    x += margin;
1836                    int x2 = x + width;
1837
1838                    int y2 = mHeight;
1839                    int y1 = (int) (y2 - data[i] * height);
1840
1841                    switch (graphType) {
1842                        case GraphDataProvider.GRAPH_TYPE_BARS: {
1843                            for (int j = 0; j < elementCount; j++) {
1844                                //noinspection MismatchedReadAndWriteOfArray
1845                                final float[] r = mProfileShapes[j];
1846                                r[index] = x;
1847                                r[index + 1] = y1;
1848                                r[index + 2] = x2;
1849                                r[index + 3] = y2;
1850
1851                                y2 = y1;
1852                                if (j < elementCount - 1) {
1853                                    y1 = (int) (y2 - data[i + j + 1] * height);
1854                                }
1855                            }
1856                        } break;
1857                        case GraphDataProvider.GRAPH_TYPE_LINES: {
1858                            for (int j = 0; j < elementCount; j++) {
1859                                //noinspection MismatchedReadAndWriteOfArray
1860                                final float[] r = mProfileShapes[j];
1861                                r[index] = (x + x2) * 0.5f;
1862                                r[index + 1] = index == 0 ? y1 : r[index - 1];
1863                                r[index + 2] = r[index] + width;
1864                                r[index + 3] = y1;
1865
1866                                y2 = y1;
1867                                if (j < elementCount - 1) {
1868                                    y1 = (int) (y2 - data[i + j + 1] * height);
1869                                }
1870                            }
1871                        } break;
1872                    }
1873
1874
1875                    x += width;
1876                    count++;
1877                }
1878
1879                x += margin;
1880
1881                drawGraph(graphType, count);
1882                drawCurrentFrame(graphType, current);
1883                drawThreshold(x, height);
1884            }
1885        }
1886
1887        private void drawGraph(int graphType, int count) {
1888            for (int i = 0; i < mProfileShapes.length; i++) {
1889                mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
1890                switch (graphType) {
1891                    case GraphDataProvider.GRAPH_TYPE_BARS:
1892                        mGlCanvas.drawRects(mProfileShapes[i], count, mProfilePaint);
1893                        break;
1894                    case GraphDataProvider.GRAPH_TYPE_LINES:
1895                        mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
1896                        break;
1897                }
1898            }
1899        }
1900
1901        private void drawCurrentFrame(int graphType, int index) {
1902            if (index >= 0) {
1903                mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
1904                switch (graphType) {
1905                    case GraphDataProvider.GRAPH_TYPE_BARS:
1906                        mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
1907                                mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
1908                                mProfilePaint);
1909                        break;
1910                    case GraphDataProvider.GRAPH_TYPE_LINES:
1911                        mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
1912                                mProfileShapes[2][index], mHeight, mProfilePaint);
1913                        break;
1914                }
1915            }
1916        }
1917
1918        private void drawThreshold(int x, int height) {
1919            float threshold = mDebugDataProvider.getThreshold();
1920            if (threshold > 0.0f) {
1921                mDebugDataProvider.setupThresholdPaint(mProfilePaint);
1922                int y = (int) (mHeight - threshold * height);
1923                mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
1924            }
1925        }
1926
1927        private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
1928            if (mProfileShapes == null) {
1929                final int elementCount = provider.getElementCount();
1930                final int frameCount = provider.getFrameCount();
1931
1932                mProfileShapes = new float[elementCount][];
1933                for (int i = 0; i < elementCount; i++) {
1934                    mProfileShapes[i] = new float[frameCount * 4];
1935                }
1936
1937                mProfilePaint = new Paint();
1938            }
1939
1940            mProfilePaint.reset();
1941            if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
1942                mProfilePaint.setAntiAlias(true);
1943            }
1944
1945            if (mDisplayMetrics == null) {
1946                mDisplayMetrics = new DisplayMetrics();
1947            }
1948
1949            attachInfo.mDisplay.getMetrics(mDisplayMetrics);
1950            provider.prepare(mDisplayMetrics);
1951        }
1952
1953        @Override
1954        void destroy(boolean full) {
1955            try {
1956                super.destroy(full);
1957            } finally {
1958                if (full && mGlCanvas != null) {
1959                    mGlCanvas = null;
1960                }
1961            }
1962        }
1963
1964        @Override
1965        void pushLayerUpdate(HardwareLayer layer) {
1966            mGlCanvas.pushLayerUpdate(layer);
1967        }
1968
1969        @Override
1970        public DisplayList createDisplayList(String name) {
1971            return new GLES20DisplayList(name);
1972        }
1973
1974        @Override
1975        HardwareLayer createHardwareLayer(boolean isOpaque) {
1976            return new GLES20TextureLayer(isOpaque);
1977        }
1978
1979        @Override
1980        HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
1981            return new GLES20RenderLayer(width, height, isOpaque);
1982        }
1983
1984        @Override
1985        SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
1986            return ((GLES20TextureLayer) layer).getSurfaceTexture();
1987        }
1988
1989        @Override
1990        void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
1991            ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture);
1992        }
1993
1994        @Override
1995        boolean safelyRun(Runnable action) {
1996            boolean needsContext = true;
1997            if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false;
1998
1999            if (needsContext) {
2000                Gl20RendererEglContext managedContext =
2001                        (Gl20RendererEglContext) sEglContextStorage.get();
2002                if (managedContext == null) return false;
2003                usePbufferSurface(managedContext.getContext());
2004            }
2005
2006            try {
2007                action.run();
2008            } finally {
2009                if (needsContext) {
2010                    sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
2011                            EGL_NO_SURFACE, EGL_NO_CONTEXT);
2012                }
2013            }
2014
2015            return true;
2016        }
2017
2018        @Override
2019        void destroyLayers(final View view) {
2020            if (view != null) {
2021                safelyRun(new Runnable() {
2022                    @Override
2023                    public void run() {
2024                        if (mCanvas != null) {
2025                            mCanvas.clearLayerUpdates();
2026                        }
2027                        destroyHardwareLayer(view);
2028                        GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
2029                    }
2030                });
2031            }
2032        }
2033
2034        private static void destroyHardwareLayer(View view) {
2035            view.destroyLayer(true);
2036
2037            if (view instanceof ViewGroup) {
2038                ViewGroup group = (ViewGroup) view;
2039
2040                int count = group.getChildCount();
2041                for (int i = 0; i < count; i++) {
2042                    destroyHardwareLayer(group.getChildAt(i));
2043                }
2044            }
2045        }
2046
2047        @Override
2048        void destroyHardwareResources(final View view) {
2049            if (view != null) {
2050                safelyRun(new Runnable() {
2051                    @Override
2052                    public void run() {
2053                        if (mCanvas != null) {
2054                            mCanvas.clearLayerUpdates();
2055                        }
2056                        destroyResources(view);
2057                        GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
2058                    }
2059                });
2060            }
2061        }
2062
2063        private static void destroyResources(View view) {
2064            view.destroyHardwareResources();
2065
2066            if (view instanceof ViewGroup) {
2067                ViewGroup group = (ViewGroup) view;
2068
2069                int count = group.getChildCount();
2070                for (int i = 0; i < count; i++) {
2071                    destroyResources(group.getChildAt(i));
2072                }
2073            }
2074        }
2075
2076        static HardwareRenderer create(boolean translucent) {
2077            if (GLES20Canvas.isAvailable()) {
2078                return new Gl20Renderer(translucent);
2079            }
2080            return null;
2081        }
2082
2083        static void startTrimMemory(int level) {
2084            if (sEgl == null || sEglConfig == null) return;
2085
2086            Gl20RendererEglContext managedContext =
2087                    (Gl20RendererEglContext) sEglContextStorage.get();
2088            // We do not have OpenGL objects
2089            if (managedContext == null) {
2090                return;
2091            } else {
2092                usePbufferSurface(managedContext.getContext());
2093            }
2094
2095            if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
2096                GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
2097            } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
2098                GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
2099            }
2100        }
2101
2102        static void endTrimMemory() {
2103            if (sEgl != null && sEglDisplay != null) {
2104                sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
2105            }
2106        }
2107
2108        private static void usePbufferSurface(EGLContext eglContext) {
2109            synchronized (sPbufferLock) {
2110                // Create a temporary 1x1 pbuffer so we have a context
2111                // to clear our OpenGL objects
2112                if (sPbuffer == null) {
2113                    sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
2114                            EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
2115                    });
2116                }
2117            }
2118            sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
2119        }
2120    }
2121}
2122