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