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