ThreadedRenderer.java revision f6829a0a618b4523619ec53c996b04d67e3186b9
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    private final float mDensity;
106
107    private long mNativeProxy;
108    private boolean mInitialized = false;
109    private RenderNode mRootNode;
110    private Choreographer mChoreographer;
111    private boolean mRootNodeNeedsUpdate;
112
113    ThreadedRenderer(Context context, boolean translucent) {
114        final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
115        mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
116        mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
117        mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
118        mAmbientShadowAlpha =
119                (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
120        mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
121        a.recycle();
122        mDensity = context.getResources().getDisplayMetrics().density;
123
124        long rootNodePtr = nCreateRootRenderNode();
125        mRootNode = RenderNode.adopt(rootNodePtr);
126        mRootNode.setClipToBounds(false);
127        mNativeProxy = nCreateProxy(translucent, rootNodePtr);
128
129        AtlasInitializer.sInstance.init(context, mNativeProxy);
130
131        // Setup timing
132        mChoreographer = Choreographer.getInstance();
133        nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
134
135        loadSystemProperties();
136    }
137
138    @Override
139    void destroy() {
140        mInitialized = false;
141        updateEnabledState(null);
142        nDestroy(mNativeProxy);
143    }
144
145    private void updateEnabledState(Surface surface) {
146        if (surface == null || !surface.isValid()) {
147            setEnabled(false);
148        } else {
149            setEnabled(mInitialized);
150        }
151    }
152
153    @Override
154    boolean initialize(Surface surface) throws OutOfResourcesException {
155        mInitialized = true;
156        updateEnabledState(surface);
157        boolean status = nInitialize(mNativeProxy, surface);
158        surface.allocateBuffers();
159        return status;
160    }
161
162    @Override
163    void updateSurface(Surface surface) throws OutOfResourcesException {
164        updateEnabledState(surface);
165        nUpdateSurface(mNativeProxy, surface);
166    }
167
168    @Override
169    boolean pauseSurface(Surface surface) {
170        return nPauseSurface(mNativeProxy, surface);
171    }
172
173    @Override
174    void destroyHardwareResources(View view) {
175        destroyResources(view);
176        nDestroyHardwareResources(mNativeProxy);
177    }
178
179    private static void destroyResources(View view) {
180        view.destroyHardwareResources();
181
182        if (view instanceof ViewGroup) {
183            ViewGroup group = (ViewGroup) view;
184
185            int count = group.getChildCount();
186            for (int i = 0; i < count; i++) {
187                destroyResources(group.getChildAt(i));
188            }
189        }
190    }
191
192    @Override
193    void invalidate(Surface surface) {
194        updateSurface(surface);
195    }
196
197    @Override
198    void detachSurfaceTexture(long hardwareLayer) {
199        nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
200    }
201
202    @Override
203    void setup(int width, int height, Rect surfaceInsets) {
204        final float lightX = width / 2.0f;
205        mWidth = width;
206        mHeight = height;
207        if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0
208                || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) {
209            mHasInsets = true;
210            mInsetLeft = surfaceInsets.left;
211            mInsetTop = surfaceInsets.top;
212            mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
213            mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
214
215            // If the surface has insets, it can't be opaque.
216            setOpaque(false);
217        } else {
218            mHasInsets = false;
219            mInsetLeft = 0;
220            mInsetTop = 0;
221            mSurfaceWidth = width;
222            mSurfaceHeight = height;
223        }
224        mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
225        nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight,
226                lightX, mLightY, mLightZ, mLightRadius,
227                mAmbientShadowAlpha, mSpotShadowAlpha, mDensity);
228    }
229
230    @Override
231    void setOpaque(boolean opaque) {
232        nSetOpaque(mNativeProxy, opaque && !mHasInsets);
233    }
234
235    @Override
236    int getWidth() {
237        return mWidth;
238    }
239
240    @Override
241    int getHeight() {
242        return mHeight;
243    }
244
245    @Override
246    void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
247        pw.flush();
248        int flags = 0;
249        for (int i = 0; i < args.length; i++) {
250            switch (args[i]) {
251                case "framestats":
252                    flags |= FLAG_DUMP_FRAMESTATS;
253                    break;
254                case "reset":
255                    flags |= FLAG_DUMP_RESET;
256                    break;
257            }
258        }
259        nDumpProfileInfo(mNativeProxy, fd, flags);
260    }
261
262    @Override
263    boolean loadSystemProperties() {
264        boolean changed = nLoadSystemProperties(mNativeProxy);
265        if (changed) {
266            invalidateRoot();
267        }
268        return changed;
269    }
270
271    private void updateViewTreeDisplayList(View view) {
272        view.mPrivateFlags |= View.PFLAG_DRAWN;
273        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
274                == View.PFLAG_INVALIDATED;
275        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
276        view.getDisplayList();
277        view.mRecreateDisplayList = false;
278    }
279
280    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
281        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
282        updateViewTreeDisplayList(view);
283
284        if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
285            DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
286            try {
287                final int saveCount = canvas.save();
288                canvas.translate(mInsetLeft, mInsetTop);
289                callbacks.onHardwarePreDraw(canvas);
290
291                canvas.insertReorderBarrier();
292                canvas.drawRenderNode(view.getDisplayList());
293                canvas.insertInorderBarrier();
294
295                callbacks.onHardwarePostDraw(canvas);
296                canvas.restoreToCount(saveCount);
297                mRootNodeNeedsUpdate = false;
298            } finally {
299                mRootNode.end(canvas);
300            }
301        }
302        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
303    }
304
305    @Override
306    void invalidateRoot() {
307        mRootNodeNeedsUpdate = true;
308    }
309
310    @Override
311    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
312        attachInfo.mIgnoreDirtyState = true;
313
314        final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
315        choreographer.mFrameInfo.markDrawStart();
316
317        updateRootDisplayList(view, callbacks);
318
319        attachInfo.mIgnoreDirtyState = false;
320
321        // register animating rendernodes which started animating prior to renderer
322        // creation, which is typical for animators started prior to first draw
323        if (attachInfo.mPendingAnimatingRenderNodes != null) {
324            final int count = attachInfo.mPendingAnimatingRenderNodes.size();
325            for (int i = 0; i < count; i++) {
326                registerAnimatingRenderNode(
327                        attachInfo.mPendingAnimatingRenderNodes.get(i));
328            }
329            attachInfo.mPendingAnimatingRenderNodes.clear();
330            // We don't need this anymore as subsequent calls to
331            // ViewRootImpl#attachRenderNodeAnimator will go directly to us.
332            attachInfo.mPendingAnimatingRenderNodes = null;
333        }
334
335        final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
336        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
337        if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
338            setEnabled(false);
339            attachInfo.mViewRootImpl.mSurface.release();
340            // Invalidate since we failed to draw. This should fetch a Surface
341            // if it is still needed or do nothing if we are no longer drawing
342            attachInfo.mViewRootImpl.invalidate();
343        }
344        if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
345            attachInfo.mViewRootImpl.invalidate();
346        }
347    }
348
349    static void invokeFunctor(long functor, boolean waitForCompletion) {
350        nInvokeFunctor(functor, waitForCompletion);
351    }
352
353    @Override
354    HardwareLayer createTextureLayer() {
355        long layer = nCreateTextureLayer(mNativeProxy);
356        return HardwareLayer.adoptTextureLayer(this, layer);
357    }
358
359    @Override
360    void buildLayer(RenderNode node) {
361        nBuildLayer(mNativeProxy, node.getNativeDisplayList());
362    }
363
364    @Override
365    boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
366        return nCopyLayerInto(mNativeProxy,
367                layer.getDeferredLayerUpdater(), bitmap.getSkBitmap());
368    }
369
370    @Override
371    void pushLayerUpdate(HardwareLayer layer) {
372        nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
373    }
374
375    @Override
376    void onLayerDestroyed(HardwareLayer layer) {
377        nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
378    }
379
380    @Override
381    void setName(String name) {
382    }
383
384    @Override
385    void fence() {
386        nFence(mNativeProxy);
387    }
388
389    @Override
390    void stopDrawing() {
391        nStopDrawing(mNativeProxy);
392    }
393
394    @Override
395    public void notifyFramePending() {
396        nNotifyFramePending(mNativeProxy);
397    }
398
399    @Override
400    void registerAnimatingRenderNode(RenderNode animator) {
401        nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
402    }
403
404    @Override
405    protected void finalize() throws Throwable {
406        try {
407            nDeleteProxy(mNativeProxy);
408            mNativeProxy = 0;
409        } finally {
410            super.finalize();
411        }
412    }
413
414    static void trimMemory(int level) {
415        nTrimMemory(level);
416    }
417
418    private static class AtlasInitializer {
419        static AtlasInitializer sInstance = new AtlasInitializer();
420
421        private boolean mInitialized = false;
422
423        private AtlasInitializer() {}
424
425        synchronized void init(Context context, long renderProxy) {
426            if (mInitialized) return;
427            IBinder binder = ServiceManager.getService("assetatlas");
428            if (binder == null) return;
429
430            IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
431            try {
432                if (atlas.isCompatible(android.os.Process.myPpid())) {
433                    GraphicBuffer buffer = atlas.getBuffer();
434                    if (buffer != null) {
435                        long[] map = atlas.getMap();
436                        if (map != null) {
437                            // TODO Remove after fixing b/15425820
438                            validateMap(context, map);
439                            nSetAtlas(renderProxy, buffer, map);
440                            mInitialized = true;
441                        }
442                        // If IAssetAtlas is not the same class as the IBinder
443                        // we are using a remote service and we can safely
444                        // destroy the graphic buffer
445                        if (atlas.getClass() != binder.getClass()) {
446                            buffer.destroy();
447                        }
448                    }
449                }
450            } catch (RemoteException e) {
451                Log.w(LOG_TAG, "Could not acquire atlas", e);
452            }
453        }
454
455        private static void validateMap(Context context, long[] map) {
456            Log.d("Atlas", "Validating map...");
457            HashSet<Long> preloadedPointers = new HashSet<Long>();
458
459            // We only care about drawables that hold bitmaps
460            final Resources resources = context.getResources();
461            final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
462
463            final int count = drawables.size();
464            ArrayList<Bitmap> tmpList = new ArrayList<Bitmap>();
465            for (int i = 0; i < count; i++) {
466                drawables.valueAt(i).addAtlasableBitmaps(tmpList);
467                for (int j = 0; j < tmpList.size(); j++) {
468                    preloadedPointers.add(tmpList.get(j).getSkBitmap());
469                }
470                tmpList.clear();
471            }
472
473            for (int i = 0; i < map.length; i += 4) {
474                if (!preloadedPointers.contains(map[i])) {
475                    Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i]));
476                    map[i] = 0;
477                }
478            }
479        }
480    }
481
482    static native void setupShadersDiskCache(String cacheFile);
483
484    private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
485
486    private static native long nCreateRootRenderNode();
487    private static native long nCreateProxy(boolean translucent, long rootRenderNode);
488    private static native void nDeleteProxy(long nativeProxy);
489
490    private static native void nSetFrameInterval(long nativeProxy, long frameIntervalNanos);
491    private static native boolean nLoadSystemProperties(long nativeProxy);
492
493    private static native boolean nInitialize(long nativeProxy, Surface window);
494    private static native void nUpdateSurface(long nativeProxy, Surface window);
495    private static native boolean nPauseSurface(long nativeProxy, Surface window);
496    private static native void nSetup(long nativeProxy, int width, int height,
497            float lightX, float lightY, float lightZ, float lightRadius,
498            int ambientShadowAlpha, int spotShadowAlpha, float density);
499    private static native void nSetOpaque(long nativeProxy, boolean opaque);
500    private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
501    private static native void nDestroy(long nativeProxy);
502    private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
503
504    private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
505
506    private static native long nCreateTextureLayer(long nativeProxy);
507    private static native void nBuildLayer(long nativeProxy, long node);
508    private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
509    private static native void nPushLayerUpdate(long nativeProxy, long layer);
510    private static native void nCancelLayerUpdate(long nativeProxy, long layer);
511    private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
512
513    private static native void nDestroyHardwareResources(long nativeProxy);
514    private static native void nTrimMemory(int level);
515
516    private static native void nFence(long nativeProxy);
517    private static native void nStopDrawing(long nativeProxy);
518    private static native void nNotifyFramePending(long nativeProxy);
519
520    private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
521            @DumpFlags int dumpFlags);
522}
523