runtime_controller.h 25.7 KB
Newer Older
M
Michael Goderbauer 已提交
1
// Copyright 2013 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#ifndef FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_
#define FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_
7 8

#include <memory>
9
#include <vector>
10

11
#include "flutter/common/task_runners.h"
12
#include "flutter/flow/layers/layer_tree.h"
13
#include "flutter/fml/macros.h"
14
#include "flutter/lib/ui/io_manager.h"
15
#include "flutter/lib/ui/text/font_collection.h"
16
#include "flutter/lib/ui/ui_dart_state.h"
17
#include "flutter/lib/ui/window/platform_configuration.h"
18
#include "flutter/lib/ui/window/pointer_data_packet.h"
19
#include "flutter/runtime/dart_vm.h"
20
#include "flutter/runtime/platform_data.h"
21 22
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
23

24
namespace flutter {
25

26
class Scene;
27
class RuntimeDelegate;
28
class View;
29
class Window;
30

31 32 33 34 35
//------------------------------------------------------------------------------
/// Represents an instance of a running root isolate with window bindings. In
/// normal operation, a single instance of this object is owned by the engine
/// per shell. This object may only be created, used, and collected on the UI
/// task runner. Window state queried by the root isolate is stored by this
36 37 38 39
/// object. In cold-restart scenarios, the engine may collect this before
/// installing a new runtime controller in its place. The Clone method may be
/// used by the engine to copy the currently accumulated window state so it can
/// be referenced by the new runtime controller.
40
///
41
class RuntimeController : public PlatformConfigurationClient {
42
 public:
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
  //----------------------------------------------------------------------------
  /// @brief      Creates a new instance of a runtime controller. This is
  ///             usually only done by the engine instance associated with the
  ///             shell.
  ///
  /// @param      client                      The runtime delegate. This is
  ///                                         usually the `Engine` instance.
  /// @param      vm                          A reference to a running Dart VM.
  ///                                         The runtime controller must be
  ///                                         collected before the VM is
  ///                                         destroyed (this order is
  ///                                         guaranteed by the shell).
  /// @param[in]  isolate_snapshot            The isolate snapshot used to start
  ///                                         the root isolate managed by this
  ///                                         runtime controller. The isolate
  ///                                         must be transitioned into the
  ///                                         running phase manually by the
  ///                                         caller.
  /// @param[in]  task_runners                The task runners used by the shell
  ///                                         hosting this runtime controller.
  ///                                         This may be used by the isolate to
  ///                                         scheduled asynchronous texture
  ///                                         uploads or post tasks to the
  ///                                         platform task runner.
  /// @param[in]  snapshot_delegate           The snapshot delegate used by the
  ///                                         isolate to gather raster snapshots
  ///                                         of Flutter view hierarchies.
70 71 72 73 74
  /// @param[in]  hint_freed_delegate         The delegate used by the isolate
  ///                                         to hint the Dart VM when
  ///                                         additional memory may be freed
  ///                                         if a GC ran at the next
  ///                                         NotifyIdle.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
  /// @param[in]  io_manager                  The IO manager used by the isolate
  ///                                         for asynchronous texture uploads.
  /// @param[in]  unref_queue                 The unref queue used by the
  ///                                         isolate to collect resources that
  ///                                         may reference resources on the
  ///                                         GPU.
  /// @param[in]  image_decoder               The image decoder
  /// @param[in]  advisory_script_uri         The advisory script URI (only used
  ///                                         for debugging). This does not
  ///                                         affect the code being run in the
  ///                                         isolate in any way.
  /// @param[in]  advisory_script_entrypoint  The advisory script entrypoint
  ///                                         (only used for debugging). This
  ///                                         does not affect the code being run
  ///                                         in the isolate in any way. The
  ///                                         isolate must be transitioned to
  ///                                         the running state explicitly by
  ///                                         the caller.
  /// @param[in]  idle_notification_callback  The idle notification callback.
  ///                                         This allows callers to run native
  ///                                         code in isolate scope when the VM
  ///                                         is about to be notified that the
  ///                                         engine is going to be idle.
98
  /// @param[in]  platform_data                 The window data (if exists).
99 100 101 102 103 104 105 106 107 108 109 110 111 112
  /// @param[in]  isolate_create_callback     The isolate create callback. This
  ///                                         allows callers to run native code
  ///                                         in isolate scope on the UI task
  ///                                         runner as soon as the root isolate
  ///                                         has been created.
  /// @param[in]  isolate_shutdown_callback   The isolate shutdown callback.
  ///                                         This allows callers to run native
  ///                                         code in isolate scoped on the UI
  ///                                         task runner just as the root
  ///                                         isolate is about to be torn down.
  /// @param[in]  persistent_isolate_data     Unstructured persistent read-only
  ///                                         data that the root isolate can
  ///                                         access in a synchronous manner.
  ///
113 114 115 116 117
  RuntimeController(
      RuntimeDelegate& client,
      DartVM* vm,
      fml::RefPtr<const DartSnapshot> isolate_snapshot,
      TaskRunners task_runners,
118
      fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
119
      fml::WeakPtr<HintFreedDelegate> hint_freed_delegate,
120
      fml::WeakPtr<IOManager> io_manager,
121 122
      fml::RefPtr<SkiaUnrefQueue> unref_queue,
      fml::WeakPtr<ImageDecoder> image_decoder,
123 124
      std::string advisory_script_uri,
      std::string advisory_script_entrypoint,
125
      const std::function<void(int64_t)>& idle_notification_callback,
126
      const PlatformData& platform_data,
127 128
      const fml::closure& isolate_create_callback,
      const fml::closure& isolate_shutdown_callback,
129
      std::shared_ptr<const fml::Mapping> persistent_isolate_data);
130

131
  // |PlatformConfigurationClient|
132
  ~RuntimeController() override;
133

134 135 136 137 138 139 140 141
  //----------------------------------------------------------------------------
  /// @brief      Clone the the runtime controller. This re-creates the root
  ///             isolate with the same snapshots and copies all window data to
  ///             the new instance. This is usually only used in the debug
  ///             runtime mode to support the cold-restart scenario.
  ///
  /// @return     A clone of the existing runtime controller.
  ///
142 143
  std::unique_ptr<RuntimeController> Clone() const;

144
  //----------------------------------------------------------------------------
145
  /// @brief      Forward the specified viewport metrics to the running isolate.
146 147 148
  ///             If the isolate is not running, these metrics will be saved and
  ///             flushed to the isolate when it starts.
  ///
149
  /// @param[in]  metrics  The window's viewport metrics.
150 151 152
  ///
  /// @return     If the window metrics were forwarded to the running isolate.
  ///
153
  bool SetViewportMetrics(const ViewportMetrics& metrics);
154

155 156 157 158 159 160 161 162
  //----------------------------------------------------------------------------
  /// @brief      Forward the specified locale data to the running isolate. If
  ///             the isolate is not running, this data will be saved and
  ///             flushed to the isolate when it starts running.
  ///
  /// @deprecated The persistent isolate data must be used for this purpose
  ///             instead.
  ///
163 164
  /// @param[in]  locale_data  The locale data. This should consist of groups of
  ///             4 strings, each group representing a single locale.
165 166 167
  ///
  /// @return     If the locale data was forwarded to the running isolate.
  ///
168
  bool SetLocales(const std::vector<std::string>& locale_data);
169

170
  //----------------------------------------------------------------------------
171 172 173 174 175 176 177 178 179 180 181 182
  /// @brief      Forward the user settings data to the running isolate. If the
  ///             isolate is not running, this data will be saved and flushed to
  ///             the isolate when it starts running.
  ///
  /// @deprecated The persistent isolate data must be used for this purpose
  ///             instead.
  ///
  /// @param[in]  data  The user settings data.
  ///
  /// @return     If the user settings data was forwarded to the running
  ///             isolate.
  ///
183 184
  bool SetUserSettingsData(const std::string& data);

185 186 187 188 189 190 191 192 193 194 195 196 197
  //----------------------------------------------------------------------------
  /// @brief      Forward the lifecycle state data to the running isolate. If
  ///             the isolate is not running, this data will be saved and
  ///             flushed to the isolate when it starts running.
  ///
  /// @deprecated The persistent isolate data must be used for this purpose
  ///             instead.
  ///
  /// @param[in]  data  The lifecycle state data.
  ///
  /// @return     If the lifecycle state data was forwarded to the running
  ///             isolate.
  ///
198 199
  bool SetLifecycleState(const std::string& data);

200 201 202 203 204 205 206 207 208 209 210
  //----------------------------------------------------------------------------
  /// @brief      Notifies the running isolate about whether the semantics tree
  ///             should be generated or not. If the isolate is not running,
  ///             this preference will be saved and flushed to the isolate when
  ///             it starts running.
  ///
  /// @param[in]  enabled  Indicates whether to generate the semantics tree.
  ///
  /// @return     If the semantics tree generation preference was forwarded to
  ///             the running isolate.
  ///
211 212
  bool SetSemanticsEnabled(bool enabled);

213 214 215 216 217 218 219 220 221 222 223 224
  //----------------------------------------------------------------------------
  /// @brief      Forward the preference of accessibility features that must be
  ///             enabled in the semantics tree to the running isolate. If the
  ///             isolate is not running, this data will be saved and flushed to
  ///             the isolate when it starts running.
  ///
  /// @param[in]  flags  The accessibility features that must be generated in
  ///             the semantics tree.
  ///
  /// @return     If the preference of accessibility features was forwarded to
  ///             the running isolate.
  ///
225
  bool SetAccessibilityFeatures(int32_t flags);
226

227 228 229 230 231 232 233 234
  //----------------------------------------------------------------------------
  /// @brief      Notifies the running isolate that it should start generating a
  ///             new frame.
  ///
  /// @see        `Engine::BeginFrame` for more context.
  ///
  /// @param[in]  frame_time  The point at which the current frame interval
  ///                         began. May be used by animation interpolators,
235
  ///                         physics simulations, etc.
236 237 238 239
  ///
  /// @return     If notification to begin frame rendering was delivered to the
  ///             running isolate.
  ///
240
  bool BeginFrame(fml::TimePoint frame_time);
241

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
  //----------------------------------------------------------------------------
  /// @brief      Dart code cannot fully measure the time it takes for a
  ///             specific frame to be rendered. This is because Dart code only
  ///             runs on the UI task runner. That is only a small part of the
  ///             overall frame workload. The GPU task runner frame workload is
  ///             executed on a thread where Dart code cannot run (and hence
  ///             instrument). Besides, due to the pipelined nature of rendering
  ///             in Flutter, there may be multiple frame workloads being
  ///             processed at any given time. However, for non-Timeline based
  ///             profiling, it is useful for trace collection and processing to
  ///             happen in Dart. To do this, the GPU task runner frame
  ///             workloads need to be instrumented separately. After a set
  ///             number of these profiles have been gathered, they need to be
  ///             reported back to Dart code. The engine reports this extra
  ///             instrumentation information back to Dart code running on the
  ///             engine by invoking this method at predefined intervals.
  ///
  /// @see        `Engine::ReportTimings`, `FrameTiming`
  ///
  /// @param[in]  timings  Collection of `FrameTiming::kCount` * `n` timestamps
  ///                      for `n` frames whose timings have not been reported
  ///                      yet. A collection of integers is reported here for
  ///                      easier conversions to Dart objects. The timestamps
  ///                      are measured against the system monotonic clock
  ///                      measured in microseconds.
  ///
268 269
  bool ReportTimings(std::vector<int64_t> timings);

270 271 272 273 274 275 276
  //----------------------------------------------------------------------------
  /// @brief      Notify the Dart VM that no frame workloads are expected on the
  ///             UI task runner till the specified deadline. The VM uses this
  ///             opportunity to perform garbage collection operations is a
  ///             manner that interferes as little as possible with frame
  ///             rendering.
  ///
277 278 279
  /// NotifyIdle is advisory. The VM may or may not run a garbage collection
  /// when this is called, and will eventually perform garbage collections even
  /// if it is not called or it is called with insufficient deadlines.
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
  ///
  /// The garbage collection mechanism and its thresholds are internal
  /// implementation details and absolutely no guarantees are made about the
  /// threshold discussed below. This discussion is also an oversimplification
  /// but hopefully serves to calibrate expectations about GC behavior:
  /// * When the Dart VM and its root isolate are initialized, the memory
  ///   consumed upto that point are treated as a baseline.
  /// * A fixed percentage of the memory consumed (~20%) over the baseline is
  ///   treated as the hard threshold.
  /// * The memory in play is divided into old space and new space. The new
  ///   space is typically very small and fills up rapidly.
  /// * The baseline plus the threshold is considered the old space while the
  ///   small new space is a separate region (typically a few pages).
  /// * The total old space size minus the max new space size is treated as the
  ///   soft threshold.
  /// * In a world where there is no call to NotifyIdle, when the total
  ///   allocation exceeds the soft threshold, a concurrent mark is initiated in
  ///   the VM. There is a “small” pause that occurs when the concurrent mark is
  ///   initiated and another pause when the mark concludes and a sweep is
  ///   initiated.
  /// * If the total allocations exceeds the the hard threshold, a “big”
  ///   stop-the-world pause is initiated.
  /// * If after either the sweep after the concurrent mark, or, the
  ///   stop-the-world pause, the consumption returns to be below the soft
  ///   threshold, the dance begins anew.
  /// * If after both the “small” and “big” pauses, memory usage is still over
  ///   the hard threshold, i.e, the objects are still reachable, that amount of
  ///   memory is treated as the new baseline and a fixed percentage of the new
  ///   baseline over the new baseline is now the new hard threshold.
  /// * Updating the baseline will continue till memory for the updated old
  ///   space can be allocated from the operating system. These allocations will
  ///   typically fail due to address space exhaustion on 32-bit systems and
  ///   page table exhaustion on 64-bit systems.
  /// * NotifyIdle initiates the concurrent mark preemptively. The deadline is
  ///   used by the VM to determine if the corresponding sweep can be performed
  ///   within the deadline. This way, jank due to “small” pauses can be
  ///   ameliorated.
  /// * There is no ability to stop a “big” pause on reaching the hard threshold
  ///   in the old space. The best you can do is release (by making them
  ///   unreachable) objects eagerly so that the are marked as unreachable in
  ///   the concurrent mark initiated by either reaching the soft threshold or
  ///   an explicit NotifyIdle.
  /// * If you are running out of memory, its because too many large objects
  ///   were allocation and remained reachable such that the the old space kept
  ///   growing till it could grow no more.
  /// * At the edges of allocation thresholds, failures can occur gracefully if
  ///   the instigating allocation was made in the Dart VM or rather gracelessly
  ///   if the allocation is made by some native component.
  ///
  /// @see        `Dart_TimelineGetMicros`
  ///
  /// @bug        The `deadline` argument must be converted to `std::chrono`
  ///             instead of a raw integer.
  ///
  /// @param[in]  deadline  The deadline measures in microseconds against the
  ///             system's monotonic time. The clock can be accessed via
  ///             `Dart_TimelineGetMicros`.
337 338
  /// @param[in] freed_hint  A hint of the number of bytes potentially freed
  ///                        since the last call to NotifyIdle if a GC were run.
339 340 341
  ///
  /// @return     If the idle notification was forwarded to the running isolate.
  ///
342
  bool NotifyIdle(int64_t deadline, size_t freed_hint);
343

344 345 346 347 348 349 350
  //----------------------------------------------------------------------------
  /// @brief      Returns if the root isolate is running. The isolate must be
  ///             transitioned to the running phase manually. The isolate can
  ///             stop running if it terminates execution on its own.
  ///
  /// @return     True if root isolate running, False otherwise.
  ///
351
  virtual bool IsRootIsolateRunning() const;
352

353 354 355 356 357 358 359 360 361
  //----------------------------------------------------------------------------
  /// @brief      Dispatch the specified platform message to running root
  ///             isolate.
  ///
  /// @param[in]  message  The message to dispatch to the isolate.
  ///
  /// @return     If the message was dispatched to the running root isolate.
  ///             This may fail is an isolate is not running.
  ///
362
  virtual bool DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message);
363

364 365 366 367 368 369 370 371 372
  //----------------------------------------------------------------------------
  /// @brief      Dispatch the specified pointer data message to the running
  ///             root isolate.
  ///
  /// @param[in]  packet  The pointer data message to dispatch to the isolate.
  ///
  /// @return     If the pointer data message was dispatched. This may fail is
  ///             an isolate is not running.
  ///
373 374
  bool DispatchPointerDataPacket(const PointerDataPacket& packet);

375 376 377 378 379 380 381 382 383 384 385 386
  //----------------------------------------------------------------------------
  /// @brief      Dispatch the semantics action to the specified accessibility
  ///             node.
  ///
  /// @param[in]  id      The identified of the accessibility node.
  /// @param[in]  action  The semantics action to perform on the specified
  ///                     accessibility node.
  /// @param[in]  args    Optional data that applies to the specified action.
  ///
  /// @return     If the semantics action was dispatched. This may fail if an
  ///             isolate is not running.
  ///
387
  bool DispatchSemanticsAction(int32_t id,
388 389
                               SemanticsAction action,
                               std::vector<uint8_t> args);
390

391 392 393 394 395 396
  //----------------------------------------------------------------------------
  /// @brief      Gets the main port identifier of the root isolate.
  ///
  /// @return     The main port identifier. If no root isolate is running,
  ///             returns `ILLEGAL_PORT`.
  ///
397
  Dart_Port GetMainPort();
398

399 400 401 402 403 404 405 406 407 408 409 410 411
  //----------------------------------------------------------------------------
  /// @brief      Gets the debug name of the root isolate. But default, the
  ///             debug name of the isolate is derived from its advisory script
  ///             URI, advisory main entrypoint and its main port name. For
  ///             example, "main.dart$main-1234" where the script URI is
  ///             "main.dart", the entrypoint is "main" and the port name
  ///             "1234". Once launched, the isolate may re-christen itself
  ///             using a name it selects via `setIsolateDebugName` in
  ///             `window.dart`. This name is purely advisory and only used by
  ///             instrumentation and reporting purposes.
  ///
  /// @return     The debug name of the root isolate.
  ///
412
  std::string GetIsolateName();
413

414 415 416 417
  //----------------------------------------------------------------------------
  /// @brief      Returns if the root isolate has any live receive ports.
  ///
  /// @return     True if there are live receive ports, False otherwise. Return
418
  ///             False if the root isolate is not running as well.
419
  ///
420
  bool HasLivePorts();
421

422 423 424 425 426
  //----------------------------------------------------------------------------
  /// @brief      Get the last error encountered by the microtask queue.
  ///
  /// @return     The last error encountered by the microtask queue.
  ///
427
  tonic::DartErrorHandleType GetLastError();
428

429 430 431 432
  //----------------------------------------------------------------------------
  /// @brief      Get a weak pointer to the root Dart isolate. This isolate may
  ///             only be locked on the UI task runner. Callers use this
  ///             accessor to transition to the root isolate to the running
433
  ///             phase.
434 435 436
  ///
  /// @return     The root isolate reference.
  ///
437
  std::weak_ptr<DartIsolate> GetRootIsolate();
438

439 440 441 442 443 444 445 446 447 448
  //----------------------------------------------------------------------------
  /// @brief      Get the return code specified by the root isolate (if one is
  ///             present).
  ///
  /// @bug        Change this method to return `std::optional<uint32_t>`
  ///             instead.
  ///
  /// @return     The root isolate return code. The first argument in the pair
  ///             indicates if one is specified by the root isolate.
  ///
449
  std::pair<bool, uint32_t> GetRootIsolateReturnCode();
450

451 452 453 454
 protected:
  /// Constructor for Mocks.
  RuntimeController(RuntimeDelegate& client, TaskRunners p_task_runners);

455
 private:
456 457 458 459
  struct Locale {
    Locale(std::string language_code_,
           std::string country_code_,
           std::string script_code_,
460 461 462
           std::string variant_code_);

