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