runtime_controller.h 24.6 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/pointer_data_packet.h"
18
#include "flutter/lib/ui/window/window.h"
19
#include "flutter/runtime/dart_vm.h"
20
#include "flutter/runtime/window_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 36 37 38 39 40
//------------------------------------------------------------------------------
/// 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
/// object. In cold-restart scenarios, the engine may collect this collect
/// 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.
///
41
class RuntimeController final : public WindowClient {
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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  //----------------------------------------------------------------------------
  /// @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.
  /// @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.
  /// @param[in]  window_data                 The window data (if exists).
  /// @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.
  ///
108 109 110 111 112
  RuntimeController(
      RuntimeDelegate& client,
      DartVM* vm,
      fml::RefPtr<const DartSnapshot> isolate_snapshot,
      TaskRunners task_runners,
113
      fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
114
      fml::WeakPtr<IOManager> io_manager,
115 116
      fml::RefPtr<SkiaUnrefQueue> unref_queue,
      fml::WeakPtr<ImageDecoder> image_decoder,
117 118
      std::string advisory_script_uri,
      std::string advisory_script_entrypoint,
119
      const std::function<void(int64_t)>& idle_notification_callback,
120
      const WindowData& window_data,
121 122
      const fml::closure& isolate_create_callback,
      const fml::closure& isolate_shutdown_callback,
123
      std::shared_ptr<const fml::Mapping> persistent_isolate_data);
124

125
  // |WindowClient|
126
  ~RuntimeController() override;
127

128 129 130 131 132 133 134 135
  //----------------------------------------------------------------------------
  /// @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.
  ///
136 137
  std::unique_ptr<RuntimeController> Clone() const;

138 139 140 141 142 143 144 145 146
  //----------------------------------------------------------------------------
  /// @brief      Forward the specified window metrics to the running isolate.
  ///             If the isolate is not running, these metrics will be saved and
  ///             flushed to the isolate when it starts.
  ///
  /// @param[in]  metrics  The metrics.
  ///
  /// @return     If the window metrics were forwarded to the running isolate.
  ///
147
  bool SetViewportMetrics(const ViewportMetrics& metrics);
148

149 150 151 152 153 154 155 156 157 158 159 160
  //----------------------------------------------------------------------------
  /// @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.
  ///
  /// @param[in]  locale_data  The locale data
  ///
  /// @return     If the locale data was forwarded to the running isolate.
  ///
161
  bool SetLocales(const std::vector<std::string>& locale_data);
162

163 164 165 166 167 168 169 170 171 172 173 174 175
  //----------------------------------------------------------------------------
  /// @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.
  ///
176 177
  bool SetUserSettingsData(const std::string& data);

178 179 180 181 182 183 184 185 186 187 188 189 190
  //----------------------------------------------------------------------------
  /// @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.
  ///
191 192
  bool SetLifecycleState(const std::string& data);

193 194 195 196 197 198 199 200 201 202 203
  //----------------------------------------------------------------------------
  /// @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.
  ///
204 205
  bool SetSemanticsEnabled(bool enabled);

206 207 208 209 210 211 212 213 214 215 216 217
  //----------------------------------------------------------------------------
  /// @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.
  ///
218
  bool SetAccessibilityFeatures(int32_t flags);
219

220 221 222 223 224 225 226 227 228 229 230 231 232
  //----------------------------------------------------------------------------
  /// @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,
  ///                         physics simulations, etc..
  ///
  /// @return     If notification to begin frame rendering was delivered to the
  ///             running isolate.
  ///
233
  bool BeginFrame(fml::TimePoint frame_time);
234

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
  //----------------------------------------------------------------------------
  /// @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.
  ///
261 262
  bool ReportTimings(std::vector<int64_t> timings);

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 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
  //----------------------------------------------------------------------------
  /// @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.
  ///
  /// NotifyIdle is advisory and not making the same or making it with
  /// insufficient deadlines does not mean that the VM will keep consuming
  /// memory indefinitely. Even if the engine made absolutely no calls to
  /// NotifyIdle, the VM would still perform garbage collection. The only issue
  /// in such a world would be that the GC pause would happen in the middle of a
  /// frame workload.
  ///
  /// 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`.
  ///
  /// @return     If the idle notification was forwarded to the running isolate.
  ///
336
  bool NotifyIdle(int64_t deadline);
337

338 339 340 341 342 343 344
  //----------------------------------------------------------------------------
  /// @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.
  ///
345 346
  bool IsRootIsolateRunning() const;

347 348 349 350 351 352 353 354 355
  //----------------------------------------------------------------------------
  /// @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.
  ///
356
  bool DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message);
