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