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