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 | |
24 | namespace flutter { |
25 | |
26 | class Scene; |
27 | class RuntimeDelegate; |
28 | class View; |
29 | class 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 | /// |
41 | class 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 | |