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