• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.NonNull;
20 import android.annotation.Nullable;
21 import android.app.ActivityManager;
22 import android.content.Context;
23 import android.content.res.TypedArray;
24 import android.graphics.BLASTBufferQueue;
25 import android.graphics.FrameInfo;
26 import android.graphics.HardwareRenderer;
27 import android.graphics.Picture;
28 import android.graphics.RecordingCanvas;
29 import android.graphics.Rect;
30 import android.graphics.RenderNode;
31 import android.os.Trace;
32 import android.util.DisplayMetrics;
33 import android.util.Log;
34 import android.view.Surface.OutOfResourcesException;
35 import android.view.View.AttachInfo;
36 import android.view.animation.AnimationUtils;
37 
38 import com.android.internal.R;
39 
40 import java.io.FileDescriptor;
41 import java.io.PrintWriter;
42 import java.util.ArrayList;
43 
44 /**
45  * Threaded renderer that proxies the rendering to a render thread. Most calls
46  * are currently synchronous.
47  *
48  * The UI thread can block on the RenderThread, but RenderThread must never
49  * block on the UI thread.
50  *
51  * ThreadedRenderer creates an instance of RenderProxy. RenderProxy in turn creates
52  * and manages a CanvasContext on the RenderThread. The CanvasContext is fully managed
53  * by the lifecycle of the RenderProxy.
54  *
55  * Note that although currently the EGL context & surfaces are created & managed
56  * by the render thread, the goal is to move that into a shared structure that can
57  * be managed by both threads. EGLSurface creation & deletion should ideally be
58  * done on the UI thread and not the RenderThread to avoid stalling the
59  * RenderThread with surface buffer allocation.
60  *
61  * @hide
62  */
63 public final class ThreadedRenderer extends HardwareRenderer {
64     /**
65      * System property used to enable or disable threaded rendering profiling.
66      * The default value of this property is assumed to be false.
67      *
68      * When profiling is enabled, the adb shell dumpsys gfxinfo command will
69      * output extra information about the time taken to execute by the last
70      * frames.
71      *
72      * Possible values:
73      * "true", to enable profiling
74      * "visual_bars", to enable profiling and visualize the results on screen
75      * "false", to disable profiling
76      *
77      * @see #PROFILE_PROPERTY_VISUALIZE_BARS
78      *
79      * @hide
80      */
81     public static final String PROFILE_PROPERTY = "debug.hwui.profile";
82 
83     /**
84      * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
85      * value, profiling data will be visualized on screen as a bar chart.
86      *
87      * @hide
88      */
89     public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
90 
91     /**
92      * System property used to specify the number of frames to be used
93      * when doing threaded rendering profiling.
94      * The default value of this property is #PROFILE_MAX_FRAMES.
95      *
96      * When profiling is enabled, the adb shell dumpsys gfxinfo command will
97      * output extra information about the time taken to execute by the last
98      * frames.
99      *
100      * Possible values:
101      * "60", to set the limit of frames to 60
102      */
103     static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes";
104 
105     /**
106      * System property used to debug EGL configuration choice.
107      *
108      * Possible values:
109      * "choice", print the chosen configuration only
110      * "all", print all possible configurations
111      */
112     static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config";
113 
114     /**
115      * Turn on to draw dirty regions every other frame.
116      *
117      * Possible values:
118      * "true", to enable dirty regions debugging
119      * "false", to disable dirty regions debugging
120      *
121      * @hide
122      */
123     public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions";
124 
125     /**
126      * Turn on to flash hardware layers when they update.
127      *
128      * Possible values:
129      * "true", to enable hardware layers updates debugging
130      * "false", to disable hardware layers updates debugging
131      *
132      * @hide
133      */
134     public static final String DEBUG_SHOW_LAYERS_UPDATES_PROPERTY =
135             "debug.hwui.show_layers_updates";
136 
137     /**
138      * Controls overdraw debugging.
139      *
140      * Possible values:
141      * "false", to disable overdraw debugging
142      * "show", to show overdraw areas on screen
143      * "count", to display an overdraw counter
144      *
145      * @hide
146      */
147     public static final String DEBUG_OVERDRAW_PROPERTY = "debug.hwui.overdraw";
148 
149     /**
150      * Value for {@link #DEBUG_OVERDRAW_PROPERTY}. When the property is set to this
151      * value, overdraw will be shown on screen by coloring pixels.
152      *
153      * @hide
154      */
155     public static final String OVERDRAW_PROPERTY_SHOW = "show";
156 
157     /**
158      * Turn on to debug non-rectangular clip operations.
159      *
160      * Possible values:
161      * "hide", to disable this debug mode
162      * "highlight", highlight drawing commands tested against a non-rectangular clip
163      * "stencil", renders the clip region on screen when set
164      *
165      * @hide
166      */
167     public static final String DEBUG_SHOW_NON_RECTANGULAR_CLIP_PROPERTY =
168             "debug.hwui.show_non_rect_clip";
169 
170     /**
171      * Sets the FPS devisor to lower the FPS.
172      *
173      * Sets a positive integer as a divisor. 1 (the default value) menas the full FPS, and 2
174      * means half the full FPS.
175      *
176      *
177      * @hide
178      */
179     public static final String DEBUG_FPS_DIVISOR = "debug.hwui.fps_divisor";
180 
181     /**
182      * Forces smart-dark to be always on.
183      * @hide
184      */
185     public static final String DEBUG_FORCE_DARK = "debug.hwui.force_dark";
186 
187     public static int EGL_CONTEXT_PRIORITY_REALTIME_NV = 0x3357;
188     public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101;
189     public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
190     public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
191 
192     /**
193      * Further threaded renderer disabling for the system process.
194      *
195      * @hide
196      */
197     public static boolean sRendererEnabled = true;
198 
199     public static boolean sTrimForeground = false;
200 
201     /**
202      * Controls whether or not the renderer should aggressively trim
203      * memory. Note that this must not be set for any process that uses
204      * WebView! This should be only used by system_process or similar
205      * that do not go into the background.
206      */
enableForegroundTrimming()207     public static void enableForegroundTrimming() {
208         sTrimForeground = true;
209     }
210 
211     /**
212      * Initialize HWUI for being in a system process like system_server
213      * Should not be called in non-system processes
214      */
initForSystemProcess()215     public static void initForSystemProcess() {
216         // The system process on low-memory devices do not get to use hardware
217         // accelerated drawing, since this can add too much overhead to the
218         // process.
219         if (!ActivityManager.isHighEndGfx()) {
220             sRendererEnabled = false;
221         } else {
222             enableForegroundTrimming();
223         }
224     }
225 
226     /**
227      * Creates a threaded renderer using OpenGL.
228      *
229      * @param translucent True if the surface is translucent, false otherwise
230      *
231      * @return A threaded renderer backed by OpenGL.
232      */
create(Context context, boolean translucent, String name)233     public static ThreadedRenderer create(Context context, boolean translucent, String name) {
234         return new ThreadedRenderer(context, translucent, name);
235     }
236 
237     private static final String[] VISUALIZERS = {
238         PROFILE_PROPERTY_VISUALIZE_BARS,
239     };
240 
241     // Size of the rendered content.
242     private int mWidth, mHeight;
243 
244     // Actual size of the drawing surface.
245     private int mSurfaceWidth, mSurfaceHeight;
246 
247     // Insets between the drawing surface and rendered content. These are
248     // applied as translation when updating the root render node.
249     private int mInsetTop, mInsetLeft;
250 
251     // Light properties specified by the theme.
252     private final float mLightY;
253     private final float mLightZ;
254     private final float mLightRadius;
255 
256     private boolean mInitialized = false;
257     private boolean mRootNodeNeedsUpdate;
258 
259     private boolean mEnabled;
260     private boolean mRequested = true;
261 
262     /**
263      * This child class exists to break ownership cycles. ViewRootImpl owns a ThreadedRenderer
264      * which owns a WebViewOverlayProvider. WebViewOverlayProvider will in turn be set as
265      * the listener for HardwareRenderer callbacks. By keeping this a child class, there are
266      * no cycles in the chain. The ThreadedRenderer will remain GC-able if any callbacks are
267      * still outstanding, which will in turn release any JNI references to WebViewOverlayProvider.
268      */
269     private static final class WebViewOverlayProvider implements
270             PrepareSurfaceControlForWebviewCallback, ASurfaceTransactionCallback {
271         private static final boolean sOverlaysAreEnabled =
272                 HardwareRenderer.isWebViewOverlaysEnabled();
273         private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
274         private boolean mHasWebViewOverlays = false;
275         private BLASTBufferQueue mBLASTBufferQueue;
276         private SurfaceControl mSurfaceControl;
277 
setSurfaceControlOpaque(boolean opaque)278         public boolean setSurfaceControlOpaque(boolean opaque) {
279             synchronized (this) {
280                 if (mHasWebViewOverlays) return false;
281                 mTransaction.setOpaque(mSurfaceControl, opaque).apply();
282             }
283             return opaque;
284         }
285 
shouldEnableOverlaySupport()286         public boolean shouldEnableOverlaySupport() {
287             return sOverlaysAreEnabled && mSurfaceControl != null && mBLASTBufferQueue != null;
288         }
289 
setSurfaceControl(SurfaceControl surfaceControl)290         public void setSurfaceControl(SurfaceControl surfaceControl) {
291             synchronized (this) {
292                 mSurfaceControl = surfaceControl;
293                 if (mSurfaceControl != null && mHasWebViewOverlays) {
294                     mTransaction.setOpaque(surfaceControl, false).apply();
295                 }
296             }
297         }
298 
setBLASTBufferQueue(BLASTBufferQueue bufferQueue)299         public void setBLASTBufferQueue(BLASTBufferQueue bufferQueue) {
300             synchronized (this) {
301                 mBLASTBufferQueue = bufferQueue;
302             }
303         }
304 
305         @Override
prepare()306         public void prepare() {
307             synchronized (this) {
308                 mHasWebViewOverlays = true;
309                 if (mSurfaceControl != null) {
310                     mTransaction.setOpaque(mSurfaceControl, false).apply();
311                 }
312             }
313         }
314 
315         @Override
onMergeTransaction(long nativeTransactionObj, long aSurfaceControlNativeObj, long frameNr)316         public boolean onMergeTransaction(long nativeTransactionObj,
317                 long aSurfaceControlNativeObj, long frameNr) {
318             synchronized (this) {
319                 if (mBLASTBufferQueue == null) {
320                     return false;
321                 } else {
322                     mBLASTBufferQueue.mergeWithNextTransaction(nativeTransactionObj, frameNr);
323                     return true;
324                 }
325             }
326         }
327     }
328 
329     private final WebViewOverlayProvider mWebViewOverlayProvider = new WebViewOverlayProvider();
330     private boolean mWebViewOverlaysEnabled = false;
331 
332     @Nullable
333     private ArrayList<FrameDrawingCallback> mNextRtFrameCallbacks;
334 
ThreadedRenderer(Context context, boolean translucent, String name)335     ThreadedRenderer(Context context, boolean translucent, String name) {
336         super();
337         setName(name);
338         setOpaque(!translucent);
339 
340         final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
341         mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
342         mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
343         mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
344         float ambientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0);
345         float spotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0);
346         a.recycle();
347         setLightSourceAlpha(ambientShadowAlpha, spotShadowAlpha);
348     }
349 
350     @Override
destroy()351     public void destroy() {
352         mInitialized = false;
353         updateEnabledState(null);
354         super.destroy();
355     }
356 
357     /**
358      * Indicates whether threaded rendering is currently enabled.
359      *
360      * @return True if threaded rendering  is in use, false otherwise.
361      */
isEnabled()362     boolean isEnabled() {
363         return mEnabled;
364     }
365 
366     /**
367      * Indicates whether threaded rendering  is currently enabled.
368      *
369      * @param enabled True if the threaded renderer is in use, false otherwise.
370      */
setEnabled(boolean enabled)371     void setEnabled(boolean enabled) {
372         mEnabled = enabled;
373     }
374 
375     /**
376      * Indicates whether threaded rendering is currently request but not
377      * necessarily enabled yet.
378      *
379      * @return True if requested, false otherwise.
380      */
isRequested()381     boolean isRequested() {
382         return mRequested;
383     }
384 
385     /**
386      * Indicates whether threaded rendering is currently requested but not
387      * necessarily enabled yet.
388      */
setRequested(boolean requested)389     void setRequested(boolean requested) {
390         mRequested = requested;
391     }
392 
updateEnabledState(Surface surface)393     private void updateEnabledState(Surface surface) {
394         if (surface == null || !surface.isValid()) {
395             setEnabled(false);
396         } else {
397             setEnabled(mInitialized);
398         }
399     }
400 
401     /**
402      * Initializes the threaded renderer for the specified surface.
403      *
404      * @param surface The surface to render
405      *
406      * @return True if the initialization was successful, false otherwise.
407      */
initialize(Surface surface)408     boolean initialize(Surface surface) throws OutOfResourcesException {
409         boolean status = !mInitialized;
410         mInitialized = true;
411         updateEnabledState(surface);
412         setSurface(surface);
413         return status;
414     }
415 
416     /**
417      * Initializes the threaded renderer for the specified surface and setup the
418      * renderer for drawing, if needed. This is invoked when the ViewAncestor has
419      * potentially lost the threaded renderer. The threaded renderer should be
420      * reinitialized and setup when the render {@link #isRequested()} and
421      * {@link #isEnabled()}.
422      *
423      * @param width The width of the drawing surface.
424      * @param height The height of the drawing surface.
425      * @param attachInfo Information about the window.
426      * @param surface The surface to render
427      * @param surfaceInsets The drawing surface insets to apply
428      *
429      * @return true if the surface was initialized, false otherwise. Returning
430      *         false might mean that the surface was already initialized.
431      */
initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, Surface surface, Rect surfaceInsets)432     boolean initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
433             Surface surface, Rect surfaceInsets) throws OutOfResourcesException {
434         if (isRequested()) {
435             // We lost the gl context, so recreate it.
436             if (!isEnabled()) {
437                 if (initialize(surface)) {
438                     setup(width, height, attachInfo, surfaceInsets);
439                     return true;
440                 }
441             }
442         }
443         return false;
444     }
445 
446     /**
447      * Updates the threaded renderer for the specified surface.
448      *
449      * @param surface The surface to render
450      */
updateSurface(Surface surface)451     void updateSurface(Surface surface) throws OutOfResourcesException {
452         updateEnabledState(surface);
453         setSurface(surface);
454     }
455 
456     @Override
setSurface(Surface surface)457     public void setSurface(Surface surface) {
458         // TODO: Do we ever pass a non-null but isValid() = false surface?
459         // This is here to be super conservative for ViewRootImpl
460         if (surface != null && surface.isValid()) {
461             super.setSurface(surface);
462         } else {
463             super.setSurface(null);
464         }
465     }
466 
467     /**
468      * Registers a callback to be executed when the next frame is being drawn on RenderThread. This
469      * callback will be executed on a RenderThread worker thread, and only used for the next frame
470      * and thus it will only fire once.
471      *
472      * @param callback The callback to register.
473      */
registerRtFrameCallback(@onNull FrameDrawingCallback callback)474     void registerRtFrameCallback(@NonNull FrameDrawingCallback callback) {
475         if (mNextRtFrameCallbacks == null) {
476             mNextRtFrameCallbacks = new ArrayList<>();
477         }
478         mNextRtFrameCallbacks.add(callback);
479     }
480 
481     /**
482      * Remove a frame drawing callback that was added via
483      * {@link #registerRtFrameCallback(FrameDrawingCallback)}
484      *
485      * @param callback The callback to unregister.
486      */
unregisterRtFrameCallback(@onNull FrameDrawingCallback callback)487     void unregisterRtFrameCallback(@NonNull FrameDrawingCallback callback) {
488         if (mNextRtFrameCallbacks == null) {
489             return;
490         }
491         mNextRtFrameCallbacks.remove(callback);
492     }
493 
494     /**
495      * Destroys all hardware rendering resources associated with the specified
496      * view hierarchy.
497      *
498      * @param view The root of the view hierarchy
499      */
destroyHardwareResources(View view)500     void destroyHardwareResources(View view) {
501         destroyResources(view);
502         clearContent();
503     }
504 
destroyResources(View view)505     private static void destroyResources(View view) {
506         view.destroyHardwareResources();
507     }
508 
509     /**
510      * Sets up the renderer for drawing.
511      *
512      * @param width The width of the drawing surface.
513      * @param height The height of the drawing surface.
514      * @param attachInfo Information about the window.
515      * @param surfaceInsets The drawing surface insets to apply
516      */
setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets)517     void setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets) {
518         mWidth = width;
519         mHeight = height;
520 
521         if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0
522                 || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) {
523             mInsetLeft = surfaceInsets.left;
524             mInsetTop = surfaceInsets.top;
525             mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
526             mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
527 
528             // If the surface has insets, it can't be opaque.
529             setOpaque(false);
530         } else {
531             mInsetLeft = 0;
532             mInsetTop = 0;
533             mSurfaceWidth = width;
534             mSurfaceHeight = height;
535         }
536 
537         mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
538 
539         setLightCenter(attachInfo);
540     }
541 
542     /**
543      * Whether or not the renderer owns the SurfaceControl's opacity. If true, use
544      * {@link #setSurfaceControlOpaque(boolean)} to update the opacity
545      */
rendererOwnsSurfaceControlOpacity()546     public boolean rendererOwnsSurfaceControlOpacity() {
547         return mWebViewOverlayProvider.mSurfaceControl != null;
548     }
549 
550     /**
551      * Sets the SurfaceControl's opacity that this HardwareRenderer is rendering onto. The renderer
552      * may opt to override the opacity, and will return the value that is ultimately set
553      *
554      * @return true if the surface is opaque, false otherwise
555      *
556      * @hide
557      */
setSurfaceControlOpaque(boolean opaque)558     public boolean setSurfaceControlOpaque(boolean opaque) {
559         return mWebViewOverlayProvider.setSurfaceControlOpaque(opaque);
560     }
561 
updateWebViewOverlayCallbacks()562     private void updateWebViewOverlayCallbacks() {
563         boolean shouldEnable = mWebViewOverlayProvider.shouldEnableOverlaySupport();
564         if (shouldEnable != mWebViewOverlaysEnabled) {
565             mWebViewOverlaysEnabled = shouldEnable;
566             if (shouldEnable) {
567                 setASurfaceTransactionCallback(mWebViewOverlayProvider);
568                 setPrepareSurfaceControlForWebviewCallback(mWebViewOverlayProvider);
569             } else {
570                 setASurfaceTransactionCallback(null);
571                 setPrepareSurfaceControlForWebviewCallback(null);
572             }
573         }
574     }
575 
576     @Override
setSurfaceControl(@ullable SurfaceControl surfaceControl)577     public void setSurfaceControl(@Nullable SurfaceControl surfaceControl) {
578         super.setSurfaceControl(surfaceControl);
579         mWebViewOverlayProvider.setSurfaceControl(surfaceControl);
580         updateWebViewOverlayCallbacks();
581     }
582 
583     /**
584      * Sets the BLASTBufferQueue being used for rendering. This is required to be specified
585      * for WebView overlay support
586      */
setBlastBufferQueue(@ullable BLASTBufferQueue blastBufferQueue)587     public void setBlastBufferQueue(@Nullable BLASTBufferQueue blastBufferQueue) {
588         mWebViewOverlayProvider.setBLASTBufferQueue(blastBufferQueue);
589         updateWebViewOverlayCallbacks();
590     }
591 
592     /**
593      * Updates the light position based on the position of the window.
594      *
595      * @param attachInfo Information about the window.
596      */
setLightCenter(AttachInfo attachInfo)597     void setLightCenter(AttachInfo attachInfo) {
598         // Adjust light position for window offsets.
599         DisplayMetrics displayMetrics = new DisplayMetrics();
600         attachInfo.mDisplay.getRealMetrics(displayMetrics);
601         final float lightX = displayMetrics.widthPixels / 2f - attachInfo.mWindowLeft;
602         final float lightY = mLightY - attachInfo.mWindowTop;
603         // To prevent shadow distortion on larger screens, scale the z position of the light source
604         // relative to the smallest screen dimension.
605         final float zRatio = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels)
606                 / (450f * displayMetrics.density);
607         final float zWeightedAdjustment = (zRatio + 2) / 3f;
608         final float lightZ = mLightZ * zWeightedAdjustment;
609 
610         setLightSourceGeometry(lightX, lightY, lightZ, mLightRadius);
611     }
612 
613     /**
614      * Gets the current width of the surface. This is the width that the surface
615      * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}.
616      *
617      * @return the current width of the surface
618      */
getWidth()619     int getWidth() {
620         return mWidth;
621     }
622 
623     /**
624      * Gets the current height of the surface. This is the height that the surface
625      * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}.
626      *
627      * @return the current width of the surface
628      */
getHeight()629     int getHeight() {
630         return mHeight;
631     }
632 
dumpArgsToFlags(String[] args)633     private static int dumpArgsToFlags(String[] args) {
634         // If there's no arguments, eg 'dumpsys gfxinfo', then dump everything.
635         // If there's a targetted package, eg 'dumpsys gfxinfo com.android.systemui', then only
636         // dump the summary information
637         if (args == null || args.length == 0) {
638             return FLAG_DUMP_ALL;
639         }
640         int flags = 0;
641         for (int i = 0; i < args.length; i++) {
642             switch (args[i]) {
643                 case "framestats":
644                     flags |= FLAG_DUMP_FRAMESTATS;
645                     break;
646                 case "reset":
647                     flags |= FLAG_DUMP_RESET;
648                     break;
649                 case "-a": // magic option passed when dumping a bugreport.
650                     flags = FLAG_DUMP_ALL;
651                     break;
652             }
653         }
654         return flags;
655     }
656 
657     /** @hide */
handleDumpGfxInfo(FileDescriptor fd, String[] args)658     public static void handleDumpGfxInfo(FileDescriptor fd, String[] args) {
659         dumpGlobalProfileInfo(fd, dumpArgsToFlags(args));
660         WindowManagerGlobal.getInstance().dumpGfxInfo(fd, args);
661     }
662 
663     /**
664      * Outputs extra debugging information in the specified file descriptor.
665      */
dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args)666     void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
667         pw.flush();
668         dumpProfileInfo(fd, dumpArgsToFlags(args));
669     }
670 
captureRenderingCommands()671     Picture captureRenderingCommands() {
672         return null;
673     }
674 
675     @Override
loadSystemProperties()676     public boolean loadSystemProperties() {
677         boolean changed = super.loadSystemProperties();
678         if (changed) {
679             invalidateRoot();
680         }
681         return changed;
682     }
683 
updateViewTreeDisplayList(View view)684     private void updateViewTreeDisplayList(View view) {
685         view.mPrivateFlags |= View.PFLAG_DRAWN;
686         view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
687                 == View.PFLAG_INVALIDATED;
688         view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
689         view.updateDisplayListIfDirty();
690         view.mRecreateDisplayList = false;
691     }
692 
updateRootDisplayList(View view, DrawCallbacks callbacks)693     private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
694         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
695         updateViewTreeDisplayList(view);
696 
697         // Consume and set the frame callback after we dispatch draw to the view above, but before
698         // onPostDraw below which may reset the callback for the next frame.  This ensures that
699         // updates to the frame callback during scroll handling will also apply in this frame.
700         if (mNextRtFrameCallbacks != null) {
701             final ArrayList<FrameDrawingCallback> frameCallbacks = mNextRtFrameCallbacks;
702             mNextRtFrameCallbacks = null;
703             setFrameCallback(new FrameDrawingCallback() {
704                 @Override
705                 public void onFrameDraw(long frame) {
706                 }
707 
708                 @Override
709                 public FrameCommitCallback onFrameDraw(int syncResult, long frame) {
710                     ArrayList<FrameCommitCallback> frameCommitCallbacks = new ArrayList<>();
711                     for (int i = 0; i < frameCallbacks.size(); ++i) {
712                         FrameCommitCallback frameCommitCallback = frameCallbacks.get(i)
713                                 .onFrameDraw(syncResult, frame);
714                         if (frameCommitCallback != null) {
715                             frameCommitCallbacks.add(frameCommitCallback);
716                         }
717                     }
718 
719                     if (frameCommitCallbacks.isEmpty()) {
720                         return null;
721                     }
722 
723                     return didProduceBuffer -> {
724                         for (int i = 0; i < frameCommitCallbacks.size(); ++i) {
725                             frameCommitCallbacks.get(i).onFrameCommit(didProduceBuffer);
726                         }
727                     };
728                 }
729             });
730         }
731 
732         if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
733             RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);
734             try {
735                 final int saveCount = canvas.save();
736                 canvas.translate(mInsetLeft, mInsetTop);
737                 callbacks.onPreDraw(canvas);
738 
739                 canvas.enableZ();
740                 canvas.drawRenderNode(view.updateDisplayListIfDirty());
741                 canvas.disableZ();
742 
743                 callbacks.onPostDraw(canvas);
744                 canvas.restoreToCount(saveCount);
745                 mRootNodeNeedsUpdate = false;
746             } finally {
747                 mRootNode.endRecording();
748             }
749         }
750         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
751     }
752 
753     /**
754      * Interface used to receive callbacks whenever a view is drawn by
755      * a threaded renderer instance.
756      */
757     interface DrawCallbacks {
758         /**
759          * Invoked before a view is drawn by a threaded renderer.
760          * This method can be used to apply transformations to the
761          * canvas but no drawing command should be issued.
762          *
763          * @param canvas The Canvas used to render the view.
764          */
onPreDraw(RecordingCanvas canvas)765         void onPreDraw(RecordingCanvas canvas);
766 
767         /**
768          * Invoked after a view is drawn by a threaded renderer.
769          * It is safe to invoke drawing commands from this method.
770          *
771          * @param canvas The Canvas used to render the view.
772          */
onPostDraw(RecordingCanvas canvas)773         void onPostDraw(RecordingCanvas canvas);
774     }
775 
776     /**
777      *  Indicates that the content drawn by DrawCallbacks needs to
778      *  be updated, which will be done by the next call to draw()
779      */
invalidateRoot()780     void invalidateRoot() {
781         mRootNodeNeedsUpdate = true;
782     }
783 
784     /**
785      * Draws the specified view.
786      *
787      * @param view The view to draw.
788      * @param attachInfo AttachInfo tied to the specified view.
789      */
draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks)790     void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
791         attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart();
792 
793         updateRootDisplayList(view, callbacks);
794 
795         // register animating rendernodes which started animating prior to renderer
796         // creation, which is typical for animators started prior to first draw
797         if (attachInfo.mPendingAnimatingRenderNodes != null) {
798             final int count = attachInfo.mPendingAnimatingRenderNodes.size();
799             for (int i = 0; i < count; i++) {
800                 registerAnimatingRenderNode(
801                         attachInfo.mPendingAnimatingRenderNodes.get(i));
802             }
803             attachInfo.mPendingAnimatingRenderNodes.clear();
804             // We don't need this anymore as subsequent calls to
805             // ViewRootImpl#attachRenderNodeAnimator will go directly to us.
806             attachInfo.mPendingAnimatingRenderNodes = null;
807         }
808 
809         final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo();
810 
811         int syncResult = syncAndDrawFrame(frameInfo);
812         if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
813             Log.w("OpenGLRenderer", "Surface lost, forcing relayout");
814             // We lost our surface. For a relayout next frame which should give us a new
815             // surface from WindowManager, which hopefully will work.
816             attachInfo.mViewRootImpl.mForceNextWindowRelayout = true;
817             attachInfo.mViewRootImpl.requestLayout();
818         }
819         if ((syncResult & SYNC_REDRAW_REQUESTED) != 0) {
820             attachInfo.mViewRootImpl.invalidate();
821         }
822     }
823 
824     /** The root of everything */
getRootNode()825     public @NonNull RenderNode getRootNode() {
826         return mRootNode;
827     }
828 
829     /**
830      * Basic synchronous renderer. Currently only used to render the Magnifier, so use with care.
831      * TODO: deduplicate against ThreadedRenderer.
832      *
833      * @hide
834      */
835     public static class SimpleRenderer extends HardwareRenderer {
836         private final float mLightY, mLightZ, mLightRadius;
837 
SimpleRenderer(final Context context, final String name, final Surface surface)838         public SimpleRenderer(final Context context, final String name, final Surface surface) {
839             super();
840             setName(name);
841             setOpaque(false);
842             setSurface(surface);
843             final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
844             mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
845             mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
846             mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
847             final float ambientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0);
848             final float spotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0);
849             a.recycle();
850             setLightSourceAlpha(ambientShadowAlpha, spotShadowAlpha);
851         }
852 
853         /**
854          * Set the light center.
855          */
setLightCenter(final Display display, final int windowLeft, final int windowTop)856         public void setLightCenter(final Display display,
857                 final int windowLeft, final int windowTop) {
858             // Adjust light position for window offsets.
859             DisplayMetrics displayMetrics = new DisplayMetrics();
860             display.getRealMetrics(displayMetrics);
861             final float lightX = displayMetrics.widthPixels / 2f - windowLeft;
862             final float lightY = mLightY - windowTop;
863             // To prevent shadow distortion on larger screens, scale the z position of the light
864             // source relative to the smallest screen dimension.
865             final float zRatio = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels)
866                     / (450f * displayMetrics.density);
867             final float zWeightedAdjustment = (zRatio + 2) / 3f;
868             final float lightZ = mLightZ * zWeightedAdjustment;
869 
870             setLightSourceGeometry(lightX, lightY, lightZ, mLightRadius);
871         }
872 
getRootNode()873         public RenderNode getRootNode() {
874             return mRootNode;
875         }
876 
877         /**
878          * Draw the surface.
879          */
draw(final FrameDrawingCallback callback)880         public void draw(final FrameDrawingCallback callback) {
881             final long vsync = AnimationUtils.currentAnimationTimeMillis() * 1000000L;
882             if (callback != null) {
883                 setFrameCallback(callback);
884             }
885             createRenderRequest()
886                     .setVsyncTime(vsync)
887                     .syncAndDraw();
888         }
889     }
890 }
891