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