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