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