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