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