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