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