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