ThreadedRenderer.java revision 828698b012e3c317073c0dbbe323d06fe9954af4
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.annotation.NonNull;
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.Bitmap;
24import android.graphics.Point;
25import android.graphics.Rect;
26import android.os.Binder;
27import android.os.IBinder;
28import android.os.ParcelFileDescriptor;
29import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.os.Trace;
32import android.util.Log;
33import android.view.Surface.OutOfResourcesException;
34import android.view.View.AttachInfo;
35
36import com.android.internal.R;
37
38import java.io.FileDescriptor;
39import java.io.PrintWriter;
40import java.lang.annotation.Retention;
41import java.lang.annotation.RetentionPolicy;
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    private static final int FLAG_DUMP_FRAMESTATS   = 1 << 0;
78    private static final int FLAG_DUMP_RESET        = 1 << 1;
79
80    @IntDef(flag = true, value = {
81            FLAG_DUMP_FRAMESTATS, FLAG_DUMP_RESET })
82    @Retention(RetentionPolicy.SOURCE)
83    public @interface DumpFlags {}
84
85    // Size of the rendered content.
86    private int mWidth, mHeight;
87
88    // Actual size of the drawing surface.
89    private int mSurfaceWidth, mSurfaceHeight;
90
91    // Insets between the drawing surface and rendered content. These are
92    // applied as translation when updating the root render node.
93    private int mInsetTop, mInsetLeft;
94
95    // Whether the surface has insets. Used to protect opacity.
96    private boolean mHasInsets;
97
98    // Light and shadow properties specified by the theme.
99    private final float mLightY;
100    private final float mLightZ;
101    private final float mLightRadius;
102    private final int mAmbientShadowAlpha;
103    private final int mSpotShadowAlpha;
104
105    private long mNativeProxy;
106    private boolean mInitialized = false;
107    private RenderNode mRootNode;
108    private Choreographer mChoreographer;
109    private boolean mRootNodeNeedsUpdate;
110
111    ThreadedRenderer(Context context, boolean translucent) {
112        final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
113        mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
114        mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
115        mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
116        mAmbientShadowAlpha =
117                (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
118        mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
119        a.recycle();
120
121        long rootNodePtr = nCreateRootRenderNode();
122        mRootNode = RenderNode.adopt(rootNodePtr);
123        mRootNode.setClipToBounds(false);
124        mNativeProxy = nCreateProxy(translucent, rootNodePtr);
125
126        ProcessInitializer.sInstance.init(context, mNativeProxy);
127
128        loadSystemProperties();
129    }
130
131    @Override
132    void destroy() {
133        mInitialized = false;
134        updateEnabledState(null);
135        nDestroy(mNativeProxy);
136    }
137
138    private void updateEnabledState(Surface surface) {
139        if (surface == null || !surface.isValid()) {
140            setEnabled(false);
141        } else {
142            setEnabled(mInitialized);
143        }
144    }
145
146    @Override
147    boolean initialize(Surface surface) throws OutOfResourcesException {
148        mInitialized = true;
149        updateEnabledState(surface);
150        boolean status = nInitialize(mNativeProxy, surface);
151        return status;
152    }
153
154    @Override
155    void updateSurface(Surface surface) throws OutOfResourcesException {
156        updateEnabledState(surface);
157        nUpdateSurface(mNativeProxy, surface);
158    }
159
160    @Override
161    boolean pauseSurface(Surface surface) {
162        return nPauseSurface(mNativeProxy, surface);
163    }
164
165    @Override
166    void destroyHardwareResources(View view) {
167        destroyResources(view);
168        nDestroyHardwareResources(mNativeProxy);
169    }
170
171    private static void destroyResources(View view) {
172        view.destroyHardwareResources();
173
174        if (view instanceof ViewGroup) {
175            ViewGroup group = (ViewGroup) view;
176
177            int count = group.getChildCount();
178            for (int i = 0; i < count; i++) {
179                destroyResources(group.getChildAt(i));
180            }
181        }
182    }
183
184    @Override
185    void invalidate(Surface surface) {
186        updateSurface(surface);
187    }
188
189    @Override
190    void detachSurfaceTexture(long hardwareLayer) {
191        nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
192    }
193
194    @Override
195    void setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets) {
196        mWidth = width;
197        mHeight = height;
198
199        if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0
200                || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) {
201            mHasInsets = true;
202            mInsetLeft = surfaceInsets.left;
203            mInsetTop = surfaceInsets.top;
204            mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
205            mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
206
207            // If the surface has insets, it can't be opaque.
208            setOpaque(false);
209        } else {
210            mHasInsets = false;
211            mInsetLeft = 0;
212            mInsetTop = 0;
213            mSurfaceWidth = width;
214            mSurfaceHeight = height;
215        }
216
217        mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
218        nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, mLightRadius,
219                mAmbientShadowAlpha, mSpotShadowAlpha);
220
221        setLightCenter(attachInfo);
222    }
223
224    @Override
225    void setLightCenter(AttachInfo attachInfo) {
226        // Adjust light position for window offsets.
227        final Point displaySize = attachInfo.mPoint;
228        attachInfo.mDisplay.getRealSize(displaySize);
229        final float lightX = displaySize.x / 2f - attachInfo.mWindowLeft;
230        final float lightY = mLightY - attachInfo.mWindowTop;
231
232        nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ);
233    }
234
235    @Override
236    void setOpaque(boolean opaque) {
237        nSetOpaque(mNativeProxy, opaque && !mHasInsets);
238    }
239
240    @Override
241    int getWidth() {
242        return mWidth;
243    }
244
245    @Override
246    int getHeight() {
247        return mHeight;
248    }
249
250    @Override
251    void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
252        pw.flush();
253        int flags = 0;
254        for (int i = 0; i < args.length; i++) {
255            switch (args[i]) {
256                case "framestats":
257                    flags |= FLAG_DUMP_FRAMESTATS;
258                    break;
259                case "reset":
260                    flags |= FLAG_DUMP_RESET;
261                    break;
262            }
263        }
264        nDumpProfileInfo(mNativeProxy, fd, flags);
265    }
266
267    @Override
268    boolean loadSystemProperties() {
269        boolean changed = nLoadSystemProperties(mNativeProxy);
270        if (changed) {
271            invalidateRoot();
272        }
273        return changed;
274    }
275
276    private void updateViewTreeDisplayList(View view) {
277        view.mPrivateFlags |= View.PFLAG_DRAWN;
278        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
279                == View.PFLAG_INVALIDATED;
280        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
281        view.updateDisplayListIfDirty();
282        view.mRecreateDisplayList = false;
283    }
284
285    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
286        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
287        updateViewTreeDisplayList(view);
288
289        if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
290            DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
291            try {
292                final int saveCount = canvas.save();
293                canvas.translate(mInsetLeft, mInsetTop);
294                callbacks.onHardwarePreDraw(canvas);
295
296                canvas.insertReorderBarrier();
297                canvas.drawRenderNode(view.updateDisplayListIfDirty());
298                canvas.insertInorderBarrier();
299
300                callbacks.onHardwarePostDraw(canvas);
301                canvas.restoreToCount(saveCount);
302                mRootNodeNeedsUpdate = false;
303            } finally {
304                mRootNode.end(canvas);
305            }
306        }
307        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
308    }
309
310    @Override
311    void invalidateRoot() {
312        mRootNodeNeedsUpdate = true;
313    }
314
315    @Override
316    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
317        attachInfo.mIgnoreDirtyState = true;
318
319        final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
320        choreographer.mFrameInfo.markDrawStart();
321
322        updateRootDisplayList(view, callbacks);
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        final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
341        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
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);
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        nSetName(mNativeProxy, name);
388    }
389
390    @Override
391    void fence() {
392        nFence(mNativeProxy);
393    }
394
395    @Override
396    void stopDrawing() {
397        nStopDrawing(mNativeProxy);
398    }
399
400    @Override
401    public void notifyFramePending() {
402        nNotifyFramePending(mNativeProxy);
403    }
404
405    @Override
406    void registerAnimatingRenderNode(RenderNode animator) {
407        nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
408    }
409
410    @Override
411    protected void finalize() throws Throwable {
412        try {
413            nDeleteProxy(mNativeProxy);
414            mNativeProxy = 0;
415        } finally {
416            super.finalize();
417        }
418    }
419
420    static void trimMemory(int level) {
421        nTrimMemory(level);
422    }
423
424    public static void overrideProperty(@NonNull String name, @NonNull String value) {
425        if (name == null || value == null) {
426            throw new IllegalArgumentException("name and value must be non-null");
427        }
428        nOverrideProperty(name, value);
429    }
430
431    public static void dumpProfileData(byte[] data, FileDescriptor fd) {
432        nDumpProfileData(data, fd);
433    }
434
435    private static class ProcessInitializer {
436        static ProcessInitializer sInstance = new ProcessInitializer();
437        private static IBinder sProcToken;
438
439        private boolean mInitialized = false;
440
441        private ProcessInitializer() {}
442
443        synchronized void init(Context context, long renderProxy) {
444            if (mInitialized) return;
445            mInitialized = true;
446            initGraphicsStats(context, renderProxy);
447            initAssetAtlas(context, renderProxy);
448        }
449
450        private static void initGraphicsStats(Context context, long renderProxy) {
451            try {
452                IBinder binder = ServiceManager.getService("graphicsstats");
453                if (binder == null) return;
454                IGraphicsStats graphicsStatsService = IGraphicsStats.Stub
455                        .asInterface(binder);
456                sProcToken = new Binder();
457                final String pkg = context.getApplicationInfo().packageName;
458                ParcelFileDescriptor pfd = graphicsStatsService.
459                        requestBufferForProcess(pkg, sProcToken);
460                nSetProcessStatsBuffer(renderProxy, pfd.getFd());
461                pfd.close();
462            } catch (Throwable t) {
463                Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t);
464            }
465        }
466
467        private static void initAssetAtlas(Context context, long renderProxy) {
468            IBinder binder = ServiceManager.getService("assetatlas");
469            if (binder == null) return;
470
471            IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
472            try {
473                if (atlas.isCompatible(android.os.Process.myPpid())) {
474                    GraphicBuffer buffer = atlas.getBuffer();
475                    if (buffer != null) {
476                        long[] map = atlas.getMap();
477                        if (map != null) {
478                            nSetAtlas(renderProxy, buffer, map);
479                        }
480                        // If IAssetAtlas is not the same class as the IBinder
481                        // we are using a remote service and we can safely
482                        // destroy the graphic buffer
483                        if (atlas.getClass() != binder.getClass()) {
484                            buffer.destroy();
485                        }
486                    }
487                }
488            } catch (RemoteException e) {
489                Log.w(LOG_TAG, "Could not acquire atlas", e);
490            }
491        }
492    }
493
494    static native void setupShadersDiskCache(String cacheFile);
495
496    private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
497    private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
498
499    private static native long nCreateRootRenderNode();
500    private static native long nCreateProxy(boolean translucent, long rootRenderNode);
501    private static native void nDeleteProxy(long nativeProxy);
502
503    private static native boolean nLoadSystemProperties(long nativeProxy);
504    private static native void nSetName(long nativeProxy, String name);
505
506    private static native boolean nInitialize(long nativeProxy, Surface window);
507    private static native void nUpdateSurface(long nativeProxy, Surface window);
508    private static native boolean nPauseSurface(long nativeProxy, Surface window);
509    private static native void nSetup(long nativeProxy, int width, int height,
510            float lightRadius, int ambientShadowAlpha, int spotShadowAlpha);
511    private static native void nSetLightCenter(long nativeProxy,
512            float lightX, float lightY, float lightZ);
513    private static native void nSetOpaque(long nativeProxy, boolean opaque);
514    private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
515    private static native void nDestroy(long nativeProxy);
516    private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
517
518    private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
519
520    private static native long nCreateTextureLayer(long nativeProxy);
521    private static native void nBuildLayer(long nativeProxy, long node);
522    private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap);
523    private static native void nPushLayerUpdate(long nativeProxy, long layer);
524    private static native void nCancelLayerUpdate(long nativeProxy, long layer);
525    private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
526
527    private static native void nDestroyHardwareResources(long nativeProxy);
528    private static native void nTrimMemory(int level);
529    private static native void nOverrideProperty(String name, String value);
530
531    private static native void nFence(long nativeProxy);
532    private static native void nStopDrawing(long nativeProxy);
533    private static native void nNotifyFramePending(long nativeProxy);
534
535    private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
536            @DumpFlags int dumpFlags);
537    private static native void nDumpProfileData(byte[] data, FileDescriptor fd);
538}
539