• 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.GLUtils;
25 import android.os.SystemClock;
26 import android.os.SystemProperties;
27 import android.util.Log;
28 
29 import javax.microedition.khronos.egl.EGL10;
30 import javax.microedition.khronos.egl.EGL11;
31 import javax.microedition.khronos.egl.EGLConfig;
32 import javax.microedition.khronos.egl.EGLContext;
33 import javax.microedition.khronos.egl.EGLDisplay;
34 import javax.microedition.khronos.egl.EGLSurface;
35 import javax.microedition.khronos.opengles.GL;
36 
37 import static javax.microedition.khronos.egl.EGL10.*;
38 
39 /**
40  * Interface for rendering a ViewAncestor using hardware acceleration.
41  *
42  * @hide
43  */
44 public abstract class HardwareRenderer {
45     static final String LOG_TAG = "HardwareRenderer";
46 
47     /**
48      * Turn on to only refresh the parts of the screen that need updating.
49      * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY}
50      * must also have the value "true".
51      */
52     public static final boolean RENDER_DIRTY_REGIONS = true;
53 
54     /**
55      * System property used to enable or disable dirty regions invalidation.
56      * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
57      * The default value of this property is assumed to be true.
58      *
59      * Possible values:
60      * "true", to enable partial invalidates
61      * "false", to disable partial invalidates
62      */
63     static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions";
64 
65     /**
66      * System property used to enable or disable vsync.
67      * The default value of this property is assumed to be false.
68      *
69      * Possible values:
70      * "true", to disable vsync
71      * "false", to enable vsync
72      */
73     static final String DISABLE_VSYNC_PROPERTY = "hwui.disable_vsync";
74 
75     /**
76      * System property used to debug EGL configuration choice.
77      *
78      * Possible values:
79      * "choice", print the chosen configuration only
80      * "all", print all possible configurations
81      */
82     static final String PRINT_CONFIG_PROPERTY = "hwui.print_config";
83 
84     /**
85      * Turn on to draw dirty regions every other frame.
86      */
87     private static final boolean DEBUG_DIRTY_REGION = false;
88 
89     /**
90      * A process can set this flag to false to prevent the use of hardware
91      * rendering.
92      *
93      * @hide
94      */
95     public static boolean sRendererDisabled = false;
96 
97     /**
98      * Further hardware renderer disabling for the system process.
99      *
100      * @hide
101      */
102     public static boolean sSystemRendererDisabled = false;
103 
104     private boolean mEnabled;
105     private boolean mRequested = true;
106 
107     /**
108      * Invoke this method to disable hardware rendering in the current process.
109      *
110      * @hide
111      */
disable(boolean system)112     public static void disable(boolean system) {
113         sRendererDisabled = true;
114         if (system) {
115             sSystemRendererDisabled = true;
116         }
117     }
118 
119     /**
120      * Indicates whether hardware acceleration is available under any form for
121      * the view hierarchy.
122      *
123      * @return True if the view hierarchy can potentially be hardware accelerated,
124      *         false otherwise
125      */
isAvailable()126     public static boolean isAvailable() {
127         return GLES20Canvas.isAvailable();
128     }
129 
130     /**
131      * Destroys the hardware rendering context.
132      *
133      * @param full If true, destroys all associated resources.
134      */
destroy(boolean full)135     abstract void destroy(boolean full);
136 
137     /**
138      * Initializes the hardware renderer for the specified surface.
139      *
140      * @param holder The holder for the surface to hardware accelerate.
141      *
142      * @return True if the initialization was successful, false otherwise.
143      */
initialize(SurfaceHolder holder)144     abstract boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException;
145 
146     /**
147      * Updates the hardware renderer for the specified surface.
148      *
149      * @param holder The holder for the surface to hardware accelerate
150      */
updateSurface(SurfaceHolder holder)151     abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException;
152 
153     /**
154      * Destoys the layers used by the specified view hierarchy.
155      *
156      * @param view The root of the view hierarchy
157      */
destroyLayers(View view)158     abstract void destroyLayers(View view);
159 
160     /**
161      * This method should be invoked whenever the current hardware renderer
162      * context should be reset.
163      *
164      * @param holder The holder for the surface to hardware accelerate
165      */
invalidate(SurfaceHolder holder)166     abstract void invalidate(SurfaceHolder holder);
167 
168     /**
169      * This method should be invoked to ensure the hardware renderer is in
170      * valid state (for instance, to ensure the correct EGL context is bound
171      * to the current thread.)
172      *
173      * @return true if the renderer is now valid, false otherwise
174      */
validate()175     abstract boolean validate();
176 
177     /**
178      * Setup the hardware renderer for drawing. This is called whenever the
179      * size of the target surface changes or when the surface is first created.
180      *
181      * @param width Width of the drawing surface.
182      * @param height Height of the drawing surface.
183      */
setup(int width, int height)184     abstract void setup(int width, int height);
185 
186     /**
187      * Gets the current width of the surface. This is the width that the surface
188      * was last set to in a call to {@link #setup(int, int)}.
189      *
190      * @return the current width of the surface
191      */
getWidth()192     abstract int getWidth();
193 
194     /**
195      * Gets the current height of the surface. This is the height that the surface
196      * was last set to in a call to {@link #setup(int, int)}.
197      *
198      * @return the current width of the surface
199      */
getHeight()200     abstract int getHeight();
201 
202     /**
203      * Interface used to receive callbacks whenever a view is drawn by
204      * a hardware renderer instance.
205      */
206     interface HardwareDrawCallbacks {
207         /**
208          * Invoked before a view is drawn by a hardware renderer.
209          *
210          * @param canvas The Canvas used to render the view.
211          */
onHardwarePreDraw(HardwareCanvas canvas)212         void onHardwarePreDraw(HardwareCanvas canvas);
213 
214         /**
215          * Invoked after a view is drawn by a hardware renderer.
216          *
217          * @param canvas The Canvas used to render the view.
218          */
onHardwarePostDraw(HardwareCanvas canvas)219         void onHardwarePostDraw(HardwareCanvas canvas);
220     }
221 
222     /**
223      * Draws the specified view.
224      *
225      * @param view The view to draw.
226      * @param attachInfo AttachInfo tied to the specified view.
227      * @param callbacks Callbacks invoked when drawing happens.
228      * @param dirty The dirty rectangle to update, can be null.
229      *
230      * @return true if the dirty rect was ignored, false otherwise
231      */
draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty)232     abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
233             Rect dirty);
234 
235     /**
236      * Creates a new display list that can be used to record batches of
237      * drawing operations.
238      *
239      * @return A new display list.
240      */
createDisplayList()241     abstract DisplayList createDisplayList();
242 
243     /**
244      * Creates a new hardware layer. A hardware layer built by calling this
245      * method will be treated as a texture layer, instead of as a render target.
246      *
247      * @param isOpaque Whether the layer should be opaque or not
248      *
249      * @return A hardware layer
250      */
createHardwareLayer(boolean isOpaque)251     abstract HardwareLayer createHardwareLayer(boolean isOpaque);
252 
253     /**
254      * Creates a new hardware layer.
255      *
256      * @param width The minimum width of the layer
257      * @param height The minimum height of the layer
258      * @param isOpaque Whether the layer should be opaque or not
259      *
260      * @return A hardware layer
261      */
createHardwareLayer(int width, int height, boolean isOpaque)262     abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
263 
264     /**
265      * Creates a new {@link SurfaceTexture} that can be used to render into the
266      * specified hardware layer.
267      *
268      *
269      * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
270      *
271      * @return A {@link SurfaceTexture}
272      */
createSurfaceTexture(HardwareLayer layer)273     abstract SurfaceTexture createSurfaceTexture(HardwareLayer layer);
274 
275     /**
276      * Initializes the hardware renderer for the specified surface and setup the
277      * renderer for drawing, if needed. This is invoked when the ViewAncestor has
278      * potentially lost the hardware renderer. The hardware renderer should be
279      * reinitialized and setup when the render {@link #isRequested()} and
280      * {@link #isEnabled()}.
281      *
282      * @param width The width of the drawing surface.
283      * @param height The height of the drawing surface.
284      * @param attachInfo The
285      * @param holder
286      */
initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, SurfaceHolder holder)287     void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
288             SurfaceHolder holder) throws Surface.OutOfResourcesException {
289         if (isRequested()) {
290             // We lost the gl context, so recreate it.
291             if (!isEnabled()) {
292                 if (initialize(holder)) {
293                     setup(width, height);
294                 }
295             }
296         }
297     }
298 
299     /**
300      * Creates a hardware renderer using OpenGL.
301      *
302      * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.)
303      * @param translucent True if the surface is translucent, false otherwise
304      *
305      * @return A hardware renderer backed by OpenGL.
306      */
createGlRenderer(int glVersion, boolean translucent)307     static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
308         switch (glVersion) {
309             case 2:
310                 return Gl20Renderer.create(translucent);
311         }
312         throw new IllegalArgumentException("Unknown GL version: " + glVersion);
313     }
314 
315     /**
316      * Invoke this method when the system is running out of memory. This
317      * method will attempt to recover as much memory as possible, based on
318      * the specified hint.
319      *
320      * @param level Hint about the amount of memory that should be trimmed,
321      *              see {@link android.content.ComponentCallbacks}
322      */
trimMemory(int level)323     static void trimMemory(int level) {
324         Gl20Renderer.trimMemory(level);
325     }
326 
327     /**
328      * Indicates whether hardware acceleration is currently enabled.
329      *
330      * @return True if hardware acceleration is in use, false otherwise.
331      */
isEnabled()332     boolean isEnabled() {
333         return mEnabled;
334     }
335 
336     /**
337      * Indicates whether hardware acceleration is currently enabled.
338      *
339      * @param enabled True if the hardware renderer is in use, false otherwise.
340      */
setEnabled(boolean enabled)341     void setEnabled(boolean enabled) {
342         mEnabled = enabled;
343     }
344 
345     /**
346      * Indicates whether hardware acceleration is currently request but not
347      * necessarily enabled yet.
348      *
349      * @return True if requested, false otherwise.
350      */
isRequested()351     boolean isRequested() {
352         return mRequested;
353     }
354 
355     /**
356      * Indicates whether hardware acceleration is currently requested but not
357      * necessarily enabled yet.
358      *
359      * @return True to request hardware acceleration, false otherwise.
360      */
setRequested(boolean requested)361     void setRequested(boolean requested) {
362         mRequested = requested;
363     }
364 
365     @SuppressWarnings({"deprecation"})
366     static abstract class GlRenderer extends HardwareRenderer {
367         // These values are not exposed in our EGL APIs
368         static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
369         static final int EGL_OPENGL_ES2_BIT = 4;
370         static final int EGL_SURFACE_TYPE = 0x3033;
371         static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
372 
373         static final int SURFACE_STATE_ERROR = 0;
374         static final int SURFACE_STATE_SUCCESS = 1;
375         static final int SURFACE_STATE_UPDATED = 2;
376 
377         static EGL10 sEgl;
378         static EGLDisplay sEglDisplay;
379         static EGLConfig sEglConfig;
380         static final Object[] sEglLock = new Object[0];
381         int mWidth = -1, mHeight = -1;
382 
383         static final ThreadLocal<EGLContext> sEglContextStorage = new ThreadLocal<EGLContext>();
384 
385         EGLContext mEglContext;
386         Thread mEglThread;
387 
388         EGLSurface mEglSurface;
389 
390         GL mGl;
391         HardwareCanvas mCanvas;
392         int mFrameCount;
393         Paint mDebugPaint;
394 
395         static boolean sDirtyRegions;
396         static final boolean sDirtyRegionsRequested;
397         static {
398             String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
399             //noinspection PointlessBooleanExpression,ConstantConditions
400             sDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty);
401             sDirtyRegionsRequested = sDirtyRegions;
402         }
403 
404         boolean mDirtyRegionsEnabled;
405         final boolean mVsyncDisabled;
406 
407         final int mGlVersion;
408         final boolean mTranslucent;
409 
410         private boolean mDestroyed;
411 
412         private final Rect mRedrawClip = new Rect();
413 
GlRenderer(int glVersion, boolean translucent)414         GlRenderer(int glVersion, boolean translucent) {
415             mGlVersion = glVersion;
416             mTranslucent = translucent;
417 
418             final String vsyncProperty = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false");
419             mVsyncDisabled = "true".equalsIgnoreCase(vsyncProperty);
420             if (mVsyncDisabled) {
421                 Log.d(LOG_TAG, "Disabling v-sync");
422             }
423         }
424 
425         /**
426          * Indicates whether this renderer instance can track and update dirty regions.
427          */
hasDirtyRegions()428         boolean hasDirtyRegions() {
429             return mDirtyRegionsEnabled;
430         }
431 
432         /**
433          * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
434          * is invoked and the requested flag is turned off. The error code is
435          * also logged as a warning.
436          */
checkEglErrors()437         void checkEglErrors() {
438             if (isEnabled()) {
439                 int error = sEgl.eglGetError();
440                 if (error != EGL_SUCCESS) {
441                     // something bad has happened revert to
442                     // normal rendering.
443                     Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
444                     fallback(error != EGL11.EGL_CONTEXT_LOST);
445                 }
446             }
447         }
448 
fallback(boolean fallback)449         private void fallback(boolean fallback) {
450             destroy(true);
451             if (fallback) {
452                 // we'll try again if it was context lost
453                 setRequested(false);
454                 Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
455                         + "Switching back to software rendering.");
456             }
457         }
458 
459         @Override
initialize(SurfaceHolder holder)460         boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException {
461             if (isRequested() && !isEnabled()) {
462                 initializeEgl();
463                 mGl = createEglSurface(holder);
464                 mDestroyed = false;
465 
466                 if (mGl != null) {
467                     int err = sEgl.eglGetError();
468                     if (err != EGL_SUCCESS) {
469                         destroy(true);
470                         setRequested(false);
471                     } else {
472                         if (mCanvas == null) {
473                             mCanvas = createCanvas();
474                         }
475                         if (mCanvas != null) {
476                             setEnabled(true);
477                         } else {
478                             Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
479                         }
480                     }
481 
482                     return mCanvas != null;
483                 }
484             }
485             return false;
486         }
487 
488         @Override
updateSurface(SurfaceHolder holder)489         void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
490             if (isRequested() && isEnabled()) {
491                 createEglSurface(holder);
492             }
493         }
494 
createCanvas()495         abstract GLES20Canvas createCanvas();
496 
getConfig(boolean dirtyRegions)497         abstract int[] getConfig(boolean dirtyRegions);
498 
initializeEgl()499         void initializeEgl() {
500             synchronized (sEglLock) {
501                 if (sEgl == null && sEglConfig == null) {
502                     sEgl = (EGL10) EGLContext.getEGL();
503 
504                     // Get to the default display.
505                     sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
506 
507                     if (sEglDisplay == EGL_NO_DISPLAY) {
508                         throw new RuntimeException("eglGetDisplay failed "
509                                 + GLUtils.getEGLErrorString(sEgl.eglGetError()));
510                     }
511 
512                     // We can now initialize EGL for that display
513                     int[] version = new int[2];
514                     if (!sEgl.eglInitialize(sEglDisplay, version)) {
515                         throw new RuntimeException("eglInitialize failed " +
516                                 GLUtils.getEGLErrorString(sEgl.eglGetError()));
517                     }
518 
519                     sEglConfig = chooseEglConfig();
520                     if (sEglConfig == null) {
521                         // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
522                         if (sDirtyRegions) {
523                             sDirtyRegions = false;
524                             sEglConfig = chooseEglConfig();
525                             if (sEglConfig == null) {
526                                 throw new RuntimeException("eglConfig not initialized");
527                             }
528                         } else {
529                             throw new RuntimeException("eglConfig not initialized");
530                         }
531                     }
532                 }
533             }
534 
535             mEglContext = sEglContextStorage.get();
536             mEglThread = Thread.currentThread();
537 
538             if (mEglContext == null) {
539                 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
540                 sEglContextStorage.set(mEglContext);
541             }
542         }
543 
chooseEglConfig()544         private EGLConfig chooseEglConfig() {
545             EGLConfig[] configs = new EGLConfig[1];
546             int[] configsCount = new int[1];
547             int[] configSpec = getConfig(sDirtyRegions);
548 
549             // Debug
550             final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
551             if ("all".equalsIgnoreCase(debug)) {
552                 sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
553 
554                 EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
555                 sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
556                         configsCount[0], configsCount);
557 
558                 for (EGLConfig config : debugConfigs) {
559                     printConfig(config);
560                 }
561             }
562 
563             if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
564                 throw new IllegalArgumentException("eglChooseConfig failed " +
565                         GLUtils.getEGLErrorString(sEgl.eglGetError()));
566             } else if (configsCount[0] > 0) {
567                 if ("choice".equalsIgnoreCase(debug)) {
568                     printConfig(configs[0]);
569                 }
570                 return configs[0];
571             }
572 
573             return null;
574         }
575 
printConfig(EGLConfig config)576         private void printConfig(EGLConfig config) {
577             int[] value = new int[1];
578 
579             Log.d(LOG_TAG, "EGL configuration " + config + ":");
580 
581             sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
582             Log.d(LOG_TAG, "  RED_SIZE = " + value[0]);
583 
584             sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
585             Log.d(LOG_TAG, "  GREEN_SIZE = " + value[0]);
586 
587             sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
588             Log.d(LOG_TAG, "  BLUE_SIZE = " + value[0]);
589 
590             sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
591             Log.d(LOG_TAG, "  ALPHA_SIZE = " + value[0]);
592 
593             sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
594             Log.d(LOG_TAG, "  DEPTH_SIZE = " + value[0]);
595 
596             sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
597             Log.d(LOG_TAG, "  STENCIL_SIZE = " + value[0]);
598 
599             sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
600             Log.d(LOG_TAG, "  SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
601         }
602 
createEglSurface(SurfaceHolder holder)603         GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
604             // Check preconditions.
605             if (sEgl == null) {
606                 throw new RuntimeException("egl not initialized");
607             }
608             if (sEglDisplay == null) {
609                 throw new RuntimeException("eglDisplay not initialized");
610             }
611             if (sEglConfig == null) {
612                 throw new RuntimeException("eglConfig not initialized");
613             }
614             if (Thread.currentThread() != mEglThread) {
615                 throw new IllegalStateException("HardwareRenderer cannot be used "
616                         + "from multiple threads");
617             }
618 
619             // In case we need to destroy an existing surface
620             destroySurface();
621 
622             // Create an EGL surface we can render into.
623             if (!createSurface(holder)) {
624                 return null;
625             }
626 
627             /*
628              * Before we can issue GL commands, we need to make sure
629              * the context is current and bound to a surface.
630              */
631             if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
632                 throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
633                         + GLUtils.getEGLErrorString(sEgl.eglGetError()));
634             }
635 
636             // If mDirtyRegions is set, this means we have an EGL configuration
637             // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
638             if (sDirtyRegions) {
639                 if (!(mDirtyRegionsEnabled = GLES20Canvas.preserveBackBuffer())) {
640                     Log.w(LOG_TAG, "Backbuffer cannot be preserved");
641                 }
642             } else if (sDirtyRegionsRequested) {
643                 // If mDirtyRegions is not set, our EGL configuration does not
644                 // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
645                 // swap behavior might be EGL_BUFFER_PRESERVED, which means we
646                 // want to set mDirtyRegions. We try to do this only if dirty
647                 // regions were initially requested as part of the device
648                 // configuration (see RENDER_DIRTY_REGIONS)
649                 mDirtyRegionsEnabled = GLES20Canvas.isBackBufferPreserved();
650             }
651 
652             return mEglContext.getGL();
653         }
654 
createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig)655         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
656             int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
657 
658             return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
659                     mGlVersion != 0 ? attribs : null);
660         }
661 
662         @Override
destroy(boolean full)663         void destroy(boolean full) {
664             if (full && mCanvas != null) {
665                 mCanvas = null;
666             }
667 
668             if (!isEnabled() || mDestroyed) {
669                 setEnabled(false);
670                 return;
671             }
672 
673             destroySurface();
674             setEnabled(false);
675 
676             mDestroyed = true;
677             mGl = null;
678         }
679 
destroySurface()680         void destroySurface() {
681             if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
682                 sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
683                 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
684                 mEglSurface = null;
685             }
686         }
687 
688         @Override
invalidate(SurfaceHolder holder)689         void invalidate(SurfaceHolder holder) {
690             // Cancels any existing buffer to ensure we'll get a buffer
691             // of the right size before we call eglSwapBuffers
692             sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
693 
694             if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
695                 sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
696                 mEglSurface = null;
697                 setEnabled(false);
698             }
699 
700             if (holder.getSurface().isValid()) {
701                 if (!createSurface(holder)) {
702                     return;
703                 }
704                 if (mCanvas != null) {
705                     setEnabled(true);
706                 }
707             }
708         }
709 
createSurface(SurfaceHolder holder)710         private boolean createSurface(SurfaceHolder holder) {
711             mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
712 
713             if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
714                 int error = sEgl.eglGetError();
715                 if (error == EGL_BAD_NATIVE_WINDOW) {
716                     Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
717                     return false;
718                 }
719                 throw new RuntimeException("createWindowSurface failed "
720                         + GLUtils.getEGLErrorString(error));
721             }
722             return true;
723         }
724 
725         @Override
validate()726         boolean validate() {
727             return checkCurrent() != SURFACE_STATE_ERROR;
728         }
729 
730         @Override
setup(int width, int height)731         void setup(int width, int height) {
732             if (validate()) {
733                 mCanvas.setViewport(width, height);
734                 mWidth = width;
735                 mHeight = height;
736             }
737         }
738 
739         @Override
getWidth()740         int getWidth() {
741             return mWidth;
742         }
743 
744         @Override
getHeight()745         int getHeight() {
746             return mHeight;
747         }
748 
canDraw()749         boolean canDraw() {
750             return mGl != null && mCanvas != null;
751         }
752 
onPreDraw(Rect dirty)753         void onPreDraw(Rect dirty) {
754         }
755 
onPostDraw()756         void onPostDraw() {
757         }
758 
759         @Override
draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty)760         boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
761                 Rect dirty) {
762             if (canDraw()) {
763                 if (!hasDirtyRegions()) {
764                     dirty = null;
765                 }
766                 attachInfo.mIgnoreDirtyState = true;
767                 attachInfo.mDrawingTime = SystemClock.uptimeMillis();
768 
769                 view.mPrivateFlags |= View.DRAWN;
770 
771                 final int surfaceState = checkCurrent();
772                 if (surfaceState != SURFACE_STATE_ERROR) {
773                     // We had to change the current surface and/or context, redraw everything
774                     if (surfaceState == SURFACE_STATE_UPDATED) {
775                         dirty = null;
776                     }
777 
778                     onPreDraw(dirty);
779 
780                     HardwareCanvas canvas = mCanvas;
781                     attachInfo.mHardwareCanvas = canvas;
782 
783                     int saveCount = canvas.save();
784                     callbacks.onHardwarePreDraw(canvas);
785 
786                     try {
787                         view.mRecreateDisplayList =
788                                 (view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED;
789                         view.mPrivateFlags &= ~View.INVALIDATED;
790 
791                         DisplayList displayList = view.getDisplayList();
792                         if (displayList != null) {
793                             if (canvas.drawDisplayList(displayList, view.getWidth(),
794                                     view.getHeight(), mRedrawClip)) {
795                                 if (mRedrawClip.isEmpty() || view.getParent() == null) {
796                                     view.invalidate();
797                                 } else {
798                                     view.getParent().invalidateChild(view, mRedrawClip);
799                                 }
800                                 mRedrawClip.setEmpty();
801                             }
802                         } else {
803                             // Shouldn't reach here
804                             view.draw(canvas);
805                         }
806 
807                         if (DEBUG_DIRTY_REGION) {
808                             if (mDebugPaint == null) {
809                                 mDebugPaint = new Paint();
810                                 mDebugPaint.setColor(0x7fff0000);
811                             }
812                             if (dirty != null && (mFrameCount++ & 1) == 0) {
813                                 canvas.drawRect(dirty, mDebugPaint);
814                             }
815                         }
816                     } finally {
817                         callbacks.onHardwarePostDraw(canvas);
818                         canvas.restoreToCount(saveCount);
819                         view.mRecreateDisplayList = false;
820                     }
821 
822                     onPostDraw();
823 
824                     attachInfo.mIgnoreDirtyState = false;
825 
826                     sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
827                     checkEglErrors();
828 
829                     return dirty == null;
830                 }
831             }
832 
833             return false;
834         }
835 
836         /**
837          * Ensures the current EGL context is the one we expect.
838          *
839          * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
840          *         {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
841          *         {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
842          */
checkCurrent()843         int checkCurrent() {
844             if (mEglThread != Thread.currentThread()) {
845                 throw new IllegalStateException("Hardware acceleration can only be used with a " +
846                         "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
847                         "Current thread: " + Thread.currentThread());
848             }
849 
850             if (!mEglContext.equals(sEgl.eglGetCurrentContext()) ||
851                     !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
852                 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
853                     Log.e(LOG_TAG, "eglMakeCurrent failed " +
854                             GLUtils.getEGLErrorString(sEgl.eglGetError()));
855                     fallback(true);
856                     return SURFACE_STATE_ERROR;
857                 } else {
858                     return SURFACE_STATE_UPDATED;
859                 }
860             }
861             return SURFACE_STATE_SUCCESS;
862         }
863     }
864 
865     /**
866      * Hardware renderer using OpenGL ES 2.0.
867      */
868     static class Gl20Renderer extends GlRenderer {
869         private GLES20Canvas mGlCanvas;
870 
871         private static EGLSurface sPbuffer;
872         private static final Object[] sPbufferLock = new Object[0];
873 
Gl20Renderer(boolean translucent)874         Gl20Renderer(boolean translucent) {
875             super(2, translucent);
876         }
877 
878         @Override
createCanvas()879         GLES20Canvas createCanvas() {
880             return mGlCanvas = new GLES20Canvas(mTranslucent);
881         }
882 
883         @Override
getConfig(boolean dirtyRegions)884         int[] getConfig(boolean dirtyRegions) {
885             return new int[] {
886                     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
887                     EGL_RED_SIZE, 8,
888                     EGL_GREEN_SIZE, 8,
889                     EGL_BLUE_SIZE, 8,
890                     EGL_ALPHA_SIZE, 8,
891                     EGL_DEPTH_SIZE, 0,
892                     EGL_STENCIL_SIZE, 0,
893                     EGL_SURFACE_TYPE, EGL_WINDOW_BIT |
894                             (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
895                     EGL_NONE
896             };
897         }
898 
899         @Override
canDraw()900         boolean canDraw() {
901             return super.canDraw() && mGlCanvas != null;
902         }
903 
904         @Override
onPreDraw(Rect dirty)905         void onPreDraw(Rect dirty) {
906             mGlCanvas.onPreDraw(dirty);
907         }
908 
909         @Override
onPostDraw()910         void onPostDraw() {
911             mGlCanvas.onPostDraw();
912         }
913 
914         @Override
destroy(boolean full)915         void destroy(boolean full) {
916             try {
917                 super.destroy(full);
918             } finally {
919                 if (full && mGlCanvas != null) {
920                     mGlCanvas = null;
921                 }
922             }
923         }
924 
925         @Override
setup(int width, int height)926         void setup(int width, int height) {
927             super.setup(width, height);
928             if (mVsyncDisabled) {
929                 GLES20Canvas.disableVsync();
930             }
931         }
932 
933         @Override
createDisplayList()934         DisplayList createDisplayList() {
935             return new GLES20DisplayList();
936         }
937 
938         @Override
createHardwareLayer(boolean isOpaque)939         HardwareLayer createHardwareLayer(boolean isOpaque) {
940             return new GLES20TextureLayer(isOpaque);
941         }
942 
943         @Override
createHardwareLayer(int width, int height, boolean isOpaque)944         HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
945             return new GLES20RenderLayer(width, height, isOpaque);
946         }
947 
948         @Override
createSurfaceTexture(HardwareLayer layer)949         SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
950             return ((GLES20TextureLayer) layer).getSurfaceTexture();
951         }
952 
953         @Override
destroyLayers(View view)954         void destroyLayers(View view) {
955             if (view != null && isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) {
956                 destroyHardwareLayer(view);
957                 GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
958             }
959         }
960 
destroyHardwareLayer(View view)961         private void destroyHardwareLayer(View view) {
962             if (view.destroyLayer()) {
963                 view.invalidate(true);
964             }
965             if (view instanceof ViewGroup) {
966                 ViewGroup group = (ViewGroup) view;
967 
968                 int count = group.getChildCount();
969                 for (int i = 0; i < count; i++) {
970                     destroyHardwareLayer(group.getChildAt(i));
971                 }
972             }
973         }
974 
create(boolean translucent)975         static HardwareRenderer create(boolean translucent) {
976             if (GLES20Canvas.isAvailable()) {
977                 return new Gl20Renderer(translucent);
978             }
979             return null;
980         }
981 
trimMemory(int level)982         static void trimMemory(int level) {
983             if (sEgl == null || sEglConfig == null) return;
984 
985             EGLContext eglContext = sEglContextStorage.get();
986             // We do not have OpenGL objects
987             if (eglContext == null) {
988                 return;
989             } else {
990                 synchronized (sPbufferLock) {
991                     // Create a temporary 1x1 pbuffer so we have a context
992                     // to clear our OpenGL objects
993                     if (sPbuffer == null) {
994                         sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
995                                 EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
996                         });
997                     }
998                 }
999                 sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
1000             }
1001 
1002             switch (level) {
1003                 case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
1004                 case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
1005                 case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
1006                     GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
1007                     break;
1008                 case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
1009                     GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
1010                     break;
1011             }
1012         }
1013     }
1014 }
1015