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