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 android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.content.Context;
22 import android.content.res.TypedArray;
23 import android.graphics.Bitmap;
24 import android.graphics.Point;
25 import android.graphics.Rect;
26 import android.os.Binder;
27 import android.os.IBinder;
28 import android.os.ParcelFileDescriptor;
29 import android.os.RemoteException;
30 import android.os.ServiceManager;
31 import android.os.Trace;
32 import android.util.Log;
33 import android.view.Surface.OutOfResourcesException;
34 import android.view.View.AttachInfo;
35 
36 import com.android.internal.R;
37 
38 import java.io.FileDescriptor;
39 import java.io.PrintWriter;
40 import java.lang.annotation.Retention;
41 import 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  */
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     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 
ThreadedRenderer(Context context, boolean translucent)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
destroy()132     void destroy() {
133         mInitialized = false;
134         updateEnabledState(null);
135         nDestroy(mNativeProxy);
136     }
137 
updateEnabledState(Surface surface)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
initialize(Surface surface)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
updateSurface(Surface surface)155     void updateSurface(Surface surface) throws OutOfResourcesException {
156         updateEnabledState(surface);
157         nUpdateSurface(mNativeProxy, surface);
158     }
159 
160     @Override
pauseSurface(Surface surface)161     boolean pauseSurface(Surface surface) {
162         return nPauseSurface(mNativeProxy, surface);
163     }
164 
165     @Override
destroyHardwareResources(View view)166     void destroyHardwareResources(View view) {
167         destroyResources(view);
168         nDestroyHardwareResources(mNativeProxy);
169     }
170 
destroyResources(View view)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
invalidate(Surface surface)185     void invalidate(Surface surface) {
186         updateSurface(surface);
187     }
188 
189     @Override
detachSurfaceTexture(long hardwareLayer)190     void detachSurfaceTexture(long hardwareLayer) {
191         nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
192     }
193 
194     @Override
setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets)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
setLightCenter(AttachInfo attachInfo)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
setOpaque(boolean opaque)236     void setOpaque(boolean opaque) {
237         nSetOpaque(mNativeProxy, opaque && !mHasInsets);
238     }
239 
240     @Override
getWidth()241     int getWidth() {
242         return mWidth;
243     }
244 
245     @Override
getHeight()246     int getHeight() {
247         return mHeight;
248     }
249 
250     @Override
dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args)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
loadSystemProperties()268     boolean loadSystemProperties() {
269         boolean changed = nLoadSystemProperties(mNativeProxy);
270         if (changed) {
271             invalidateRoot();
272         }
273         return changed;
274     }
275 
updateViewTreeDisplayList(View view)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 
updateRootDisplayList(View view, HardwareDrawCallbacks callbacks)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
invalidateRoot()311     void invalidateRoot() {
312         mRootNodeNeedsUpdate = true;
313     }
314 
315     @Override
draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks)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 
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);
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         nSetName(mNativeProxy, name);
388     }
389 
390     @Override
fence()391     void fence() {
392         nFence(mNativeProxy);
393     }
394 
395     @Override
stopDrawing()396     void stopDrawing() {
397         nStopDrawing(mNativeProxy);
398     }
399 
400     @Override
notifyFramePending()401     public void notifyFramePending() {
402         nNotifyFramePending(mNativeProxy);
403     }
404 
405     @Override
registerAnimatingRenderNode(RenderNode animator)406     void registerAnimatingRenderNode(RenderNode animator) {
407         nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
408     }
409 
410     @Override
finalize()411     protected void finalize() throws Throwable {
412         try {
413             nDeleteProxy(mNativeProxy);
414             mNativeProxy = 0;
415         } finally {
416             super.finalize();
417         }
418     }
419 
trimMemory(int level)420     static void trimMemory(int level) {
421         nTrimMemory(level);
422     }
423 
overrideProperty(@onNull String name, @NonNull String value)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 
dumpProfileData(byte[] data, FileDescriptor fd)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 
ProcessInitializer()441         private ProcessInitializer() {}
442 
init(Context context, long renderProxy)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 
initGraphicsStats(Context context, long renderProxy)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 
initAssetAtlas(Context context, long renderProxy)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 
setupShadersDiskCache(String cacheFile)494     static native void setupShadersDiskCache(String cacheFile);
495 
nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map)496     private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
nSetProcessStatsBuffer(long nativeProxy, int fd)497     private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
498 
nCreateRootRenderNode()499     private static native long nCreateRootRenderNode();
nCreateProxy(boolean translucent, long rootRenderNode)500     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
nDeleteProxy(long nativeProxy)501     private static native void nDeleteProxy(long nativeProxy);
502 
nLoadSystemProperties(long nativeProxy)503     private static native boolean nLoadSystemProperties(long nativeProxy);
nSetName(long nativeProxy, String name)504     private static native void nSetName(long nativeProxy, String name);
505 
nInitialize(long nativeProxy, Surface window)506     private static native boolean nInitialize(long nativeProxy, Surface window);
nUpdateSurface(long nativeProxy, Surface window)507     private static native void nUpdateSurface(long nativeProxy, Surface window);
nPauseSurface(long nativeProxy, Surface window)508     private static native boolean nPauseSurface(long nativeProxy, Surface window);
nSetup(long nativeProxy, int width, int height, float lightRadius, int ambientShadowAlpha, int spotShadowAlpha)509     private static native void nSetup(long nativeProxy, int width, int height,
510             float lightRadius, int ambientShadowAlpha, int spotShadowAlpha);
nSetLightCenter(long nativeProxy, float lightX, float lightY, float lightZ)511     private static native void nSetLightCenter(long nativeProxy,
512             float lightX, float lightY, float lightZ);
nSetOpaque(long nativeProxy, boolean opaque)513     private static native void nSetOpaque(long nativeProxy, boolean opaque);
nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size)514     private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
nDestroy(long nativeProxy)515     private static native void nDestroy(long nativeProxy);
nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode)516     private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
517 
nInvokeFunctor(long functor, boolean waitForCompletion)518     private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
519 
nCreateTextureLayer(long nativeProxy)520     private static native long nCreateTextureLayer(long nativeProxy);
nBuildLayer(long nativeProxy, long node)521     private static native void nBuildLayer(long nativeProxy, long node);
nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap)522     private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap);
nPushLayerUpdate(long nativeProxy, long layer)523     private static native void nPushLayerUpdate(long nativeProxy, long layer);
nCancelLayerUpdate(long nativeProxy, long layer)524     private static native void nCancelLayerUpdate(long nativeProxy, long layer);
nDetachSurfaceTexture(long nativeProxy, long layer)525     private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
526 
nDestroyHardwareResources(long nativeProxy)527     private static native void nDestroyHardwareResources(long nativeProxy);
nTrimMemory(int level)528     private static native void nTrimMemory(int level);
nOverrideProperty(String name, String value)529     private static native void nOverrideProperty(String name, String value);
530 
nFence(long nativeProxy)531     private static native void nFence(long nativeProxy);
nStopDrawing(long nativeProxy)532     private static native void nStopDrawing(long nativeProxy);
nNotifyFramePending(long nativeProxy)533     private static native void nNotifyFramePending(long nativeProxy);
534 
nDumpProfileInfo(long nativeProxy, FileDescriptor fd, @DumpFlags int dumpFlags)535     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
536             @DumpFlags int dumpFlags);
nDumpProfileData(byte[] data, FileDescriptor fd)537     private static native void nDumpProfileData(byte[] data, FileDescriptor fd);
538 }
539