    ~Locale();
463 464 465 466 467 468 469

    std::string language_code;
    std::string country_code;
    std::string script_code;
    std::string variant_code;
  };

470
  RuntimeDelegate& client_;
B
Ben Konyi 已提交
471
  DartVM* const vm_;
472
  fml::RefPtr<const DartSnapshot> isolate_snapshot_;
473
  TaskRunners task_runners_;
474
  fml::WeakPtr<SnapshotDelegate> snapshot_delegate_;
475
  fml::WeakPtr<HintFreedDelegate> hint_freed_delegate_;
476
  fml::WeakPtr<IOManager> io_manager_;
477
  fml::RefPtr<SkiaUnrefQueue> unref_queue_;
478
  fml::WeakPtr<ImageDecoder> image_decoder_;
479 480
  std::string advisory_script_uri_;
  std::string advisory_script_entrypoint_;
481
  std::function<void(int64_t)> idle_notification_callback_;
482
  PlatformData platform_data_;
483
  std::weak_ptr<DartIsolate> root_isolate_;
484
  std::pair<bool, uint32_t> root_isolate_return_code_ = {false, 0};
485 486
  const fml::closure isolate_create_callback_;
  const fml::closure isolate_shutdown_callback_;
487 488
  std::shared_ptr<const fml::Mapping> persistent_isolate_data_;

489
  PlatformConfiguration* GetPlatformConfigurationIfAvailable();
490 491 492

