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