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