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