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