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