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