357

358 359 360 361 362 363 364 365 366
  //----------------------------------------------------------------------------
  /// @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.
  ///
367 368
  bool DispatchPointerDataPacket(const PointerDataPacket& packet);

369 370 371 372 373 374 375 376 377 378 379 380
  //----------------------------------------------------------------------------
  /// @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.
  ///
381
  bool DispatchSemanticsAction(int32_t id,
382 383
                               SemanticsAction action,
                               std::vector<uint8_t> args);
384

385 386 387 388 389 390
  //----------------------------------------------------------------------------
  /// @brief      Gets the main port identifier of the root isolate.
  ///
  /// @return     The main port identifier. If no root isolate is running,
  ///             returns `ILLEGAL_PORT`.
  ///
391
  Dart_Port GetMainPort();
392

393 394 395 396 397 398 399 400 401 402 403 404 405
  //----------------------------------------------------------------------------
  /// @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.
  ///
406
  std::string GetIsolateName();
407

408 409 410 411 412 413
  //----------------------------------------------------------------------------
  /// @brief      Returns if the root isolate has any live receive ports.
  ///
  /// @return     True if there are live receive ports, False otherwise. Return
  ///             False is the root isolate is not running as well.
  ///
414
  bool HasLivePorts();
415

416 417 418 419 420
  //----------------------------------------------------------------------------
  /// @brief      Get the last error encountered by the microtask queue.
  ///
  /// @return     The last error encountered by the microtask queue.
  ///
421
  tonic::DartErrorHandleType GetLastError();
422

423 424 425 426 427 428 429 430
  //----------------------------------------------------------------------------
  /// @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
  ///             phase.
  ///
  /// @return     The root isolate reference.
  ///
431
  std::weak_ptr<DartIsolate> GetRootIsolate();
432

433 434 435 436 437 438 439 440 441 442
  //----------------------------------------------------------------------------
  /// @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.
  ///
443
  std::pair<bool, uint32_t> GetRootIsolateReturnCode();
444

445
 private:
446 447 448 449
  struct Locale {
    Locale(std::string language_code_,
           std::string country_code_,
           std::string script_code_,
450 451 452
           std::string variant_code_);

    ~Locale();
453 454 455 456 457 458 459

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

460
  RuntimeDelegate& client_;
B
Ben Konyi 已提交
461
  DartVM* const vm_;
462
  fml::RefPtr<const DartSnapshot> isolate_snapshot_;
463
  TaskRunners task_runners_;
464
  fml::WeakPtr<SnapshotDelegate> snapshot_delegate_;
465
  fml::WeakPtr<IOManager> io_manager_;
466
  fml::RefPtr<SkiaUnrefQueue> unref_queue_;
467
  fml::WeakPtr<ImageDecoder> image_decoder_;
468 469
  std::string advisory_script_uri_;
  std::string advisory_script_entrypoint_;
470
  std::function<void(int64_t)> idle_notification_callback_;
471
  WindowData window_data_;
472
  std::weak_ptr<DartIsolate> root_isolate_;
473
  std::pair<bool, uint32_t> root_isolate_return_code_ = {false, 0};
474 475
  const fml::closure isolate_create_callback_;
  const fml::closure isolate_shutdown_callback_;
476 477
  std::shared_ptr<const fml::Mapping> persistent_isolate_data_;

478 479 480 481
  Window* GetWindowIfAvailable();

  bool FlushRuntimeStateToIsolate();

482
  // |WindowClient|
483
  std::string DefaultRouteName() override;
484

485
  // |WindowClient|
486
  void ScheduleFrame() override;
487

488
  // |WindowClient|
489
  void Render(Scene* scene) override;
490

491
  // |WindowClient|
492
  void UpdateSemantics(SemanticsUpdate* update) override;
493

494
  // |WindowClient|
495
  void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) override;
496

497
  // |WindowClient|
498
  FontCollection& GetFontCollection() override;
499

500
  // |WindowClient|
501 502
  void UpdateIsolateDescription(const std::string isolate_name,
                                int64_t isolate_port) override;
503

504 505 506
  // |WindowClient|
  void SetNeedsReportTimings(bool value) override;

507 508 509
  // |WindowClient|
  std::shared_ptr<const fml::Mapping> GetPersistentIsolateData() override;

510
  FML_DISALLOW_COPY_AND_ASSIGN(RuntimeController);
511 512
};

513
}  // namespace flutter
514

515
#endif  // FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_