ThreadedRenderer.java revision 43c410eaacf7d287c6c3f5621e6e0b96501004dc
1/*
2 * Copyright (C) 2013 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
17package android.view;
18
19import android.annotation.IntDef;
20import android.content.Context;
21import android.content.res.Resources;
22import android.content.res.TypedArray;
23import android.graphics.Bitmap;
24import android.graphics.Rect;
25import android.graphics.drawable.Drawable;
26import android.os.IBinder;
27import android.os.RemoteException;
28import android.os.ServiceManager;
29import android.os.Trace;
30import android.util.Log;
31import android.util.LongSparseArray;
32import android.view.Surface.OutOfResourcesException;
33import android.view.View.AttachInfo;
34
35import com.android.internal.R;
36
37import java.io.FileDescriptor;
38import java.io.PrintWriter;
39import java.lang.annotation.Retention;
40import java.lang.annotation.RetentionPolicy;
41import java.util.ArrayList;
42import java.util.HashSet;
43
44/**
45 * Hardware renderer that proxies the rendering to a render thread. Most calls
46 * are currently synchronous.
47 *
48 * The UI thread can block on the RenderThread, but RenderThread must never
49 * block on the UI thread.
50 *
51 * ThreadedRenderer creates an instance of RenderProxy. RenderProxy in turn creates
52 * and manages a CanvasContext on the RenderThread. The CanvasContext is fully managed
53 * by the lifecycle of the RenderProxy.
54 *
55 * Note that although currently the EGL context & surfaces are created & managed
56 * by the render thread, the goal is to move that into a shared structure that can
57 * be managed by both threads. EGLSurface creation & deletion should ideally be
58 * done on the UI thread and not the RenderThread to avoid stalling the
59 * RenderThread with surface buffer allocation.
60 *
61 * @hide
62 */
63public class ThreadedRenderer extends HardwareRenderer {
64    private static final String LOGTAG = "ThreadedRenderer";
65
66    // Keep in sync with DrawFrameTask.h SYNC_* flags
67    // Nothing interesting to report
68    private static final int SYNC_OK = 0;
69    // Needs a ViewRoot invalidate
70    private static final int SYNC_INVALIDATE_REQUIRED = 1 << 0;
71    // Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
72    private static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1;
73
74    private static final String[] VISUALIZERS = {
75        PROFILE_PROPERTY_VISUALIZE_BARS,
76    };
77
78    private static final int FLAG_DUMP_FRAMESTATS   = 1 << 0;
79    private static final int FLAG_DUMP_RESET        = 1 << 1;
80
81    @IntDef(flag = true, value = {
82            FLAG_DUMP_FRAMESTATS, FLAG_DUMP_RESET })
83    @Retention(RetentionPolicy.SOURCE)
84    public @interface DumpFlags {}
85
86    // Size of the rendered content.
87    private int mWidth, mHeight;
88
89    // Actual size of the drawing surface.
90    private int mSurfaceWidth, mSurfaceHeight;
91
92    // Insets between the drawing surface and rendered content. These are
93    // applied as translation when updating the root render node.
94    private int mInsetTop, mInsetLeft;
95
96    // Whether the surface has insets. Used to protect opacity.
97    private boolean mHasInsets;
98
99    // Light and shadow properties specified by the theme.
100    private final float mLightY;
101    private final float mLightZ;
102    private final float mLightRadius;
103    private final int mAmbientShadowAlpha;
104    private final int mSpotShadowAlpha;
105
106    private long mNativeProxy;
107    private boolean mInitialized = false;
108    private RenderNode mRootNode;
109    private Choreographer mChoreographer;
110    private boolean mRootNodeNeedsUpdate;
111
112    ThreadedRenderer(Context context, boolean translucent) {
113        final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
114        mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
115        mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
116        mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
117        mAmbientShadowAlpha =
118                (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
119        mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
120        a.recycle();
121
122        long rootNodePtr = nCreateRootRenderNode();
123        mRootNode = RenderNode.adopt(rootNodePtr);
124        mRootNode.setClipToBounds(false);
125        mNativeProxy = nCreateProxy(translucent, rootNodePtr);
126
127        AtlasInitializer.sInstance.init(context, mNativeProxy);
128
129        loadSystemProperties();
130    }
131
132    @Override
133    void destroy() {
134        mInitialized = false;
135        updateEnabledState(null);
136        nDestroy(mNativeProxy);
137    }
138
139    private void updateEnabledState(Surface surface) {
140        if (surface == null || !surface.isValid()) {
141            setEnabled(false);
142        } else {
143            setEnabled(mInitialized);
144        }
145    }
146
147    @Override
148    boolean initialize(Surface surface) throws OutOfResourcesException {
149        mInitialized = true;
150        updateEnabledState(surface);
151        boolean status = nInitialize(mNativeProxy, surface);
152        surface.allocateBuffers();
153        return status;
154    }
155
156    @Override
157    void updateSurface(Surface surface) throws OutOfResourcesException {
158        updateEnabledState(surface);
159        nUpdateSurface(mNativeProxy, surface);
160    }
161
162    @Override
163    boolean pauseSurface(Surface surface) {
164        return nPauseSurface(mNativeProxy, surface);
165    }
166
167    @Override
168    void destroyHardwareResources(View view) {
169        destroyResources(view);
170        nDestroyHardwareResources(mNativeProxy);
171    }
172
173    private static void destroyResources(View view) {
174        view.destroyHardwareResources();
175
176        if (view instanceof ViewGroup) {
177            ViewGroup group = (ViewGroup) view;
178
179            int count = group.getChildCount();
180            for (int i = 0; i < count; i++) {
181                destroyResources(group.getChildAt(i));
182            }
183        }
184    }
185
186    @Override
187    void invalidate(Surface surface) {
188        updateSurface(surface);
189    }
190
191    @Override
192    void detachSurfaceTexture(long hardwareLayer) {
193        nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
194    }
195
196    @Override
197    void setup(int width, int height, Rect surfaceInsets) {
198        final float lightX = width / 2.0f;
199        mWidth = width;
200        mHeight = height;
201        if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0
202                || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) {
203            mHasInsets = true;
204            mInsetLeft = surfaceInsets.left;
205            mInsetTop = surfaceInsets.top;
206            mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
207            mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
208
209            // If the surface has insets, it can't be opaque.
210            setOpaque(false);
211        } else {
212            mHasInsets = false;
213            mInsetLeft = 0;
214            mInsetTop = 0;
215            mSurfaceWidth = width;
216            mSurfaceHeight = height;
217        }
218        mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
219        nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight,
220                lightX, mLightY, mLightZ, mLightRadius,
221                mAmbientShadowAlpha, mSpotShadowAlpha);
222    }
223
224    @Override
225    void setOpaque(boolean opaque) {
226        nSetOpaque(mNativeProxy, opaque && !mHasInsets);
227    }
228
229    @Override
230    int getWidth() {
231        return mWidth;
232    }
233
234    @Override
235    int getHeight() {
236        return mHeight;
237    }
238
239    @Override
240    void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
241        pw.flush();
242        int flags = 0;
243        for (int i = 0; i < args.length; i++) {
244            switch (args[i]) {
245                case "framestats":
246                    flags |= FLAG_DUMP_FRAMESTATS;
247                    break;
248                case "reset":
249                    flags |= FLAG_DUMP_RESET;
250                    break;
251            }
252        }
253        nDumpProfileInfo(mNativeProxy, fd, flags);
254    }
255
256    @Override
257    boolean loadSystemProperties() {
258        boolean changed = nLoadSystemProperties(mNativeProxy);
259        if (changed) {
260            invalidateRoot();
261        }
262        return changed;
263    }
264
265    private void updateViewTreeDisplayList(View view) {
266        view.mPrivateFlags |= View.PFLAG_DRAWN;
267        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
268                == View.PFLAG_INVALIDATED;
269        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
270        view.getDisplayList();
271        view.mRecreateDisplayList = false;
272    }
273
274    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
275        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
276        updateViewTreeDisplayList(view);
277
278        if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
279            DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
280            try {
281                final int saveCount = canvas.save();
282                canvas.translate(mInsetLeft, mInsetTop);
283                callbacks.onHardwarePreDraw(canvas);
284
285                canvas.insertReorderBarrier();
286                canvas.drawRenderNode(view.getDisplayList());
287                canvas.insertInorderBarrier();
288
289                callbacks.onHardwarePostDraw(canvas);
290                canvas.restoreToCount(saveCount);
291                mRootNodeNeedsUpdate = false;
292            } finally {
293                mRootNode.end(canvas);
294            }
295        }
296        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
297    }
298
299    @Override
300    void invalidateRoot() {
301        mRootNodeNeedsUpdate = true;
302    }
303
304    @Override
305    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
306        attachInfo.mIgnoreDirtyState = true;
307
308        final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
309        choreographer.mFrameInfo.markDrawStart();
310
311        updateRootDisplayList(view, callbacks);
312
313        attachInfo.mIgnoreDirtyState = false;
314
315        // register animating rendernodes which started animating prior to renderer
316        // creation, which is typical for animators started prior to first draw
317        if (attachInfo.mPendingAnimatingRenderNodes != null) {
318            final int count = attachInfo.mPendingAnimatingRenderNodes.size();
319            for (int i = 0; i < count; i++) {
320                registerAnimatingRenderNode(
321                        attachInfo.mPendingAnimatingRenderNodes.get(i));
322            }
323            attachInfo.mPendingAnimatingRenderNodes.clear();
324            // We don't need this anymore as subsequent calls to
325            // ViewRootImpl#attachRenderNodeAnimator will go directly to us.
326            attachInfo.mPendingAnimatingRenderNodes = null;
327        }
328
329        final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
330        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
331        if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
332            setEnabled(false);
333            attachInfo.mViewRootImpl.mSurface.release();
334            // Invalidate since we failed to draw. This should fetch a Surface
335            // if it is still needed or do nothing if we are no longer drawing
336            attachInfo.mViewRootImpl.invalidate();
337        }
338        if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
339            attachInfo.mViewRootImpl.invalidate();
340        }
341    }
342
343    static void invokeFunctor(long functor, boolean waitForCompletion) {
344        nInvokeFunctor(functor, waitForCompletion);
345    }
346
347    @Override
348    HardwareLayer createTextureLayer() {
349        long layer = nCreateTextureLayer(mNativeProxy);
350        return HardwareLayer.adoptTextureLayer(this, layer);
351    }
352
353    @Override
354    void buildLayer(RenderNode node) {
355        nBuildLayer(mNativeProxy, node.getNativeDisplayList());
356    }
357
358    @Override
359    boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
360        return nCopyLayerInto(mNativeProxy,
361                layer.getDeferredLayerUpdater(), bitmap.getSkBitmap());
362    }
363
364    @Override
365    void pushLayerUpdate(HardwareLayer layer) {
366        nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
367    }
368
369    @Override
370    void onLayerDestroyed(HardwareLayer layer) {
371        nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
372    }
373
374    @Override
375    void setName(String name) {
376        nSetName(mNativeProxy, name);
377    }
378
379    @Override
380    void fence() {
381        nFence(mNativeProxy);
382    }
383
384    @Override
385    void stopDrawing() {
386        nStopDrawing(mNativeProxy);
387    }
388
389    @Override
390    public void notifyFramePending() {
391        nNotifyFramePending(mNativeProxy);
392    }
393
394    @Override
395    void registerAnimatingRenderNode(RenderNode animator) {
396        nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
397    }
398
399    @Override
400    protected void finalize() throws Throwable {
401        try {
402            nDeleteProxy(mNativeProxy);
403            mNativeProxy = 0;
404        } finally {
405            super.finalize();
406        }
407    }
408
409    static void trimMemory(int level) {
410        nTrimMemory(level);
411    }
412
413    private static class AtlasInitializer {
414        static AtlasInitializer sInstance = new AtlasInitializer();
415
416        private boolean mInitialized = false;
417
418        private AtlasInitializer() {}
419
420        synchronized void init(Context context, long renderProxy) {
421            if (mInitialized) return;
422            IBinder binder = ServiceManager.getService("assetatlas");
423            if (binder == null) return;
424
425            IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
426            try {
427                if (atlas.isCompatible(android.os.Process.myPpid())) {
428                    GraphicBuffer buffer = atlas.getBuffer();
429                    if (buffer != null) {
430                        long[] map = atlas.getMap();
431                        if (map != null) {
432                            // TODO Remove after fixing b/15425820
433                            validateMap(context, map);
434                            nSetAtlas(renderProxy, buffer, map);
435                            mInitialized = true;
436                        }
437                        // If IAssetAtlas is not the same class as the IBinder
438                        // we are using a remote service and we can safely
439                        // destroy the graphic buffer
440                        if (atlas.getClass() != binder.getClass()) {
441                            buffer.destroy();
442                        }
443                    }
444                }
445            } catch (RemoteException e) {
446                Log.w(LOG_TAG, "Could not acquire atlas", e);
447            }
448        }
449
450        private static void validateMap(Context context, long[] map) {
451            Log.d("Atlas", "Validating map...");
452            HashSet<Long> preloadedPointers = new HashSet<Long>();
453
454            // We only care about drawables that hold bitmaps
455            final Resources resources = context.getResources();
456            final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
457
458            final int count = drawables.size();
459            ArrayList<Bitmap> tmpList = new ArrayList<Bitmap>();
460            for (int i = 0; i < count; i++) {
461                drawables.valueAt(i).addAtlasableBitmaps(tmpList);
462                for (int j = 0; j < tmpList.size(); j++) {
463                    preloadedPointers.add(tmpList.get(j).getSkBitmap());
464                }
465                tmpList.clear();
466            }
467
468            for (int i = 0; i < map.length; i += 4) {
469                if (!preloadedPointers.contains(map[i])) {
470                    Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i]));
471                    map[i] = 0;
472                }
473            }
474        }
475    }
476
477    static native void setupShadersDiskCache(String cacheFile);
478
479    private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
480
481    private static native long nCreateRootRenderNode();
482    private static native long nCreateProxy(boolean translucent, long rootRenderNode);
483    private static native void nDeleteProxy(long nativeProxy);
484
485    private static native boolean nLoadSystemProperties(long nativeProxy);
486    private static native void nSetName(long nativeProxy, String name);
487
488    private static native boolean nInitialize(long nativeProxy, Surface window);
489    private static native void nUpdateSurface(long nativeProxy, Surface window);
490    private static native boolean nPauseSurface(long nativeProxy, Surface window);
491    private static native void nSetup(long nativeProxy, int width, int height,
492            float lightX, float lightY, float lightZ, float lightRadius,
493            int ambientShadowAlpha, int spotShadowAlpha);
494    private static native void nSetOpaque(long nativeProxy, boolean opaque);
495    private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
496    private static native void nDestroy(long nativeProxy);
497    private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
498
499    private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
500
501    private static native long nCreateTextureLayer(long nativeProxy);
502    private static native void nBuildLayer(long nativeProxy, long node);
503    private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
504    private static native void nPushLayerUpdate(long nativeProxy, long layer);
505    private static native void nCancelLayerUpdate(long nativeProxy, long layer);
506    private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
507
508    private static native void nDestroyHardwareResources(long nativeProxy);
509    private static native void nTrimMemory(int level);
510
511    private static native void nFence(long nativeProxy);
512    private static native void nStopDrawing(long nativeProxy);
513    private static native void nNotifyFramePending(long nativeProxy);
514
515    private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
516            @DumpFlags int dumpFlags);
517}
518