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