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 FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_
6#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#include "rapidjson/document.h"
22#include "rapidjson/stringbuffer.h"
23
24namespace flutter {
25
26class Scene;
27class RuntimeDelegate;
28class View;
29class Window;
30
31//------------------------------------------------------------------------------
32/// Represents an instance of a running root isolate with window bindings. In
33/// normal operation, a single instance of this object is owned by the engine
34/// per shell. This object may only be created, used, and collected on the UI
35/// task runner. Window state queried by the root isolate is stored by this
36/// object. In cold-restart scenarios, the engine may collect this before
37/// installing a new runtime controller in its place. The Clone method may be
38/// used by the engine to copy the currently accumulated window state so it can
39/// be referenced by the new runtime controller.
40///
41class RuntimeController : public PlatformConfigurationClient {
42 public:
43 //----------------------------------------------------------------------------
44 /// @brief Creates a new instance of a runtime controller. This is
45 /// usually only done by the engine instance associated with the
46 /// shell.
47 ///
48 /// @param client The runtime delegate. This is
49 /// usually the `Engine` instance.
50 /// @param vm A reference to a running Dart VM.
51 /// The runtime controller must be
52 /// collected before the VM is
53 /// destroyed (this order is
54 /// guaranteed by the shell).
55 /// @param[in] isolate_snapshot The isolate snapshot used to start
56 /// the root isolate managed by this
57 /// runtime controller. The isolate
58 /// must be transitioned into the
59 /// running phase manually by the
60 /// caller.
61 /// @param[in] task_runners The task runners used by the shell
62 /// hosting this runtime controller.
63 /// This may be used by the isolate to
64 /// scheduled asynchronous texture
65 /// uploads or post tasks to the
66 /// platform task runner.
67 /// @param[in] snapshot_delegate The snapshot delegate used by the
68 /// isolate to gather raster snapshots
69 /// of Flutter view hierarchies.
70 /// @param[in] io_manager The IO manager used by the isolate
71 /// for asynchronous texture uploads.
72 /// @param[in] unref_queue The unref queue used by the
73 /// isolate to collect resources that
74 /// may reference resources on the
75 /// GPU.
76 /// @param[in] image_decoder The image decoder
77 /// @param[in] advisory_script_uri The advisory script URI (only used
78 /// for debugging). This does not
79 /// affect the code being run in the
80 /// isolate in any way.
81 /// @param[in] advisory_script_entrypoint The advisory script entrypoint
82 /// (only used for debugging). This
83 /// does not affect the code being run
84 /// in the isolate in any way. The
85 /// isolate must be transitioned to
86 /// the running state explicitly by
87 /// the caller.
88 /// @param[in] idle_notification_callback The idle notification callback.
89 /// This allows callers to run native
90 /// code in isolate scope when the VM
91 /// is about to be notified that the
92 /// engine is going to be idle.
93 /// @param[in] platform_data The window data (if exists).
94 /// @param[in] isolate_create_callback The isolate create callback. This
95 /// allows callers to run native code
96 /// in isolate scope on the UI task
97 /// runner as soon as the root isolate
98 /// has been created.
99 /// @param[in] isolate_shutdown_callback The isolate shutdown callback.
100 /// This allows callers to run native
101 /// code in isolate scoped on the UI
102 /// task runner just as the root
103 /// isolate is about to be torn down.
104 /// @param[in] persistent_isolate_data Unstructured persistent read-only
105 /// data that the root isolate can
106 /// access in a synchronous manner.
107 ///
108 RuntimeController(
109 RuntimeDelegate& client,
110 DartVM* vm,
111 fml::RefPtr<const DartSnapshot> isolate_snapshot,
112 TaskRunners task_runners,
113 fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
114 fml::WeakPtr<IOManager> io_manager,
115 fml::RefPtr<SkiaUnrefQueue> unref_queue,
116 fml::WeakPtr<ImageDecoder> image_decoder,
117 std::string advisory_script_uri,
118 std::string advisory_script_entrypoint,
119 const std::function<void(int64_t)>& idle_notification_callback,
120 const PlatformData& platform_data,
121 const fml::closure& isolate_create_callback,
122 const fml::closure& isolate_shutdown_callback,
123 std::shared_ptr<const fml::Mapping> persistent_isolate_data);
124
125 // |PlatformConfigurationClient|
126 ~RuntimeController() override;
127
128 //----------------------------------------------------------------------------
129 /// @brief Clone the the runtime controller. This re-creates the root
130 /// isolate with the same snapshots and copies all window data to
131 /// the new instance. This is usually only used in the debug
132 /// runtime mode to support the cold-restart scenario.
133 ///
134 /// @return A clone of the existing runtime controller.
135 ///
136 std::unique_ptr<RuntimeController> Clone() const;
137
138 //----------------------------------------------------------------------------
139 /// @brief Forward the specified viewport metrics to the running isolate.
140 /// If the isolate is not running, these metrics will be saved and
141 /// flushed to the isolate when it starts.
142 ///
143 /// @param[in] metrics The viewport metrics.
144 ///
145 /// @return If the window metrics were forwarded to the running isolate.
146 ///
147 bool SetViewportMetrics(const ViewportMetrics& metrics);
148
149 //----------------------------------------------------------------------------
150 /// @brief Forward the specified locale data to the running isolate. If
151 /// the isolate is not running, this data will be saved and
152 /// flushed to the isolate when it starts running.
153 ///
154 /// @deprecated The persistent isolate data must be used for this purpose
155 /// instead.
156 ///
157 /// @param[in] locale_data The locale data. This should consist of groups of
158 /// 4 strings, each group representing a single locale.
159 ///
160 /// @return If the locale data was forwarded to the running isolate.
161 ///
162 bool SetLocales(const std::vector<std::string>& locale_data);
163
164 //----------------------------------------------------------------------------
165 /// @brief Forward the user settings data to the running isolate. If the
166 /// isolate is not running, this data will be saved and flushed to
167 /// the isolate when it starts running.
168 ///
169 /// @deprecated The persistent isolate data must be used for this purpose
170 /// instead.
171 ///
172 /// @param[in] data The user settings data.
173 ///
174 /// @return If the user settings data was forwarded to the running
175 /// isolate.
176 ///
177 bool SetUserSettingsData(const std::string& data);
178
179 //----------------------------------------------------------------------------
180 /// @brief Forward the lifecycle state data to the running isolate. If
181 /// the isolate is not running, this data will be saved and
182 /// flushed to the isolate when it starts running.
183 ///
184 /// @deprecated The persistent isolate data must be used for this purpose
185 /// instead.
186 ///
187 /// @param[in] data The lifecycle state data.
188 ///
189 /// @return If the lifecycle state data was forwarded to the running
190 /// isolate.
191 ///
192 bool SetLifecycleState(const std::string& data);
193
194 //----------------------------------------------------------------------------
195 /// @brief Notifies the running isolate about whether the semantics tree
196 /// should be generated or not. If the isolate is not running,
197 /// this preference will be saved and flushed to the isolate when
198 /// it starts running.
199 ///
200 /// @param[in] enabled Indicates whether to generate the semantics tree.
201 ///
202 /// @return If the semantics tree generation preference was forwarded to
203 /// the running isolate.
204 ///
205 bool SetSemanticsEnabled(bool enabled);
206
207 //----------------------------------------------------------------------------
208 /// @brief Forward the preference of accessibility features that must be
209 /// enabled in the semantics tree to the running isolate. If the
210 /// isolate is not running, this data will be saved and flushed to
211 /// the isolate when it starts running.
212 ///
213 /// @param[in] flags The accessibility features that must be generated in
214 /// the semantics tree.
215 ///
216 /// @return If the preference of accessibility features was forwarded to
217 /// the running isolate.
218 ///
219 bool SetAccessibilityFeatures(int32_t flags);
220
221 //----------------------------------------------------------------------------
222 /// @brief Notifies the running isolate that it should start generating a
223 /// new frame.
224 ///
225 /// @see `Engine::BeginFrame` for more context.
226 ///
227 /// @param[in] frame_time The point at which the current frame interval
228 /// began. May be used by animation interpolators,
229 /// physics simulations, etc.
230 ///
231 /// @return If notification to begin frame rendering was delivered to the
232 /// running isolate.
233 ///
234 bool BeginFrame(fml::TimePoint frame_time);
235
236 //----------------------------------------------------------------------------
237 /// @brief Dart code cannot fully measure the time it takes for a
238 /// specific frame to be rendered. This is because Dart code only
239 /// runs on the UI task runner. That is only a small part of the
240 /// overall frame workload. The GPU task runner frame workload is
241 /// executed on a thread where Dart code cannot run (and hence
242 /// instrument). Besides, due to the pipelined nature of rendering
243 /// in Flutter, there may be multiple frame workloads being
244 /// processed at any given time. However, for non-Timeline based
245 /// profiling, it is useful for trace collection and processing to
246 /// happen in Dart. To do this, the GPU task runner frame
247 /// workloads need to be instrumented separately. After a set
248 /// number of these profiles have been gathered, they need to be
249 /// reported back to Dart code. The engine reports this extra
250 /// instrumentation information back to Dart code running on the
251 /// engine by invoking this method at predefined intervals.
252 ///
253 /// @see `Engine::ReportTimings`, `FrameTiming`
254 ///
255 /// @param[in] timings Collection of `FrameTiming::kCount` * `n` timestamps
256 /// for `n` frames whose timings have not been reported
257 /// yet. A collection of integers is reported here for
258 /// easier conversions to Dart objects. The timestamps
259 /// are measured against the system monotonic clock
260 /// measured in microseconds.
261 ///
262 bool ReportTimings(std::vector<int64_t> timings);
263
264 //----------------------------------------------------------------------------
265 /// @brief Notify the Dart VM that no frame workloads are expected on the
266 /// UI task runner till the specified deadline. The VM uses this
267 /// opportunity to perform garbage collection operations is a
268 /// manner that interferes as little as possible with frame
269 /// rendering.
270 ///
271 /// NotifyIdle is advisory. The VM may or may not run a garbage collection
272 /// when this is called, and will eventually perform garbage collections even
273 /// if it is not called or it is called with insufficient deadlines.
274 ///
275 /// The garbage collection mechanism and its thresholds are internal
276 /// implementation details and absolutely no guarantees are made about the
277 /// threshold discussed below. This discussion is also an oversimplification
278 /// but hopefully serves to calibrate expectations about GC behavior:
279 /// * When the Dart VM and its root isolate are initialized, the memory
280 /// consumed upto that point are treated as a baseline.
281 /// * A fixed percentage of the memory consumed (~20%) over the baseline is
282 /// treated as the hard threshold.
283 /// * The memory in play is divided into old space and new space. The new
284 /// space is typically very small and fills up rapidly.
285 /// * The baseline plus the threshold is considered the old space while the
286 /// small new space is a separate region (typically a few pages).
287 /// * The total old space size minus the max new space size is treated as the
288 /// soft threshold.
289 /// * In a world where there is no call to NotifyIdle, when the total
290 /// allocation exceeds the soft threshold, a concurrent mark is initiated in
291 /// the VM. There is a “small” pause that occurs when the concurrent mark is
292 /// initiated and another pause when the mark concludes and a sweep is
293 /// initiated.
294 /// * If the total allocations exceeds the the hard threshold, a “big”
295 /// stop-the-world pause is initiated.
296 /// * If after either the sweep after the concurrent mark, or, the
297 /// stop-the-world pause, the consumption returns to be below the soft
298 /// threshold, the dance begins anew.
299 /// * If after both the “small” and “big” pauses, memory usage is still over
300 /// the hard threshold, i.e, the objects are still reachable, that amount of
301 /// memory is treated as the new baseline and a fixed percentage of the new
302 /// baseline over the new baseline is now the new hard threshold.
303 /// * Updating the baseline will continue till memory for the updated old
304 /// space can be allocated from the operating system. These allocations will
305 /// typically fail due to address space exhaustion on 32-bit systems and
306 /// page table exhaustion on 64-bit systems.
307 /// * NotifyIdle initiates the concurrent mark preemptively. The deadline is
308 /// used by the VM to determine if the corresponding sweep can be performed
309 /// within the deadline. This way, jank due to “small” pauses can be
310 /// ameliorated.
311 /// * There is no ability to stop a “big” pause on reaching the hard threshold
312 /// in the old space. The best you can do is release (by making them
313 /// unreachable) objects eagerly so that the are marked as unreachable in
314 /// the concurrent mark initiated by either reaching the soft threshold or
315 /// an explicit NotifyIdle.
316 /// * If you are running out of memory, its because too many large objects
317 /// were allocation and remained reachable such that the the old space kept
318 /// growing till it could grow no more.
319 /// * At the edges of allocation thresholds, failures can occur gracefully if
320 /// the instigating allocation was made in the Dart VM or rather gracelessly
321 /// if the allocation is made by some native component.
322 ///
323 /// @see `Dart_TimelineGetMicros`
324 ///
325 /// @bug The `deadline` argument must be converted to `std::chrono`
326 /// instead of a raw integer.
327 ///
328 /// @param[in] deadline The deadline measures in microseconds against the
329 /// system's monotonic time. The clock can be accessed via
330 /// `Dart_TimelineGetMicros`.
331 ///
332 /// @return If the idle notification was forwarded to the running isolate.
333 ///
334 bool NotifyIdle(int64_t deadline);
335
336 //----------------------------------------------------------------------------
337 /// @brief Returns if the root isolate is running. The isolate must be
338 /// transitioned to the running phase manually. The isolate can
339 /// stop running if it terminates execution on its own.
340 ///
341 /// @return True if root isolate running, False otherwise.
342 ///
343 virtual bool IsRootIsolateRunning() const;
344
345 //----------------------------------------------------------------------------
346 /// @brief Dispatch the specified platform message to running root
347 /// isolate.
348 ///
349 /// @param[in] message The message to dispatch to the isolate.
350 ///
351 /// @return If the message was dispatched to the running root isolate.
352 /// This may fail is an isolate is not running.
353 ///
354 virtual bool DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message);
355
356 //----------------------------------------------------------------------------
357 /// @brief Dispatch the specified pointer data message to the running
358 /// root isolate.
359 ///
360 /// @param[in] packet The pointer data message to dispatch to the isolate.
361 ///
362 /// @return If the pointer data message was dispatched. This may fail is
363 /// an isolate is not running.
364 ///
365 bool DispatchPointerDataPacket(const PointerDataPacket& packet);
366
367 //----------------------------------------------------------------------------
368 /// @brief Dispatch the semantics action to the specified accessibility
369 /// node.
370 ///
371 /// @param[in] id The identified of the accessibility node.
372 /// @param[in] action The semantics action to perform on the specified
373 /// accessibility node.
374 /// @param[in] args Optional data that applies to the specified action.
375 ///
376 /// @return If the semantics action was dispatched. This may fail if an
377 /// isolate is not running.
378 ///
379 bool DispatchSemanticsAction(int32_t id,
380 SemanticsAction action,
381 std::vector<uint8_t> args);
382
383 //----------------------------------------------------------------------------
384 /// @brief Gets the main port identifier of the root isolate.
385 ///
386 /// @return The main port identifier. If no root isolate is running,
387 /// returns `ILLEGAL_PORT`.
388 ///
389 Dart_Port GetMainPort();
390
391 //----------------------------------------------------------------------------
392 /// @brief Gets the debug name of the root isolate. But default, the
393 /// debug name of the isolate is derived from its advisory script
394 /// URI, advisory main entrypoint and its main port name. For
395 /// example, "main.dart$main-1234" where the script URI is
396 /// "main.dart", the entrypoint is "main" and the port name
397 /// "1234". Once launched, the isolate may re-christen itself
398 /// using a name it selects via `setIsolateDebugName` in
399 /// `window.dart`. This name is purely advisory and only used by
400 /// instrumentation and reporting purposes.
401 ///
402 /// @return The debug name of the root isolate.
403 ///
404 std::string GetIsolateName();
405
406 //----------------------------------------------------------------------------
407 /// @brief Returns if the root isolate has any live receive ports.
408 ///
409 /// @return True if there are live receive ports, False otherwise. Return
410 /// False if the root isolate is not running as well.
411 ///
412 bool HasLivePorts();
413
414 //----------------------------------------------------------------------------
415 /// @brief Get the last error encountered by the microtask queue.
416 ///
417 /// @return The last error encountered by the microtask queue.
418 ///
419 tonic::DartErrorHandleType GetLastError();
420
421 //----------------------------------------------------------------------------
422 /// @brief Get a weak pointer to the root Dart isolate. This isolate may
423 /// only be locked on the UI task runner. Callers use this
424 /// accessor to transition to the root isolate to the running
425 /// phase.
426 ///
427 /// @return The root isolate reference.
428 ///
429 std::weak_ptr<DartIsolate> GetRootIsolate();
430
431 //----------------------------------------------------------------------------
432 /// @brief Get the return code specified by the root isolate (if one is
433 /// present).
434 ///
435 /// @bug Change this method to return `std::optional<uint32_t>`
436 /// instead.
437 ///
438 /// @return The root isolate return code. The first argument in the pair
439 /// indicates if one is specified by the root isolate.
440 ///
441 std::pair<bool, uint32_t> GetRootIsolateReturnCode();
442
443 protected:
444 /// Constructor for Mocks.
445 RuntimeController(RuntimeDelegate& client, TaskRunners p_task_runners);
446
447 private:
448 struct Locale {
449 Locale(std::string language_code_,
450 std::string country_code_,
451 std::string script_code_,
452 std::string variant_code_);
453
454 ~Locale();
455
456 std::string language_code;
457 std::string country_code;
458 std::string script_code;
459 std::string variant_code;
460 };
461
462 RuntimeDelegate& client_;
463 DartVM* const vm_;
464 fml::RefPtr<const DartSnapshot> isolate_snapshot_;
465 TaskRunners task_runners_;
466 fml::WeakPtr<SnapshotDelegate> snapshot_delegate_;
467 fml::WeakPtr<IOManager> io_manager_;
468 fml::RefPtr<SkiaUnrefQueue> unref_queue_;
469 fml::WeakPtr<ImageDecoder> image_decoder_;
470 std::string advisory_script_uri_;
471 std::string advisory_script_entrypoint_;
472 std::function<void(int64_t)> idle_notification_callback_;
473 PlatformData platform_data_;
474 std::weak_ptr<DartIsolate> root_isolate_;
475 std::pair<bool, uint32_t> root_isolate_return_code_ = {false, 0};
476 const fml::closure isolate_create_callback_;
477 const fml::closure isolate_shutdown_callback_;
478 std::shared_ptr<const fml::Mapping> persistent_isolate_data_;
479
480 PlatformConfiguration* GetPlatformConfigurationIfAvailable();
481
482 bool FlushRuntimeStateToIsolate();
483
484 // |PlatformConfigurationClient|
485 std::string DefaultRouteName() override;
486
487 // |PlatformConfigurationClient|
488 void ScheduleFrame() override;
489
490 // |PlatformConfigurationClient|
491 void Render(Scene* scene) override;
492
493 // |PlatformConfigurationClient|
494 void UpdateSemantics(SemanticsUpdate* update) override;
495
496 // |PlatformConfigurationClient|
497 void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) override;
498
499 // |PlatformConfigurationClient|
500 FontCollection& GetFontCollection() override;
501
502 // |PlatformConfigurationClient|
503 void UpdateIsolateDescription(const std::string isolate_name,
504 int64_t isolate_port) override;
505
506 // |PlatformConfigurationClient|
507 void SetNeedsReportTimings(bool value) override;
508
509 // |PlatformConfigurationClient|
510 std::shared_ptr<const fml::Mapping> GetPersistentIsolateData() override;
511
512 // |PlatformConfigurationClient|
513 std::unique_ptr<std::vector<std::string>> ComputePlatformResolvedLocale(
514 const std::vector<std::string>& supported_locale_data) override;
515
516 FML_DISALLOW_COPY_AND_ASSIGN(RuntimeController);
517};
518
519} // namespace flutter
520
521#endif // FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_
522