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 
17 package android.view;
18 
19 import com.android.internal.R;
20 
21 import android.content.Context;
22 import android.content.res.Resources;
23 import android.content.res.TypedArray;
24 import android.graphics.Bitmap;
25 import android.graphics.Rect;
26 import android.graphics.drawable.Drawable;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.os.SystemProperties;
31 import android.os.Trace;
32 import android.util.Log;
33 import android.util.LongSparseArray;
34 import android.util.TimeUtils;
35 import android.view.Surface.OutOfResourcesException;
36 import android.view.View.AttachInfo;
37 
38 import java.io.FileDescriptor;
39 import java.io.PrintWriter;
40 import java.util.ArrayList;
41 import 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  */
62 public 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     // Size of the rendered content.
78     private int mWidth, mHeight;
79 
80     // Actual size of the drawing surface.
81     private int mSurfaceWidth, mSurfaceHeight;
82 
83     // Insets between the drawing surface and rendered content. These are
84     // applied as translation when updating the root render node.
85     private int mInsetTop, mInsetLeft;
86 
87     // Whether the surface has insets. Used to protect opacity.
88     private boolean mHasInsets;
89 
90     // Light and shadow properties specified by the theme.
91     private final float mLightY;
92     private final float mLightZ;
93     private final float mLightRadius;
94     private final int mAmbientShadowAlpha;
95     private final int mSpotShadowAlpha;
96 
97     private long mNativeProxy;
98     private boolean mInitialized = false;
99     private RenderNode mRootNode;
100     private Choreographer mChoreographer;
101     private boolean mProfilingEnabled;
102     private boolean mRootNodeNeedsUpdate;
103 
ThreadedRenderer(Context context, boolean translucent)104     ThreadedRenderer(Context context, boolean translucent) {
105         final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
106         mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
107         mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
108         mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
109         mAmbientShadowAlpha =
110                 (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
111         mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
112         a.recycle();
113 
114         long rootNodePtr = nCreateRootRenderNode();
115         mRootNode = RenderNode.adopt(rootNodePtr);
116         mRootNode.setClipToBounds(false);
117         mNativeProxy = nCreateProxy(translucent, rootNodePtr);
118 
119         AtlasInitializer.sInstance.init(context, mNativeProxy);
120 
121         // Setup timing
122         mChoreographer = Choreographer.getInstance();
123         nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
124 
125         loadSystemProperties();
126     }
127 
128     @Override
destroy()129     void destroy() {
130         mInitialized = false;
131         updateEnabledState(null);
132         nDestroy(mNativeProxy);
133     }
134 
updateEnabledState(Surface surface)135     private void updateEnabledState(Surface surface) {
136         if (surface == null || !surface.isValid()) {
137             setEnabled(false);
138         } else {
139             setEnabled(mInitialized);
140         }
141     }
142 
143     @Override
initialize(Surface surface)144     boolean initialize(Surface surface) throws OutOfResourcesException {
145         mInitialized = true;
146         updateEnabledState(surface);
147         boolean status = nInitialize(mNativeProxy, surface);
148         surface.allocateBuffers();
149         return status;
150     }
151 
152     @Override
updateSurface(Surface surface)153     void updateSurface(Surface surface) throws OutOfResourcesException {
154         updateEnabledState(surface);
155         nUpdateSurface(mNativeProxy, surface);
156     }
157 
158     @Override
pauseSurface(Surface surface)159     boolean pauseSurface(Surface surface) {
160         return nPauseSurface(mNativeProxy, surface);
161     }
162 
163     @Override
destroyHardwareResources(View view)164     void destroyHardwareResources(View view) {
165         destroyResources(view);
166         nDestroyHardwareResources(mNativeProxy);
167     }
168 
destroyResources(View view)169     private static void destroyResources(View view) {
170         view.destroyHardwareResources();
171 
172         if (view instanceof ViewGroup) {
173             ViewGroup group = (ViewGroup) view;
174 
175             int count = group.getChildCount();
176             for (int i = 0; i < count; i++) {
177                 destroyResources(group.getChildAt(i));
178             }
179         }
180     }
181 
182     @Override
invalidate(Surface surface)183     void invalidate(Surface surface) {
184         updateSurface(surface);
185     }
186 
187     @Override
detachSurfaceTexture(long hardwareLayer)188     void detachSurfaceTexture(long hardwareLayer) {
189         nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
190     }
191 
192     @Override
setup(int width, int height, Rect surfaceInsets)193     void setup(int width, int height, Rect surfaceInsets) {
194         final float lightX = width / 2.0f;
195         mWidth = width;
196         mHeight = height;
197         if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0
198                 || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) {
199             mHasInsets = true;
200             mInsetLeft = surfaceInsets.left;
201             mInsetTop = surfaceInsets.top;
202             mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
203             mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
204 
205             // If the surface has insets, it can't be opaque.
206             setOpaque(false);
207         } else {
208             mHasInsets = false;
209             mInsetLeft = 0;
210             mInsetTop = 0;
211             mSurfaceWidth = width;
212             mSurfaceHeight = height;
213         }
214         mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
215         nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight,
216                 lightX, mLightY, mLightZ, mLightRadius,
217                 mAmbientShadowAlpha, mSpotShadowAlpha);
218     }
219 
220     @Override
setOpaque(boolean opaque)221     void setOpaque(boolean opaque) {
222         nSetOpaque(mNativeProxy, opaque && !mHasInsets);
223     }
224 
225     @Override
getWidth()226     int getWidth() {
227         return mWidth;
228     }
229 
230     @Override
getHeight()231     int getHeight() {
232         return mHeight;
233     }
234 
235     @Override
dumpGfxInfo(PrintWriter pw, FileDescriptor fd)236     void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
237         pw.flush();
238         nDumpProfileInfo(mNativeProxy, fd);
239     }
240 
search(String[] values, String value)241     private static int search(String[] values, String value) {
242         for (int i = 0; i < values.length; i++) {
243             if (values[i].equals(value)) return i;
244         }
245         return -1;
246     }
247 
checkIfProfilingRequested()248     private static boolean checkIfProfilingRequested() {
249         String profiling = SystemProperties.get(HardwareRenderer.PROFILE_PROPERTY);
250         int graphType = search(VISUALIZERS, profiling);
251         return (graphType >= 0) || Boolean.parseBoolean(profiling);
252     }
253 
254     @Override
loadSystemProperties()255     boolean loadSystemProperties() {
256         boolean changed = nLoadSystemProperties(mNativeProxy);
257         boolean wantProfiling = checkIfProfilingRequested();
258         if (wantProfiling != mProfilingEnabled) {
259             mProfilingEnabled = wantProfiling;
260             changed = true;
261         }
262         if (changed) {
263             invalidateRoot();
264         }
265         return changed;
266     }
267 
updateViewTreeDisplayList(View view)268     private void updateViewTreeDisplayList(View view) {
269         view.mPrivateFlags |= View.PFLAG_DRAWN;
270         view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
271                 == View.PFLAG_INVALIDATED;
272         view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
273         view.getDisplayList();
274         view.mRecreateDisplayList = false;
275     }
276 
updateRootDisplayList(View view, HardwareDrawCallbacks callbacks)277     private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
278         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
279         updateViewTreeDisplayList(view);
280 
281         if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
282             HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
283             try {
284                 final int saveCount = canvas.save();
285                 canvas.translate(mInsetLeft, mInsetTop);
286                 callbacks.onHardwarePreDraw(canvas);
287 
288                 canvas.insertReorderBarrier();
289                 canvas.drawRenderNode(view.getDisplayList());
290                 canvas.insertInorderBarrier();
291 
292                 callbacks.onHardwarePostDraw(canvas);
293                 canvas.restoreToCount(saveCount);
294                 mRootNodeNeedsUpdate = false;
295             } finally {
296                 mRootNode.end(canvas);
297             }
298         }
299         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
300     }
301 
302     @Override
invalidateRoot()303     void invalidateRoot() {
304         mRootNodeNeedsUpdate = true;
305     }
306 
307     @Override
draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks)308     void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
309         attachInfo.mIgnoreDirtyState = true;
310         long frameTimeNanos = mChoreographer.getFrameTimeNanos();
311         attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
312 
313         long recordDuration = 0;
314         if (mProfilingEnabled) {
315             recordDuration = System.nanoTime();
316         }
317 
318         updateRootDisplayList(view, callbacks);
319 
320         if (mProfilingEnabled) {
321             recordDuration = System.nanoTime() - recordDuration;
322         }
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         int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
341                 recordDuration, view.getResources().getDisplayMetrics().density);
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 
invokeFunctor(long functor, boolean waitForCompletion)354     static void invokeFunctor(long functor, boolean waitForCompletion) {
355         nInvokeFunctor(functor, waitForCompletion);
356     }
357 
358     @Override
createTextureLayer()359     HardwareLayer createTextureLayer() {
360         long layer = nCreateTextureLayer(mNativeProxy);
361         return HardwareLayer.adoptTextureLayer(this, layer);
362     }
363 
364     @Override
buildLayer(RenderNode node)365     void buildLayer(RenderNode node) {
366         nBuildLayer(mNativeProxy, node.getNativeDisplayList());
367     }
368 
369     @Override
copyLayerInto(final HardwareLayer layer, final Bitmap bitmap)370     boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
371         return nCopyLayerInto(mNativeProxy,
372                 layer.getDeferredLayerUpdater(), bitmap.mNativeBitmap);
373     }
374 
375     @Override
pushLayerUpdate(HardwareLayer layer)376     void pushLayerUpdate(HardwareLayer layer) {
377         nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
378     }
379 
380     @Override
onLayerDestroyed(HardwareLayer layer)381     void onLayerDestroyed(HardwareLayer layer) {
382         nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
383     }
384 
385     @Override
setName(String name)386     void setName(String name) {
387     }
388 
389     @Override
fence()390     void fence() {
391         nFence(mNativeProxy);
392     }
393 
394     @Override
stopDrawing()395     void stopDrawing() {
396         nStopDrawing(mNativeProxy);
397     }
398 
399     @Override
notifyFramePending()400     public void notifyFramePending() {
401         nNotifyFramePending(mNativeProxy);
402     }
403 
404     @Override
registerAnimatingRenderNode(RenderNode animator)405     void registerAnimatingRenderNode(RenderNode animator) {
406         nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
407     }
408 
409     @Override
finalize()410     protected void finalize() throws Throwable {
411         try {
412             nDeleteProxy(mNativeProxy);
413             mNativeProxy = 0;
414         } finally {
415             super.finalize();
416         }
417     }
418 
trimMemory(int level)419     static void trimMemory(int level) {
420         nTrimMemory(level);
421     }
422 
423     private static class AtlasInitializer {
424         static AtlasInitializer sInstance = new AtlasInitializer();
425 
426         private boolean mInitialized = false;
427 
AtlasInitializer()428         private AtlasInitializer() {}
429 
init(Context context, long renderProxy)430         synchronized void init(Context context, long renderProxy) {
431             if (mInitialized) return;
432             IBinder binder = ServiceManager.getService("assetatlas");
433             if (binder == null) return;
434 
435             IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
436             try {
437                 if (atlas.isCompatible(android.os.Process.myPpid())) {
438                     GraphicBuffer buffer = atlas.getBuffer();
439                     if (buffer != null) {
440                         long[] map = atlas.getMap();
441                         if (map != null) {
442                             // TODO Remove after fixing b/15425820
443                             validateMap(context, map);
444                             nSetAtlas(renderProxy, buffer, map);
445                             mInitialized = true;
446                         }
447                         // If IAssetAtlas is not the same class as the IBinder
448                         // we are using a remote service and we can safely
449                         // destroy the graphic buffer
450                         if (atlas.getClass() != binder.getClass()) {
451                             buffer.destroy();
452                         }
453                     }
454                 }
455             } catch (RemoteException e) {
456                 Log.w(LOG_TAG, "Could not acquire atlas", e);
457             }
458         }
459 
validateMap(Context context, long[] map)460         private static void validateMap(Context context, long[] map) {
461             Log.d("Atlas", "Validating map...");
462             HashSet<Long> preloadedPointers = new HashSet<Long>();
463 
464             // We only care about drawables that hold bitmaps
465             final Resources resources = context.getResources();
466             final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
467 
468             final int count = drawables.size();
469             ArrayList<Bitmap> tmpList = new ArrayList<Bitmap>();
470             for (int i = 0; i < count; i++) {
471                 drawables.valueAt(i).addAtlasableBitmaps(tmpList);
472                 for (int j = 0; j < tmpList.size(); j++) {
473                     preloadedPointers.add(tmpList.get(j).mNativeBitmap);
474                 }
475                 tmpList.clear();
476             }
477 
478             for (int i = 0; i < map.length; i += 4) {
479                 if (!preloadedPointers.contains(map[i])) {
480                     Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i]));
481                     map[i] = 0;
482                 }
483             }
484         }
485     }
486 
setupShadersDiskCache(String cacheFile)487     static native void setupShadersDiskCache(String cacheFile);
488 
nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map)489     private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
490 
nCreateRootRenderNode()491     private static native long nCreateRootRenderNode();
nCreateProxy(boolean translucent, long rootRenderNode)492     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
nDeleteProxy(long nativeProxy)493     private static native void nDeleteProxy(long nativeProxy);
494 
nSetFrameInterval(long nativeProxy, long frameIntervalNanos)495     private static native void nSetFrameInterval(long nativeProxy, long frameIntervalNanos);
nLoadSystemProperties(long nativeProxy)496     private static native boolean nLoadSystemProperties(long nativeProxy);
497 
nInitialize(long nativeProxy, Surface window)498     private static native boolean nInitialize(long nativeProxy, Surface window);
nUpdateSurface(long nativeProxy, Surface window)499     private static native void nUpdateSurface(long nativeProxy, Surface window);
nPauseSurface(long nativeProxy, Surface window)500     private static native boolean nPauseSurface(long nativeProxy, Surface window);
nSetup(long nativeProxy, int width, int height, float lightX, float lightY, float lightZ, float lightRadius, int ambientShadowAlpha, int spotShadowAlpha)501     private static native void nSetup(long nativeProxy, int width, int height,
502             float lightX, float lightY, float lightZ, float lightRadius,
503             int ambientShadowAlpha, int spotShadowAlpha);
nSetOpaque(long nativeProxy, boolean opaque)504     private static native void nSetOpaque(long nativeProxy, boolean opaque);
nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos, long recordDuration, float density)505     private static native int nSyncAndDrawFrame(long nativeProxy,
506             long frameTimeNanos, long recordDuration, float density);
nDestroy(long nativeProxy)507     private static native void nDestroy(long nativeProxy);
nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode)508     private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
509 
nInvokeFunctor(long functor, boolean waitForCompletion)510     private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
511 
nCreateTextureLayer(long nativeProxy)512     private static native long nCreateTextureLayer(long nativeProxy);
nBuildLayer(long nativeProxy, long node)513     private static native void nBuildLayer(long nativeProxy, long node);
nCopyLayerInto(long nativeProxy, long layer, long bitmap)514     private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
nPushLayerUpdate(long nativeProxy, long layer)515     private static native void nPushLayerUpdate(long nativeProxy, long layer);
nCancelLayerUpdate(long nativeProxy, long layer)516     private static native void nCancelLayerUpdate(long nativeProxy, long layer);
nDetachSurfaceTexture(long nativeProxy, long layer)517     private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
518 
nDestroyHardwareResources(long nativeProxy)519     private static native void nDestroyHardwareResources(long nativeProxy);
nTrimMemory(int level)520     private static native void nTrimMemory(int level);
521 
nFence(long nativeProxy)522     private static native void nFence(long nativeProxy);
nStopDrawing(long nativeProxy)523     private static native void nStopDrawing(long nativeProxy);
nNotifyFramePending(long nativeProxy)524     private static native void nNotifyFramePending(long nativeProxy);
525 
nDumpProfileInfo(long nativeProxy, FileDescriptor fd)526     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd);
527 }
528