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