• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef SHELL_COMMON_ACE_ENGINE_H_
6 #define SHELL_COMMON_ACE_ENGINE_H_
7 
8 #include <memory>
9 #include <string>
10 
11 #include "flutter/assets/asset_manager.h"
12 #include "flutter/common/task_runners.h"
13 #include "flutter/fml/macros.h"
14 #include "flutter/fml/memory/weak_ptr.h"
15 #include "flutter/lib/ui/semantics/semantics_node.h"
16 #include "flutter/lib/ui/text/font_collection.h"
17 #include "flutter/lib/ui/window/platform_message.h"
18 #include "flutter/lib/ui/window/viewport_metrics.h"
19 #include "flutter/runtime/runtime_controller.h"
20 #include "flutter/runtime/runtime_delegate.h"
21 #include "flutter/shell/common/animator.h"
22 #include "flutter/shell/common/rasterizer.h"
23 #include "flutter/shell/common/run_configuration.h"
24 #include "flutter/shell/common/shell_io_manager.h"
25 #include "third_party/skia/include/core/SkPicture.h"
26 
27 namespace flutter {
28 
29 //------------------------------------------------------------------------------
30 /// The engine is a component owned by the shell that resides on the UI task
31 /// runner and is responsible for managing the needs of the root isolate and its
32 /// runtime. The engine can only be created, accessed and collected on the UI
33 /// task runner. Each shell owns exactly one instance of the engine.
34 ///
35 /// The root isolate of Flutter application gets "window" bindings. Using these
36 /// bindings, the application can schedule frames, post layer-trees for
37 /// rendering, ask to decompress images and upload them to the GPU, etc..
38 /// Non-root isolates of the VM do not get any of these capabilities and are run
39 /// in a VM managed thread pool (so if they did have "window", the threading
40 /// guarantees needed for engine operation would be violated).
41 ///
42 /// The engine is responsible for the entire life-cycle of the root isolate.
43 /// When the engine is collected, its owner assumes that the root isolate has
44 /// been shutdown and appropriate resources collected. While each engine
45 /// instance can only manage a single instance of a root isolate, it may restart
46 /// that isolate on request. This is how the cold-restart development scenario
47 /// is supported.
48 ///
49 /// When the engine instance is initially created, the root isolate is created
50 /// but it is not in the |DartIsolate::Phase::Running| phase yet. It only moves
51 /// into that phase when a successful call to `Engine::Run` is made.
52 ///
53 /// @see      `Shell`
54 ///
55 /// @note     This name of this class is perhaps a bit unfortunate and has
56 ///           sometimes been the cause of confusion. For a class named "Engine"
57 ///           in the Flutter "Engine" repository, its responsibilities are
58 ///           decidedly unremarkable. But, it does happen to be the primary
59 ///           entry-point used by components higher up in the Flutter tech stack
60 ///           (usually in Dart code) to peer into the lower level functionality.
61 ///           Besides, the authors haven't been able to come up with a more apt
62 ///           name and it does happen to be one of the older classes in the
63 ///           repository.
64 ///
65 class Engine final : public RuntimeDelegate {
66  public:
67   // ACE PC preivew
68   using IdleCallback = std::function<void(int64_t)>;
69 
70   //----------------------------------------------------------------------------
71   /// @brief      Indicates the result of the call to `Engine::Run`.
72   ///
73   enum class RunStatus {
74     //--------------------------------------------------------------------------
75     /// The call to |Engine::Run| was successful and the root isolate is in the
76     /// `DartIsolate::Phase::Running` phase with its entry-point invocation
77     /// already pending in the task queue.
78     ///
79     Success,
80 
81     //--------------------------------------------------------------------------
82     /// The engine can only manage a single instance of a root isolate. If a
83     /// previous call to run the root isolate was successful, subsequent calls
84     /// to run the isolate (even if the new run configuration is different) will
85     /// be rejected.
86     ///
87     /// It is up to the caller to decide to re-purpose the running isolate,
88     /// terminate it, or use another shell to host the new isolate. This is
89     /// mostly used by embedders which have a fire-and-forget strategy to root
90     /// isolate launch. For example, the application may try to "launch" and
91     /// isolate when the embedders launches or resumes from a paused state. That
92     /// the isolate is running is not necessarily a failure condition for them.
93     /// But from the engine's perspective, the run configuration was rejected.
94     ///
95     FailureAlreadyRunning,
96 
97     //--------------------------------------------------------------------------
98     /// Used to indicate to the embedder that a root isolate was not already
99     /// running but the run configuration was not valid and root isolate could
100     /// not be moved into the `DartIsolate::Phase::Running` phase.
101     ///
102     /// The caller must attempt the run call again with a valid configuration.
103     /// The set of all failure modes is massive and can originate from a variety
104     /// of sub-components. The engine will attempt to log the same when
105     /// possible. With the aid of logs, the common causes of failure are:
106     ///
107     /// * AOT assets give to JIT/DBC mode VM's and vice-versa.
108     /// * The assets could not be found in the asset manager. Callers must make
109     ///   sure their run configuration asset managers have been correctly setup.
110     /// * The assets themselves were corrupt or invalid. Callers must make sure
111     ///   their asset delivery mechanisms are sound.
112     /// * The application entry-point or the root library of the entry-point
113     ///   specified in the run configuration was invalid. Callers must make sure
114     ///   that the entry-point is present in the application. If the name of the
115     ///   entrypoint is not "main" in the root library, callers must also ensure
116     ///   that the snapshotting process has not tree-shaken away this
117     ///   entrypoint. This requires the decoration of the entrypoint with the
118     ///   `@pragma('vm:entry-point')` directive. This problem will manifest in
119     ///   AOT mode operation of the Dart VM.
120     ///
121     Failure,
122   };
123 
124   //----------------------------------------------------------------------------
125   /// @brief      While the engine operates entirely on the UI task runner, it
126   ///             needs the capabilities of the other components to fulfill the
127   ///             requirements of the root isolate. The shell is the only class
128   ///             that implements this interface as no other component has
129   ///             access to all components in a thread safe manner. The engine
130   ///             delegates these tasks to the shell via this interface.
131   ///
132   class Delegate {
133    public:
134     //--------------------------------------------------------------------------
135     /// @brief      When the Flutter application has a message to send to the
136     ///             underlying platform, the message needs to be forwarded to
137     ///             the platform on the the appropriate thread (via the platform
138     ///             task runner). The engine delegates this task to the shell
139     ///             via this method.
140     ///
141     /// @see        `PlatformView::HandlePlatformMessage`
142     ///
143     /// @param[in]  message  The message from the Flutter application to send to
144     ///                      the underlying platform.
145     ///
146     virtual void OnEngineHandlePlatformMessage(
147         fml::RefPtr<PlatformMessage> message) = 0;
148     //--------------------------------------------------------------------------
149     /// @brief      Notifies the delegate that the root isolate of the
150     ///             application is about to be discarded and a new isolate with
151     ///             the same runtime started in its place. This should only
152     ///             happen in the Flutter "debug" runtime mode in the
153     ///             cold-restart scenario. The embedder may need to reset native
154     ///             resource in response to the restart.
155     ///
156     /// @see        `PlatformView::OnPreEngineRestart`
157     ///
158     virtual void OnPreEngineRestart() = 0;
159     //--------------------------------------------------------------------------
160     /// @brief      Notifies the shell that the application has an opinion about
161     ///             whether its frame timings need to be reported backed to it.
162     ///             Due to the asynchronous nature of rendering in Flutter, it
163     ///             is not possible for the application to determine the total
164     ///             time it took to render a specific frame. While the
165     ///             layer-tree is constructed on the UI thread, it needs to be
166     ///             rendering on the GPU thread. Dart code cannot execute on
167     ///             this thread. So any instrumentation about the frame times
168     ///             gathered on this thread needs to be aggregated and sent back
169     ///             to the UI thread for processing in Dart.
170     ///
171     ///             When the application indicates that frame times need to be
172     ///             reported, it collects this information till a specified
173     ///             number of data points are gathered. Then this information is
174     ///             sent back to Dart code via `Engine::ReportTimings`.
175     ///
176     ///             This option is engine counterpart of the
177     ///             `Window._setNeedsReportTimings` in `window.dart`.
178     ///
179     /// @param[in]  needs_reporting  If reporting information should be
180     ///                              collected and send back to Dart.
181     ///
182     virtual void SetNeedsReportTimings(bool needs_reporting) = 0;
183   };
184 
185   //----------------------------------------------------------------------------
186   /// @brief      Creates an instance of the engine. This is done by the Shell
187   ///             on the the UI task runner.
188   ///
189   /// @param      delegate           The object used by the engine to perform
190   ///                                tasks that require access to components
191   ///                                that cannot be safely accessed by the
192   ///                                engine. This is the shell.
193   /// @param      vm                 An instance of the running Dart VM.
194   /// @param[in]  isolate_snapshot   The snapshot used to create the root
195   ///                                isolate. Even though the isolate is not
196   ///                                `DartIsolate::Phase::Running` phase, it is
197   ///                                created when the engine is created. This
198   ///                                requires access to the isolate snapshot
199   ///                                upfront.
200   /// @param[in]  shared_snapshot    The portion of the isolate snapshot shared
201   ///                                among multiple isolates.
202   //  TODO(chinmaygarde): This is probably redundant now that the IO manager is
203   //  it's own object.
204   /// @param[in]  task_runners       The task runners used by the shell that
205   ///                                hosts this engine.
206   /// @param[in]  settings           The settings used to initialize the shell
207   ///                                and the engine.
208   /// @param[in]  animator           The animator used to schedule frames.
209   //  TODO(chinmaygarde): Move this to `Engine::Delegate`
210   /// @param[in]  snapshot_delegate  The delegate used to fulfill requests to
211   ///                                snapshot a specified scene. The engine
212   ///                                cannot snapshot a scene on the UI thread
213   ///                                directly because the scene (described via
214   ///                                an `SkPicture`) may reference resources on
215   ///                                the GPU and there is no GPU context current
216   ///                                on the UI thread. The delegate is a
217   ///                                component that has access to all the
218   ///                                requisite GPU resources.
219   /// @param[in]  io_manager         The IO manager used by this root isolate to
220   ///                                schedule tasks that manage resources on the
221   ///                                GPU.
222   ///
223   Engine(Delegate& delegate,
224          TaskRunners task_runners,
225          Settings settings,
226          std::unique_ptr<Animator> animator,
227          fml::WeakPtr<IOManager> io_manager);
228 
229   //----------------------------------------------------------------------------
230   /// @brief      Destroys the engine engine. Called by the shell on the UI task
231   ///             runner. The running root isolate is terminated and will no
232   ///             longer access the task runner after this call returns. This
233   ///             allows the embedder to tear down the thread immediately if
234   ///             needed.
235   ///
236   ~Engine() override;
237 
238   //----------------------------------------------------------------------------
239   /// @brief      Gets the refresh rate in frames per second of the vsync waiter
240   ///             used by the animator managed by this engine. This information
241   ///             is purely advisory and is not used by any component. It is
242   ///             only used by the tooling to visualize frame performance.
243   ///
244   /// @attention  The display refresh rate is useless for frame scheduling
245   ///             because it can vary and more accurate frame specific
246   ///             information is given to the engine by the vsync waiter
247   ///             already. However, this call is used by the tooling to ask very
248   ///             high level questions about display refresh rate. For example,
249   ///             "Is the display 60 or 120Hz?". This information is quite
250   ///             unreliable (not available immediately on launch on some
251   ///             platforms), variable and advisory. It must not be used by any
252   ///             component that claims to use it to perform accurate frame
253   ///             scheduling.
254   ///
255   /// @return     The display refresh rate in frames per second. This may change
256   ///             from frame to frame, throughout the lifecycle of the
257   ///             application, and, may not be available immediately upon
258   ///             application launch.
259   ///
260   float GetDisplayRefreshRate() const;
261 
262   //----------------------------------------------------------------------------
263   /// @return     The pointer to this instance of the engine. The engine may
264   ///             only be accessed safely on the UI task runner.
265   ///
266   fml::WeakPtr<Engine> GetWeakPtr() const;
267 
268   //----------------------------------------------------------------------------
269   /// @brief      Moves the root isolate to the `DartIsolate::Phase::Running`
270   ///             phase on a successful call to this method.
271   ///
272   ///             The isolate itself is created when the engine is created, but
273   ///             it is not yet in the running phase. This is done to amortize
274   ///             initial time taken to launch the root isolate. The isolate
275   ///             snapshots used to run the isolate can be fetched on another
276   ///             thread while the engine itself is launched on the UI task
277   ///             runner.
278   ///
279   ///             Repeated calls to this method after a successful run will be
280   ///             rejected even if the run configuration is valid (with the
281   ///             appropriate error returned).
282   ///
283   /// @param[in]  configuration  The configuration used to run the root isolate.
284   ///                            The configuration must be valid.
285   ///
286   /// @return     The result of the call to run the root isolate.
287   ///
288   FML_WARN_UNUSED_RESULT
289   RunStatus Run(RunConfiguration configuration);
290 
291   //----------------------------------------------------------------------------
292   /// @brief      Tears down an existing root isolate, reuses the components of
293   ///             that isolate and attempts to launch a new isolate using the
294   ///             given the run configuration. This is only used in the
295   ///             "debug" Flutter runtime mode in the cold-restart scenario.
296   ///
297   /// @attention  This operation must be performed with care as even a
298   ///             non-successful restart will still tear down any existing root
299   ///             isolate. In such cases, the engine and its shell must be
300   ///             discarded.
301   ///
302   /// @param[in]  configuration  The configuration used to launch the new
303   ///                            isolate.
304   ///
305   /// @return     Whether the restart was successful. If not, the engine and its
306   ///             shell must be discarded.
307   ///
308   FML_WARN_UNUSED_RESULT
309   bool Restart(RunConfiguration configuration);
310 
311   //----------------------------------------------------------------------------
312   /// @brief      Updates the asset manager referenced by the root isolate of a
313   ///             Flutter application. This happens implicitly in the call to
314   ///             `Engine::Run` and `Engine::Restart` as the asset manager is
315   ///             referenced from the run configuration provided to those calls.
316   ///             In addition to the the `Engine::Run` and `Engine::Restart`
317   ///             calls, the tooling may need to update the assets available to
318   ///             the application as the user adds them to their project. For
319   ///             example, these assets may be referenced by code that is newly
320   ///             patched in after a hot-reload. Neither the shell or the
321   ///             isolate in relaunched in such cases. The tooling usually
322   ///             patches in the new assets in a temporary location and updates
323   ///             the asset manager to point to that location.
324   ///
325   /// @param[in]  asset_manager  The new asset manager to use for the running
326   ///                            root isolate.
327   ///
328   /// @return     If the asset manager was successfully replaced. This may fail
329   ///             if the new asset manager is invalid.
330   ///
331   bool UpdateAssetManager(std::shared_ptr<AssetManager> asset_manager);
332 
333   //----------------------------------------------------------------------------
334   /// @brief      Notifies the engine that it is time to begin working on a new
335   ///             frame previously scheduled via a call to
336   ///             `Engine::ScheduleFrame`. This call originates in the animator.
337   ///
338   ///             The frame time given as the argument indicates the point at
339   ///             which the current frame interval began. It is very slightly
340   ///             (because of scheduling overhead) in the past. If a new layer
341   ///             tree is not produced and given to the GPU task runner within
342   ///             one frame interval from this point, the Flutter application
343   ///             will jank.
344   ///
345   ///             If an root isolate is running, this method calls the
346   ///             `::_beginFrame` method in `hooks.dart`. If a root isolate is
347   ///             not running, this call does nothing.
348   ///
349   ///             This method encapsulates the entire UI thread frame workload.
350   ///             The following (mis)behavior in the functioning of the method
351   ///             will cause the jank in the Flutter application:
352   ///             * The time taken by this method to create a layer-tree exceeds
353   ///               on frame interval (for example, 16.66 ms on a 60Hz display).
354   ///             * The time take by this method to generate a new layer-tree
355   ///               causes the current layer-tree pipeline depth to change. To
356   ///               illustrate this point, note that maximum pipeline depth used
357   ///               by layer tree in the engine is 2. If both the UI and GPU
358   ///               task runner tasks finish within one frame interval, the
359   ///               pipeline depth is one. If the UI thread happens to be
360   ///               working on a frame when the GPU thread is still not done
361   ///               with the previous frame, the pipeline depth is 2. When the
362   ///               pipeline depth changes from 1 to 2, animations and UI
363   ///               interactions that cause the generation of the new layer tree
364   ///               appropriate for (frame_time + one frame interval) will
365   ///               actually end up at (frame_time + two frame intervals). This
366   ///               is not what code running on the UI thread expected would
367   ///               happen. This causes perceptible jank.
368   ///
369   /// @param[in]  frame_time  The point at which the current frame interval
370   ///                         began. May be used by animation interpolators,
371   ///                         physics simulations, etc..
372   ///
373   void BeginFrame(fml::TimePoint frame_time);
374 
375   void ReportTimings(std::vector<int64_t> timings);
376   //----------------------------------------------------------------------------
377   /// @brief      Notifies the engine that the UI task runner is not expected to
378   ///             undertake a new frame workload till a specified timepoint. The
379   ///             timepoint is measured in microseconds against the system's
380   ///             monotonic clock. It is recommended that the clock be accessed
381   ///             via `Dart_TimelineGetMicros` from `dart_api.h` for
382   ///             consistency. In reality, the clocks used by Dart, FML and
383   ///             std::steady_clock are all the same and the timepoints can be
384   ///             converted from on clock type to another.
385   ///
386   ///             The Dart VM uses this notification to schedule book-keeping
387   ///             tasks that may include a garbage collection. In this way, it
388   ///             is less likely for the VM to perform such (potentially long
389   ///             running) tasks in the middle of a frame workload.
390   ///
391   ///             This notification is advisory. That is, not providing this
392   ///             notification does not mean garbage collection is postponed
393   ///             till this call is made. If this notification is not provided,
394   ///             garbage collection will happen based on the usual heuristics
395   ///             used by the Dart VM.
396   ///
397   ///             Currently, this idle notification is delivered to the engine
398   ///             at two points. Once, the deadline is calculated based on how
399   ///             much time in the current frame interval is left on the UI task
400   ///             runner. Since the next frame workload cannot begin till at
401   ///             least the next callback from the vsync waiter, this period may
402   ///             be used to used as a "small" idle notification. On the other
403   ///             hand, if no more frames are scheduled, a large (but arbitrary)
404   ///             idle notification deadline is chosen for a "big" idle
405   ///             notification. Again, this notification does not guarantee
406   ///             collection, just gives the Dart VM more hints about opportune
407   ///             moments to perform collections.
408   ///
409   //  TODO(chinmaygarde): This should just use fml::TimePoint instead of having
410   //  to remember that the unit is microseconds (which is no used anywhere else
411   //  in the engine).
412   ///
413   /// @param[in]  deadline  The deadline as a timepoint in microseconds measured
414   ///                       against the system monotonic clock. Use
415   ///                       `Dart_TimelineGetMicros()`, for consistency.
416   ///
417   void NotifyIdle(int64_t deadline);
418 
419   //----------------------------------------------------------------------------
420   /// @brief      Indicates to the Flutter application that it has obtained a
421   ///             rendering surface. This is a good opportunity for the engine
422   ///             to start servicing any outstanding frame requests from the
423   ///             Flutter applications. Flutter application that have no
424   ///             rendering concerns may never get a rendering surface. In such
425   ///             cases, while their root isolate can perform as normal, any
426   ///             frame requests made by them will never be serviced and layer
427   ///             trees produced outside of frame workloads will be dropped.
428   ///
429   ///             Very close to when this call is made, the application can
430   ///             expect the updated viewport metrics. Rendering only begins
431   ///             when the Flutter application gets an output surface and a
432   ///             valid set of viewport metrics.
433   ///
434   /// @see        `OnOutputSurfaceDestroyed`
435   ///
436   void OnOutputSurfaceCreated();
437 
438   //----------------------------------------------------------------------------
439   /// @brief      Indicates to the Flutter application that a previously
440   ///             acquired rendering surface has been lost. Further frame
441   ///             requests will no longer be serviced and any layer tree
442   ///             submitted for rendering will be dropped. If/when a new surface
443   ///             is acquired, a new layer tree must be generated.
444   ///
445   /// @see        `OnOutputSurfaceCreated`
446   ///
447   void OnOutputSurfaceDestroyed();
448 
449   //----------------------------------------------------------------------------
450   /// @brief      Ace PC preivew
451   ///
452   void SetIdleNotificationCallback(const IdleCallback& idleCallback);
453 
454   //----------------------------------------------------------------------------
455   /// @brief      Updates the viewport metrics for the currently running Flutter
456   ///             application. The viewport metrics detail the size of the
457   ///             rendering viewport in texels as well as edge insets if
458   ///             present.
459   ///
460   /// @see        `ViewportMetrics`
461   ///
462   /// @param[in]  metrics  The metrics
463   ///
464   void SetViewportMetrics(const ViewportMetrics& metrics);
465 
466   //----------------------------------------------------------------------------
467   /// @brief      Notifies the engine that the embedder has sent it a message.
468   ///             This call originates in the platform view and has been
469   ///             forwarded to the engine on the UI task runner here.
470   ///
471   /// @param[in]  message  The message sent from the embedder to the Dart
472   ///                      application.
473   ///
474   void DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message);
475 
476   //----------------------------------------------------------------------------
477   /// @brief      Notifies the engine that the embedder has sent it a pointer
478   ///             data packet. A pointer data packet may contain multiple
479   ///             input events. This call originates in the platform view and
480   ///             the shell has forwarded the same to the engine on the UI task
481   ///             runner here.
482   ///
483   /// @param[in]  packet         The pointer data packet containing multiple
484   ///                            input events.
485   /// @param[in]  trace_flow_id  The trace flow identifier associated with the
486   ///                            pointer data packet. The engine uses this trace
487   ///                            identifier to connect trace flows in the
488   ///                            timeline from the input event event to the
489   ///                            frames generated due to those input events.
490   ///                            These flows are tagged as "PointerEvent" in the
491   ///                            timeline and allow grouping frames and input
492   ///                            events into logical chunks.
493   ///
494   void DispatchPointerDataPacket(const PointerDataPacket& packet,
495                                  uint64_t trace_flow_id);
496   // |RuntimeDelegate|
497   void ScheduleFrame(bool regenerate_layer_tree = true) override;
498 
499   // |RuntimeDelegate|
500   FontCollection& GetFontCollection() override;
501 
502  private:
503   Engine::Delegate& delegate_;
504   const Settings settings_;
505   std::unique_ptr<Animator> animator_;
506   std::unique_ptr<RuntimeController> runtime_controller_;
507   std::string initial_route_;
508   ViewportMetrics viewport_metrics_;
509   std::shared_ptr<AssetManager> asset_manager_;
510   bool activity_running_;
511   bool have_surface_;
512   std::once_flag font_flag_;
513   std::unique_ptr<FontCollection> font_collection_;
514   fml::WeakPtrFactory<Engine> weak_factory_;
515 
516   // |RuntimeDelegate|
517   std::string DefaultRouteName() override;
518 
519   // |RuntimeDelegate|
520   void Render(std::unique_ptr<flutter::LayerTree> layer_tree) override;
521 
522   void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) override;
523 
524   void SetNeedsReportTimings(bool value) override;
525 
526   void StopAnimator();
527 
528   void StartAnimatorIfPossible();
529 
530   bool HandleLifecyclePlatformMessage(PlatformMessage* message);
531 
532   bool HandleNavigationPlatformMessage(fml::RefPtr<PlatformMessage> message);
533 
534   bool HandleLocalizationPlatformMessage(PlatformMessage* message);
535 
536   void HandleSettingsPlatformMessage(PlatformMessage* message);
537 
538   void HandleAssetPlatformMessage(fml::RefPtr<PlatformMessage> message);
539 
540   bool GetAssetAsBuffer(const std::string& name, std::vector<uint8_t>* data);
541 
542   RunStatus PrepareAndLaunchIsolate(RunConfiguration configuration);
543 
544   friend class testing::ShellTest;
545 
546   FML_DISALLOW_COPY_AND_ASSIGN(Engine);
547 };
548 
549 }  // namespace flutter
550 
551 #endif  // SHELL_COMMON_ENGINE_H_
552