ThreadedRenderer.java revision 23d307c8d88f4a3849163b9e5b7cd11d0d4f372c
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        if (changed) {
259            invalidateRoot();
260        }
261        return changed;
262    }
263
264    private void updateViewTreeDisplayList(View view) {
265        view.mPrivateFlags |= View.PFLAG_DRAWN;
266        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
267                == View.PFLAG_INVALIDATED;
268        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
269        view.getDisplayList();
270        view.mRecreateDisplayList = false;
271    }
272
273    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
274        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
275        updateViewTreeDisplayList(view);
276
277        if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
278            HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
279            try {
280                final int saveCount = canvas.save();
281                canvas.translate(mInsetLeft, mInsetTop);
282                callbacks.onHardwarePreDraw(canvas);
283
284                canvas.insertReorderBarrier();
285                canvas.drawRenderNode(view.getDisplayList());
286                canvas.insertInorderBarrier();
287
288                callbacks.onHardwarePostDraw(canvas);
289                canvas.restoreToCount(saveCount);
290                mRootNodeNeedsUpdate = false;
291            } finally {
292                mRootNode.end(canvas);
293            }
294        }
295        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
296    }
297
298    @Override
299    void invalidateRoot() {
300        mRootNodeNeedsUpdate = true;
301    }
302
303    @Override
304    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
305        attachInfo.mIgnoreDirtyState = true;
306        long frameTimeNanos = mChoreographer.getFrameTimeNanos();
307        attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
308
309        long recordDuration = 0;
310        if (mProfilingEnabled) {
311            recordDuration = System.nanoTime();
312        }
313
314        updateRootDisplayList(view, callbacks);
315
316        if (mProfilingEnabled) {
317            recordDuration = System.nanoTime() - recordDuration;
318        }
319
320        attachInfo.mIgnoreDirtyState = false;
321
322        // register animating rendernodes which started animating prior to renderer
323        // creation, which is typical for animators started prior to first draw
324        if (attachInfo.mPendingAnimatingRenderNodes != null) {
325            final int count = attachInfo.mPendingAnimatingRenderNodes.size();
326            for (int i = 0; i < count; i++) {
327                registerAnimatingRenderNode(
328                        attachInfo.mPendingAnimatingRenderNodes.get(i));
329            }
330            attachInfo.mPendingAnimatingRenderNodes.clear();
331            // We don't need this anymore as subsequent calls to
332            // ViewRootImpl#attachRenderNodeAnimator will go directly to us.
333            attachInfo.mPendingAnimatingRenderNodes = null;
334        }
335
336        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
337                recordDuration, view.getResources().getDisplayMetrics().density);
338        if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
339            attachInfo.mViewRootImpl.invalidate();
340        }
341    }
342
343    static void invokeFunctor(long functor, boolean waitForCompletion) {
344        nInvokeFunctor(functor, waitForCompletion);
345    }
346
347    @Override
348    HardwareLayer createTextureLayer() {
349        long layer = nCreateTextureLayer(mNativeProxy);
350        return HardwareLayer.adoptTextureLayer(this, layer);
351    }
352
353    @Override
354    void buildLayer(RenderNode node) {
355        nBuildLayer(mNativeProxy, node.getNativeDisplayList());
356    }
357
358    @Override
359    boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
360        return nCopyLayerInto(mNativeProxy,
361                layer.getDeferredLayerUpdater(), bitmap.mNativeBitmap);
362    }
363
364    @Override
365    void pushLayerUpdate(HardwareLayer layer) {
366        nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
367    }
368
369    @Override
370    void onLayerDestroyed(HardwareLayer layer) {
371        nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
372    }
373
374    @Override
375    void setName(String name) {
376    }
377
378    @Override
379    void fence() {
380        nFence(mNativeProxy);
381    }
382
383    @Override
384    void stopDrawing() {
385        nStopDrawing(mNativeProxy);
386    }
387
388    @Override
389    public void notifyFramePending() {
390        nNotifyFramePending(mNativeProxy);
391    }
392
393    @Override
394    void registerAnimatingRenderNode(RenderNode animator) {
395        nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
396    }
397
398    @Override
399    protected void finalize() throws Throwable {
400        try {
401            nDeleteProxy(mNativeProxy);
402            mNativeProxy = 0;
403        } finally {
404            super.finalize();
405        }
406    }
407
408    static void trimMemory(int level) {
409        nTrimMemory(level);
410    }
411
412    private static class AtlasInitializer {
413        static AtlasInitializer sInstance = new AtlasInitializer();
414
415        private boolean mInitialized = false;
416
417        private AtlasInitializer() {}
418
419        synchronized void init(Context context, long renderProxy) {
420            if (mInitialized) return;
421            IBinder binder = ServiceManager.getService("assetatlas");
422            if (binder == null) return;
423
424            IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
425            try {
426                if (atlas.isCompatible(android.os.Process.myPpid())) {
427                    GraphicBuffer buffer = atlas.getBuffer();
428                    if (buffer != null) {
429                        long[] map = atlas.getMap();
430                        if (map != null) {
431                            // TODO Remove after fixing b/15425820
432                            validateMap(context, map);
433                            nSetAtlas(renderProxy, buffer, map);
434                            mInitialized = true;
435                        }
436                        // If IAssetAtlas is not the same class as the IBinder
437                        // we are using a remote service and we can safely
438                        // destroy the graphic buffer
439                        if (atlas.getClass() != binder.getClass()) {
440                            buffer.destroy();
441                        }
442                    }
443                }
444            } catch (RemoteException e) {
445                Log.w(LOG_TAG, "Could not acquire atlas", e);
446            }
447        }
448
449        private static void validateMap(Context context, long[] map) {
450            Log.d("Atlas", "Validating map...");
451            HashSet<Long> preloadedPointers = new HashSet<Long>();
452
453            // We only care about drawables that hold bitmaps
454            final Resources resources = context.getResources();
455            final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
456
457            final int count = drawables.size();
458            for (int i = 0; i < count; i++) {
459                final Bitmap bitmap = drawables.valueAt(i).getBitmap();
460                if (bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888) {
461                    preloadedPointers.add(bitmap.mNativeBitmap);
462                }
463            }
464
465            for (int i = 0; i < map.length; i += 4) {
466                if (!preloadedPointers.contains(map[i])) {
467                    Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i]));
468                    map[i] = 0;
469                }
470            }
471        }
472    }
473
474    static native void setupShadersDiskCache(String cacheFile);
475
476    private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
477
478    private static native long nCreateRootRenderNode();
479    private static native long nCreateProxy(boolean translucent, long rootRenderNode);
480    private static native void nDeleteProxy(long nativeProxy);
481
482    private static native void nSetFrameInterval(long nativeProxy, long frameIntervalNanos);
483    private static native boolean nLoadSystemProperties(long nativeProxy);
484
485    private static native boolean nInitialize(long nativeProxy, Surface window);
486    private static native void nUpdateSurface(long nativeProxy, Surface window);
487    private static native void nPauseSurface(long nativeProxy, Surface window);
488    private static native void nSetup(long nativeProxy, int width, int height,
489            float lightX, float lightY, float lightZ, float lightRadius,
490            int ambientShadowAlpha, int spotShadowAlpha);
491    private static native void nSetOpaque(long nativeProxy, boolean opaque);
492    private static native int nSyncAndDrawFrame(long nativeProxy,
493            long frameTimeNanos, long recordDuration, float density);
494    private static native void nDestroy(long nativeProxy);
495    private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
496
497    private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
498
499    private static native long nCreateTextureLayer(long nativeProxy);
500    private static native void nBuildLayer(long nativeProxy, long node);
501    private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
502    private static native void nPushLayerUpdate(long nativeProxy, long layer);
503    private static native void nCancelLayerUpdate(long nativeProxy, long layer);
504    private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
505
506    private static native void nDestroyHardwareResources(long nativeProxy);
507    private static native void nTrimMemory(int level);
508
509    private static native void nFence(long nativeProxy);
510    private static native void nStopDrawing(long nativeProxy);
511    private static native void nNotifyFramePending(long nativeProxy);
512
513    private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd);
514}
515