  bool FlushRuntimeStateToIsolate();

493
  // |PlatformConfigurationClient|
494
  std::string DefaultRouteName() override;
495

496
  // |PlatformConfigurationClient|
497
  void ScheduleFrame() override;
498

499
  // |PlatformConfigurationClient|
500
  void Render(Scene* scene) override;
501

502
  // |PlatformConfigurationClient|
503
  void UpdateSemantics(SemanticsUpdate* update) override;
504

505
  // |PlatformConfigurationClient|
506
  void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) override;
507

508
  // |PlatformConfigurationClient|
509
  FontCollection& GetFontCollection() override;
510

511
  // |PlatformConfigurationClient|
512 513
  void UpdateIsolateDescription(const std::string isolate_name,
                                int64_t isolate_port) override;
514

515
  // |PlatformConfigurationClient|
516 517
  void SetNeedsReportTimings(bool value) override;

518
  // |PlatformConfigurationClient|
519 520
  std::shared_ptr<const fml::Mapping> GetPersistentIsolateData() override;

521
  // |PlatformConfigurationClient|
522 523 524
  std::unique_ptr<std::vector<std::string>> ComputePlatformResolvedLocale(
      const std::vector<std::string>& supported_locale_data) override;

525
  FML_DISALLOW_COPY_AND_ASSIGN(RuntimeController);
526 527
};

528
}  // namespace flutter
529

530
#endif  // FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_