diff --git a/shell/common/isolate_configuration.h b/shell/common/isolate_configuration.h index 88833df7faf23..c5f286103a0d6 100644 --- a/shell/common/isolate_configuration.h +++ b/shell/common/isolate_configuration.h @@ -19,30 +19,139 @@ namespace flutter { -/// Abstract Base Class that will configure a |DartIsolate|. +//------------------------------------------------------------------------------ +/// @brief An isolate configuration is a collection of snapshots and asset +/// managers that the engine will use to configure the isolate +/// before invoking its root entrypoint. The set of snapshots must +/// be sufficient for the engine to move the isolate from the +/// |DartIsolate::Phase::LibrariesSetup| phase to the +/// |DartIsolate::Phase::Ready| phase. Note that the isolate +/// configuration will not be collected till the isolate tied to the +/// configuration as well as any and all child isolates of that +/// isolate are collected. The engine may ask the configuration to +/// prepare multiple isolates. All subclasses of this class must be +/// thread safe as the configuration may be created, collected and +/// used on multiple threads. Usually these threads are engine or VM +/// managed so care must be taken to ensure that subclasses do not +/// reference any thread local state. +/// class IsolateConfiguration { public: + //---------------------------------------------------------------------------- + /// @brief Attempts to infer the isolate configuration from the + /// `Settings` object. If the VM is configured for AOT mode, + /// snapshot resolution is attempted with predefined symbols + /// present in the currently loaded process. In JIT mode, Dart + /// kernel file resolution is attempted in the assets directory. + /// If an IO worker is specified, snapshot resolution may be + /// attempted on the serial worker task runner. The worker task + /// runner thread must remain valid and running till after the + /// shell associated with the engine used to launch the isolate + /// for which this run configuration is used is collected. + /// + /// @param[in] settings The settings + /// @param[in] asset_manager The asset manager + /// @param[in] io_worker An optional IO worker. Specify `nullptr` is a + /// worker should not be used or one is not + /// available. + /// + /// @return An isolate configuration if one can be inferred from the + /// settings. If not, returns `nullptr`. + /// static std::unique_ptr InferFromSettings( const Settings& settings, std::shared_ptr asset_manager, fml::RefPtr io_worker); + //---------------------------------------------------------------------------- + /// @brief Creates an AOT isolate configuration using snapshot symbols + /// present in the currently loaded process. These symbols need to + /// be given to the Dart VM on bootstrap and hence have already + /// been resolved. + /// + /// @return An AOT isolate configuration. + /// static std::unique_ptr CreateForAppSnapshot(); - static std::unique_ptr CreateForKernel( - std::unique_ptr kernel); - + //---------------------------------------------------------------------------- + /// @brief Creates a JIT isolate configuration using a list of futures to + /// snapshots defining the ready isolate state. In environments + /// where snapshot resolution is extremely expensive, embedders + /// attempt to resolve snapshots on worker thread(s) and return + /// the future of the promise of snapshot resolution to this + /// method. That way, snapshot resolution begins well before + /// isolate launch is attempted by the engine. + /// + /// @param[in] kernel_pieces The list of futures to Dart kernel snapshots. + /// + /// @return A JIT isolate configuration. + /// static std::unique_ptr CreateForKernelList( std::vector>> kernel_pieces); + //---------------------------------------------------------------------------- + /// @brief Creates a JIT isolate configuration using the specified + /// snapshot. This is a convenience method for the + /// `CreateForKernelList` method that takes a list of futures to + /// Dart kernel snapshots. + /// + /// @see CreateForKernelList() + /// + /// @param[in] kernel The kernel snapshot. + /// + /// @return A JIT isolate configuration. + /// + static std::unique_ptr CreateForKernel( + std::unique_ptr kernel); + + //---------------------------------------------------------------------------- + /// @brief Creates a JIT isolate configuration using the specified + /// snapshots. This is a convenience method for the + /// `CreateForKernelList` method that takes a list of futures to + /// Dart kernel snapshots. + /// + /// @see CreateForKernelList() + /// + /// @param[in] kernel_pieces The kernel pieces + /// + /// @return { description_of_the_return_value } + /// static std::unique_ptr CreateForKernelList( std::vector> kernel_pieces); + //---------------------------------------------------------------------------- + /// @brief Create an isolate configuration. This has no threading + /// restrictions. + /// IsolateConfiguration(); + //---------------------------------------------------------------------------- + /// @brief Destroys an isolate configuration. This has no threading + /// restrictions and may be collection of configurations may occur + /// on any thread (and usually happens on an internal VM managed + /// thread pool thread). + /// virtual ~IsolateConfiguration(); + //---------------------------------------------------------------------------- + /// @brief When an isolate is created and sufficiently initialized to + /// move it into the `DartIsolate::Phase::LibrariesSetup` phase, + /// this method is invoked on the isolate to then move the isolate + /// into the `DartIsolate::Phase::Ready` phase. Then isolate's + /// main entrypoint is then invoked to move it into the + /// `DartIsolate::Phase::Running` phase. This method will be + /// called each time the root isolate is launched (which may be + /// multiple times in cold-restart scenarios) as well as one each + /// for any child isolates referenced by that isolate. + /// + /// @param isolate The isolate which is already in the + /// `DartIsolate::Phase::LibrariesSetup` phase. + /// + /// @return Returns true if the isolate could be configured. Unless this + /// returns true, the engine will not move the isolate to the + /// `DartIsolate::Phase::Ready` phase for subsequent run. + /// bool PrepareIsolate(DartIsolate& isolate); protected: diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 7965975249a50..fcbb63eee2f0e 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -25,95 +25,507 @@ namespace flutter { class Shell; -/// Abstract Base Class that represents the platform specific view we will be -/// rendering to. +//------------------------------------------------------------------------------ +/// @brief Platform views are created by the shell on the platform task +/// runner. Unless explicitly specified, all platform view methods +/// are called on the platform task runner as well. Platform views +/// are usually sub-classed on a per platform basis and the bulk of +/// the window system integration happens using that subclass. Since +/// most platform window toolkits are usually only safe to access on +/// a single "main" thread, any interaction that requires access to +/// the underlying platform's window toolkit is routed through the +/// platform view associated with that shell. This involves +/// operations like settings up and tearing down the render surface, +/// platform messages, interacting with accessibility features on +/// the platform, input events, etc. +/// class PlatformView { public: + //---------------------------------------------------------------------------- + /// @brief Used to forward events from the platform view to interested + /// subsystems. This forwarding is done by the shell which sets + /// itself up as the delegate of the platform view. + /// class Delegate { public: + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the platform view was created + /// with the given render surface. This surface is platform + /// (iOS, Android) and client-rendering API (OpenGL, Software, + /// Metal, Vulkan) specific. This is usually a sign to the + /// rasterizer to setup and begin rendering to that surface. + /// + /// @param[in] surface The surface + /// virtual void OnPlatformViewCreated(std::unique_ptr surface) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the platform view was destroyed. + /// This is usually a sign to the rasterizer to suspend + /// rendering a previously configured surface and collect any + /// intermediate resources. + /// virtual void OnPlatformViewDestroyed() = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the specified callback needs to + /// be invoked after the rasterizer is done rendering the next + /// frame. This callback will be called on the render thread and + /// it is caller responsibility to perform any re-threading as + /// necessary. Due to the asynchronous nature of rendering in + /// Flutter, embedders usually add a placeholder over the + /// contents in which Flutter is going to render when Flutter is + /// first initialized. This callback may be used as a signal to + /// remove that placeholder. + /// + /// @attention The callback will be invoked on the render thread and not + /// the calling thread. + /// + /// @param[in] closure The callback to execute on the next frame. + /// virtual void OnPlatformViewSetNextFrameCallback(fml::closure closure) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate the viewport metrics of the platform + /// view have been updated. The rasterizer will need to be + /// reconfigured to render the frame in the updated viewport + /// metrics. + /// + /// @param[in] metrics The updated viewport metrics. + /// virtual void OnPlatformViewSetViewportMetrics( const ViewportMetrics& metrics) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the platform has dispatched a + /// platform message from the embedder to the Flutter + /// application. This message must be forwarded to the running + /// isolate hosted by the engine on the UI thread. + /// + /// @param[in] message The platform message to dispatch to the running + /// root isolate. + /// virtual void OnPlatformViewDispatchPlatformMessage( fml::RefPtr message) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the platform view has encountered + /// a pointer event. This pointer event needs to be forwarded to + /// the running root isolate hosted by the engine on the UI + /// thread. + /// + /// @param[in] packet The pointer data packet containing multiple pointer + /// events. + /// virtual void OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the platform view has encountered + /// an accessibility related action on the specified node. This + /// event must be forwarded to the running root isolate hosted + /// by the engine on the UI thread. + /// + /// @param[in] id The identifier of the accessibility node. + /// @param[in] action The accessibility related action performed on the + /// node of the specified ID. + /// @param[in] args An optional list of argument that apply to the + /// specified action. + /// virtual void OnPlatformViewDispatchSemanticsAction( int32_t id, SemanticsAction action, std::vector args) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the embedder has expressed an + /// opinion about whether the accessibility tree needs to be + /// enabled or disabled. This information needs to be forwarded + /// to the root isolate running on the UI thread. + /// + /// @param[in] enabled Whether the accessibility tree is enabled or + /// disabled. + /// virtual void OnPlatformViewSetSemanticsEnabled(bool enabled) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the embedder has expressed an + /// opinion about the features to enable in the accessibility + /// tree. + /// + /// The engine does not care about the accessibility feature + /// flags as all it does is forward this information from the + /// embedder to the framework. However, curious readers may + /// refer to `AccessibilityFeatures` in `window.dart` for + /// currently supported accessibility feature flags. + /// + /// @param[in] flags The features to enable in the accessibility tree. + /// virtual void OnPlatformViewSetAccessibilityFeatures(int32_t flags) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the embedder has specified a + /// texture that it want the rasterizer to composite within the + /// Flutter layer tree. All textures must have a unique + /// identifier. When the rasterizer encounters an external + /// texture within its hierarchy, it gives the embedder a chance + /// to update that texture on the GPU thread before it + /// composites the same on-screen. + /// + /// @param[in] texture The texture that is being updated by the embedder + /// but composited by Flutter in its own hierarchy. + /// virtual void OnPlatformViewRegisterTexture( - std::shared_ptr texture) = 0; - + std::shared_ptr texture) = 0; + + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the embedder will no longer + /// attempt to composite the specified texture within the layer + /// tree. This allows the rasterizer to collect associated + /// resources. + /// + /// @param[in] texture_id The identifier of the texture to unregister. If + /// the texture has not been previously registered, + /// this call does nothing. + /// virtual void OnPlatformViewUnregisterTexture(int64_t texture_id) = 0; + //-------------------------------------------------------------------------- + /// @brief Notifies the delegate that the embedder has updated the + /// contents of the texture with the specified identifier. + /// Typically, Flutter will only render a frame if there is an + /// updated layer tree. However, in cases where the layer tree + /// is static but one of the externally composited textures has + /// been updated by the embedder, the embedder needs to notify + /// the rasterizer to render a new frame. In such cases, the + /// existing layer tree may be reused with the frame composited + /// with all updated external textures. + /// + /// @param[in] texture_id The identifier of the texture that has been + /// updated. + /// virtual void OnPlatformViewMarkTextureFrameAvailable( int64_t texture_id) = 0; }; + //---------------------------------------------------------------------------- + /// @brief Creates a platform view with the specified delegate and task + /// runner. The base class by itself does not do much but is + /// suitable for use in test environments where full platform + /// integration may not be necessary. The platform view may only + /// be created, accessed and destroyed on the platform task + /// runner. + /// + /// @param delegate The delegate. This is typically the shell. + /// @param[in] task_runners The task runners used by this platform view. + /// explicit PlatformView(Delegate& delegate, TaskRunners task_runners); + //---------------------------------------------------------------------------- + /// @brief Destroys the platform view. The platform view is owned by the + /// shell and will be destroyed by the same on the platform tasks + /// runner. + /// virtual ~PlatformView(); + //---------------------------------------------------------------------------- + /// @brief Invoked by the shell to obtain a platform specific vsync + /// waiter. It is optional for platforms to override this method + /// and provide a custom vsync waiter because a timer based + /// fall-back waiter is used by default. However, it is highly + /// recommended that platform provide their own Vsync waiter as + /// the timer based fall-back will not render frames aligned with + /// vsync boundaries. + /// + /// @attention If a timer based fall-back is used, a warning is logged to the + /// console. In case this method is overridden in a subclass, it + /// must return a valid vsync waiter. Returning null will lead to + /// internal errors. If a valid vsync waiter cannot be returned, + /// subclasses should just call the based class method instead. + /// + /// @return A vsync waiter. If is an internal error to return a null + /// waiter. + /// virtual std::unique_ptr CreateVSyncWaiter(); + //---------------------------------------------------------------------------- + /// @brief Used by embedders to dispatch a platform message to a + /// running root isolate hosted by the engine. If an isolate is + /// not running, the message is dropped. If there is no one on the + /// other side listening on the channel, the message is dropped. + /// When a platform message is dropped, any response handles + /// associated with that message will be dropped as well. All + /// users of platform messages must assume that message may not be + /// delivered and/or their response handles may not be invoked. + /// Platform messages are not buffered. + /// + /// For embedders that wish to respond to platform message + /// directed from the framework to the embedder, the + /// `HandlePlatformMessage` method may be overridden. + /// + /// @see HandlePlatformMessage() + /// + /// @param[in] message The platform message to deliver to the root isolate. + /// void DispatchPlatformMessage(fml::RefPtr message); + //---------------------------------------------------------------------------- + /// @brief Overridden by embedders to perform actions in response to + /// platform messages sent from the framework to the embedder. + /// Default implementation of this method simply returns an empty + /// response. + /// + /// Embedders that wish to send platform messages to the framework + /// may use the `DispatchPlatformMessage` method. This method is + /// for messages that go the other way. + /// + /// @see DisplatchPlatformMessage() + /// + /// @param[in] message The message + /// + virtual void HandlePlatformMessage(fml::RefPtr message); + + //---------------------------------------------------------------------------- + /// @brief Used by embedders to dispatch an accessibility action to a + /// running isolate hosted by the engine. + /// + /// @param[in] id The identifier of the accessibility node on which to + /// perform the action. + /// @param[in] action The action + /// @param[in] args The arguments + /// void DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args); + //---------------------------------------------------------------------------- + /// @brief Used by embedder to notify the running isolate hosted by the + /// engine on the UI thread that the accessibility tree needs to + /// be generated. + /// + /// @attention Subclasses may choose to override this method to perform + /// platform specific functions. However, they must call the base + /// class method at some point in their implementation. + /// + /// @param[in] enabled Whether the accessibility tree needs to be generated. + /// virtual void SetSemanticsEnabled(bool enabled); + //---------------------------------------------------------------------------- + /// @brief Used by the embedder to specify the features to enable in the + /// accessibility tree generated by the isolate. This information + /// is forwarded to the root isolate hosted by the engine on the + /// UI thread. + /// + /// The engine does not care about the accessibility feature flags + /// as all it does is forward this information from the embedder + /// to the framework. However, curious readers may refer to + /// `AccessibilityFeatures` in `window.dart` for currently + /// supported accessibility feature flags. + /// + /// @attention Subclasses may choose to override this method to perform + /// platform specific functions. However, they must call the base + /// class method at some point in their implementation. + /// + /// @param[in] flags The features to enable in the accessibility tree. + /// virtual void SetAccessibilityFeatures(int32_t flags); + //---------------------------------------------------------------------------- + /// @brief Used by the framework to tell the embedder to apply the + /// specified semantics node updates. + /// + /// @see SemanticsNode, SemticsNodeUpdates, + /// CustomAccessibilityActionUpdates + /// + /// @param[in] updates A map with the stable semantics node identifier as + /// key and the node properties as the value. + /// @param[in] actions A map with the stable semantics node identifier as + /// key and the custom node action as the value. + /// + virtual void UpdateSemantics(SemanticsNodeUpdates updates, + CustomAccessibilityActionUpdates actions); + + //---------------------------------------------------------------------------- + /// @brief Used by embedders to specify the updated viewport metrics. In + /// response to this call, on the GPU thread, the rasterizer may + /// need to be reconfigured to the updated viewport dimensions. On + /// the UI thread, the framework may need to start generating a + /// new frame for the updated viewport metrics as well. + /// + /// @param[in] metrics The updated viewport metrics. + /// void SetViewportMetrics(const ViewportMetrics& metrics); + //---------------------------------------------------------------------------- + /// @brief Used by embedders to notify the shell that a platform view + /// has been created. This notification is used to create a + /// rendering surface and pick the client rendering API to use to + /// render into this surface. No frames will be scheduled or + /// rendered before this call. The surface must remain valid till + /// the corresponding call to NotifyDestroyed. + /// void NotifyCreated(); + //---------------------------------------------------------------------------- + /// @brief Used by embedders to notify the shell that the platform view + /// has been destroyed. This notification used to collect the + /// rendering surface and all associated resources. Frame + /// scheduling is also suspended. + /// + /// @attention Subclasses may choose to override this method to perform + /// platform specific functions. However, they must call the base + /// class method at some point in their implementation. + /// virtual void NotifyDestroyed(); - // Unlike all other methods on the platform view, this one may be called on a - // non-platform task runner. + //---------------------------------------------------------------------------- + /// @brief Used by the shell to obtain a Skia GPU context that is capable + /// of operating on the IO thread. The context must be in the same + /// share-group as the Skia GPU context used on the render thread. + /// This context will always be used on the IO thread. Because it + /// is in the same share-group as the separate render thread + /// context, any GPU resources uploaded in this context will be + /// visible to the render thread context (synchronization of GPU + /// resources is managed by Skia). + /// + /// If such context cannot be created on the IO thread, callers + /// may return `nullptr`. This will mean that all texture uploads + /// will be queued onto the render thread which will cause + /// performance issues. When this context is `nullptr`, an error + /// is logged to the console. It is highly recommended that all + /// platforms provide a resource context. + /// + /// @attention Unlike all other methods on the platform view, this will be + /// called on IO task runner. + /// + /// @return The Skia GPU context that is in the same share-group as the + /// main render thread GPU context. May be `nullptr` in case such + /// a context cannot be created. + /// virtual sk_sp CreateResourceContext() const; - // Unlike all other methods on the platform view, this one may be called on a - // non-platform task runner. + //---------------------------------------------------------------------------- + /// @brief Used by the shell to notify the embedder that the resource + /// context previously obtained via a call to + /// `CreateResourceContext()` is being collected. The embedder is + /// free to collect an platform specific resources associated with + /// this context. + /// + /// @attention Unlike all other methods on the platform view, this will be + /// called on IO task runner. + /// virtual void ReleaseResourceContext() const; + //---------------------------------------------------------------------------- + /// @brief Returns a weak pointer to the platform view. Since the + /// platform view may only be created, accessed and destroyed on + /// the platform thread, any access to the platform view from a + /// non-platform task runner needs a weak pointer to the platform + /// view along with a reference to the platform task runner. A + /// task must be posted to the platform task runner with the weak + /// pointer captured in the same. The platform view method may + /// only be called in the posted task once the weak pointer + /// validity has been checked. This method is used by callers to + /// obtain that weak pointer. + /// + /// @return The weak pointer to the platform view. + /// fml::WeakPtr GetWeakPtr() const; - virtual void UpdateSemantics(SemanticsNodeUpdates updates, - CustomAccessibilityActionUpdates actions); - - virtual void HandlePlatformMessage(fml::RefPtr message); - + //---------------------------------------------------------------------------- + /// @brief Gives embedders a chance to react to a "cold restart" of the + /// running isolate. The default implementation of this method + /// does nothing. + /// + /// While a "hot restart" patches a running isolate, a "cold + /// restart" restarts the root isolate in a running shell. + /// virtual void OnPreEngineRestart() const; + //---------------------------------------------------------------------------- + /// @brief Sets a callback that gets executed when the rasterizer renders + /// the next frame. Due to the asynchronous nature of rendering in + /// Flutter, embedders usually add a placeholder over the + /// contents in which Flutter is going to render when Flutter is + /// first initialized. This callback may be used as a signal to + /// remove that placeholder. The callback is executed on the + /// render task runner and not the platform task runner. It is + /// the embedder's responsibility to re-thread as necessary. + /// + /// @attention The callback is executed on the render task runner and not the + /// platform task runner. Embedders must re-thread as necessary. + /// + /// @param[in] closure The callback to execute on the render thread when the + /// next frame gets rendered. + /// void SetNextFrameCallback(fml::closure closure); + //---------------------------------------------------------------------------- + /// @brief Dispatches pointer events from the embedder to the + /// framework. Each pointer data packet may contain multiple + /// pointer input events. Each call to this method wakes up the UI + /// thread. + /// + /// @param[in] packet The pointer data packet to dispatch to the framework. + /// void DispatchPointerDataPacket(std::unique_ptr packet); - // Called once per texture, on the platform thread. + //-------------------------------------------------------------------------- + /// @brief Used by the embedder to specify a texture that it wants the + /// rasterizer to composite within the Flutter layer tree. All + /// textures must have a unique identifier. When the rasterizer + /// encounters an external texture within its hierarchy, it gives + /// the embedder a chance to update that texture on the GPU thread + /// before it composites the same on-screen. + /// + /// @attention This method must only be called once per texture. When the + /// texture is updated, calling `MarkTextureFrameAvailable` with + /// the specified texture identifier is sufficient to make Flutter + /// re-render the frame with the updated texture composited + /// in-line. + /// + /// @see UnregisterTexture, MarkTextureFrameAvailable + /// + /// @param[in] texture The texture that is being updated by the embedder + /// but composited by Flutter in its own hierarchy. + /// void RegisterTexture(std::shared_ptr texture); - // Called once per texture, on the platform thread. + //-------------------------------------------------------------------------- + /// @brief Used by the embedder to notify the rasterizer that it will no + /// longer attempt to composite the specified texture within the + /// layer tree. This allows the rasterizer to collect associated + /// resources. + /// + /// @attention This call must only be called once per texture identifier. + /// + /// @see RegisterTexture, MarkTextureFrameAvailable + /// + /// @param[in] texture_id The identifier of the texture to unregister. If + /// the texture has not been previously registered, + /// this call does nothing. + /// void UnregisterTexture(int64_t texture_id); - // Called once per texture update (e.g. video frame), on the platform thread. + //-------------------------------------------------------------------------- + /// @brief Used by the embedder to notify the rasterizer that the context + /// of the previously registered texture have been updated. + /// Typically, Flutter will only render a frame if there is an + /// updated layer tree. However, in cases where the layer tree + /// is static but one of the externally composited textures has + /// been updated by the embedder, the embedder needs to notify + /// the rasterizer to render a new frame. In such cases, the + /// existing layer tree may be reused with the frame re-composited + /// with all updated external textures. Unlike the calls to + /// register and unregister the texture, this call must be made + /// each time a new texture frame is available. + /// + /// @see RegisterTexture, UnregisterTexture + /// + /// @param[in] texture_id The identifier of the texture that has been + /// updated. + /// void MarkTextureFrameAvailable(int64_t texture_id); protected: diff --git a/shell/common/run_configuration.h b/shell/common/run_configuration.h index d5dfd29e0b92e..45dfed1c2d232 100644 --- a/shell/common/run_configuration.h +++ b/shell/common/run_configuration.h @@ -18,38 +18,164 @@ namespace flutter { -/// Configuration pertaining to how we will execute the Dart code. +//------------------------------------------------------------------------------ +/// @brief Specifies all the configuration required by the runtime library +/// to launch the root isolate. This object may be created on any +/// thread but must be given to the |Run| call of the |Engine| on +/// the UI thread. The configuration object is used to specify how +/// the root isolate finds its snapshots, assets, root library and +/// the "main" entrypoint. /// -/// For example: what will be the first function we execute (entrypoint). class RunConfiguration { public: + //---------------------------------------------------------------------------- + /// @brief Attempts to infer a run configuration from the settings + /// object. This tries to create a run configuration with sensible + /// defaults for the given Dart VM runtime mode. In JIT mode, this + /// will attempt to look for the VM and isolate snapshots in the + /// assets directory (must be specified in settings). In AOT mode, + /// it will attempt to look for known snapshot symbols in the + /// currently currently loaded process. The entrypoint defaults to + /// the "main" method in the root library. + /// + /// @param[in] settings The settings object used to look for the various + /// snapshots and settings. This is usually initialized + /// from command line arguments. + /// @param[in] io_worker An optional IO worker. Resolving and reading the + /// various snapshots may be slow. Providing an IO + /// worker will ensure that realization of these + /// snapshots happens on a worker thread instead of the + /// calling thread. Note that the work done to realize + /// the snapshots may occur after this call returns. If + /// is the embedder's responsibility to make sure the + /// serial worker is kept alive for the lifetime of the + /// shell associated with the engine that this run + /// configuration is given to. + /// + /// @return A run configuration. Depending on the completeness of the + /// settings, This object may potentially be invalid. + /// static RunConfiguration InferFromSettings( const Settings& settings, fml::RefPtr io_worker = nullptr); + //---------------------------------------------------------------------------- + /// @brief Creates a run configuration with only an isolate + /// configuration. There is no asset manager and default + /// entrypoint and root library are used ("main" in root library). + /// + /// @param[in] configuration The configuration + /// RunConfiguration(std::unique_ptr configuration); + //---------------------------------------------------------------------------- + /// @brief Creates a run configuration with the specified isolate + /// configuration and asset manager. The default entrypoint and + /// root library are used ("main" in root library). + /// + /// @param[in] configuration The configuration + /// @param[in] asset_manager The asset manager + /// RunConfiguration(std::unique_ptr configuration, std::shared_ptr asset_manager); - RunConfiguration(RunConfiguration&&); - + //---------------------------------------------------------------------------- + /// @brief Run configurations cannot be copied because it may not always + /// be possible to copy the underlying isolate snapshots. If + /// multiple run configurations share the same underlying + /// snapshots, creating a configuration from isolate snapshots + /// sharing the same underlying buffers is recommended. + /// + /// @param config The run configuration to move. + /// + RunConfiguration(RunConfiguration&& config); + + //---------------------------------------------------------------------------- + /// @brief There are no threading restrictions on the destruction of the + /// run configuration. + /// ~RunConfiguration(); + //---------------------------------------------------------------------------- + /// @brief A valid run configuration only guarantees that the engine + /// should be able to find the assets and the isolate snapshots + /// when it attempts to launch the root isolate. The validity of + /// the snapshot cannot be determined yet. That determination can + /// only be made when the configuration is used to run the root + /// isolate in the engine. However, the engine will always reject + /// an invalid run configuration. + /// + /// @attention A valid run configuration does not mean that the root isolate + /// will always be launched. It only indicates that the various + /// snapshots are isolate snapshots and asset managers are present + /// and accounted for. The validity of the snapshots will only be + /// checked when the engine attempts to launch the root isolate. + /// + /// @return Returns whether the snapshots and asset manager registrations + /// are valid. + /// bool IsValid() const; + //---------------------------------------------------------------------------- + /// @brief Asset managers maintain a list of resolvers that are checked + /// in order when attempting to locate an asset. This method adds + /// a resolver to the end of the list. + /// + /// @param[in] resolver The asset resolver to add to the engine of the list + /// resolvers maintained by the asset manager. + /// + /// @return Returns whether the resolver was successfully registered. The + /// resolver must be valid for its registration to be successful. + /// bool AddAssetResolver(std::unique_ptr resolver); + //---------------------------------------------------------------------------- + /// @brief Updates the main application entrypoint. If this is not set, + /// the + /// "main" method is used as the entrypoint. + /// + /// @param[in] entrypoint The entrypoint to use. void SetEntrypoint(std::string entrypoint); + //---------------------------------------------------------------------------- + /// @brief Specifies the main Dart entrypoint and the library to find + /// that entrypoint in. By default, this is the "main" method in + /// the root library. The root library may be specified by + /// entering the empty string as the second argument. + /// + /// @see SetEntrypoint() + /// + /// @param[in] entrypoint The entrypoint + /// @param[in] library The library + /// void SetEntrypointAndLibrary(std::string entrypoint, std::string library); + //---------------------------------------------------------------------------- + /// @return The asset manager referencing all previously registered asset + /// resolvers. + /// std::shared_ptr GetAssetManager() const; + //---------------------------------------------------------------------------- + /// @return The main Dart entrypoint to be used for the root isolate. + /// const std::string& GetEntrypoint() const; + //---------------------------------------------------------------------------- + /// @return The name of the library in which the main entrypoint resides. + /// If empty, the root library is used. + /// const std::string& GetEntrypointLibrary() const; + //---------------------------------------------------------------------------- + /// @brief The engine uses this to take the isolate configuration from + /// the run configuration. The run configuration is no longer + /// valid after this call is made. The non-copyable nature of some + /// of the snapshots referenced in the isolate configuration is + /// why the run configuration as a whole is not copyable. + /// + /// @return The run configuration if one is present. + /// std::unique_ptr TakeIsolateConfiguration(); private: diff --git a/shell/common/shell.h b/shell/common/shell.h index 97cbe5ef8a976..db188aab63c0d 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -34,8 +34,43 @@ namespace flutter { -/// Wraps up all the different components of Flutter engine and coordinates them -/// through a series of delegates. +//------------------------------------------------------------------------------ +/// Perhaps the single most important class in the Flutter engine repository. +/// When embedders create a Flutter application, they are referring to the +/// creation of an instance of a shell. Creation and destruction of the shell is +/// synchronous and the embedder only holds a unique pointer to the shell. The +/// shell does not create the threads its primary components run on. Instead, it +/// is the embedder's responsibility to create threads and give the shell task +/// runners for those threads. Due to deterministic destruction of the shell, +/// the embedder can terminate all threads immediately after collecting the +/// shell. The shell must be created and destroyed on the same thread, but, +/// different shells (i.e. a separate instances of a Flutter application) may be +/// run on different threads simultaneously. The task runners themselves do not +/// have to be unique. If all task runner references given to the shell during +/// shell creation point to the same task runner, the Flutter application is +/// effectively single threaded. +/// +/// The shell is the central nervous system of the Flutter application. None of +/// the shell components are thread safe and must be created, accessed and +/// destroyed on the same thread. To interact with one another, the various +/// components delegate to the shell for communication. Instead of using back +/// pointers to the shell, a delegation pattern is used by all components that +/// want to communicate with one another. Because of this, the shell implements +/// the delegate interface for all these components. +/// +/// All shell methods accessed by the embedder may only be called on the +/// platform task runner. In case the embedder wants to directly access a shell +/// subcomponent, it is the embedder's responsibility to acquire a weak pointer +/// to that component and post a task to the task runner used by the component +/// to access its methods. +/// +/// There is no explicit API to bootstrap and shutdown the Dart VM. The first +/// instance of the shell in the process bootstraps the Dart VM and the +/// destruction of the last shell instance destroys the same. Since different +/// shells may be created and destroyed on different threads. VM bootstrap may +/// happen on one thread but its collection on another. This behavior is thread +/// safe. +/// class Shell final : public PlatformView::Delegate, public Animator::Delegate, public Engine::Delegate, @@ -45,16 +80,73 @@ class Shell final : public PlatformView::Delegate, template using CreateCallback = std::function(Shell&)>; - // Create a shell with the given task runners and settings. The isolate - // snapshot will be shared with the snapshot of the service isolate. + //---------------------------------------------------------------------------- + /// @brief Creates a shell instance using the provided settings. The + /// callbacks to create the various shell subcomponents will be + /// called on the appropriate threads before this method returns. + /// If this is the first instance of a shell in the process, this + /// call also bootstraps the Dart VM. + /// + /// @param[in] task_runners The task runners + /// @param[in] settings The settings + /// @param[in] on_create_platform_view The callback that must return a + /// platform view. This will be called on + /// the platform task runner before this + /// method returns. + /// @param[in] on_create_rasterizer That callback that must provide a + /// valid rasterizer. This will be called + /// on the render task runner before this + /// method returns. + /// + /// @return A full initialized shell if the settings and callbacks are + /// valid. The root isolate has been created but not yet launched. + /// It may be launched by obtaining the engine weak pointer and + /// posting a task onto the UI task runner with a valid run + /// configuration to run the isolate. The embedder must always + /// check the validity of the shell (using the IsSetup call) + /// immediately after getting a pointer to it. + /// static std::unique_ptr Create( TaskRunners task_runners, Settings settings, CreateCallback on_create_platform_view, CreateCallback on_create_rasterizer); - // Creates a shell with the given task runners and settings. The isolate - // snapshot is specified upfront. + //---------------------------------------------------------------------------- + /// @brief Creates a shell instance using the provided settings. The + /// callbacks to create the various shell subcomponents will be + /// called on the appropriate threads before this method returns. + /// Unlike the simpler variant of this factory method, this method + /// allows for the specification of an isolate snapshot that + /// cannot be adequately described in the settings. This call also + /// requires the specification of a running VM instance. + /// + /// @param[in] task_runners The task runners + /// @param[in] settings The settings + /// @param[in] isolate_snapshot A custom isolate snapshot. Takes + /// precedence over any snapshots + /// specified in the settings. + /// @param[in] shared_snapshot A custom shared snapshot. Takes + /// precedence over any snapshots + /// specified in the settings. + /// @param[in] on_create_platform_view The callback that must return a + /// platform view. This will be called on + /// the platform task runner before this + /// method returns. + /// @param[in] on_create_rasterizer That callback that must provide a + /// valid rasterizer. This will be called + /// on the render task runner before this + /// method returns. + /// @param[in] vm A running VM instance. + /// + /// @return A full initialized shell if the settings and callbacks are + /// valid. The root isolate has been created but not yet launched. + /// It may be launched by obtaining the engine weak pointer and + /// posting a task onto the UI task runner with a valid run + /// configuration to run the isolate. The embedder must always + /// check the validity of the shell (using the IsSetup call) + /// immediately after getting a pointer to it. + /// static std::unique_ptr Create( TaskRunners task_runners, Settings settings, @@ -64,29 +156,90 @@ class Shell final : public PlatformView::Delegate, CreateCallback on_create_rasterizer, DartVMRef vm); + //---------------------------------------------------------------------------- + /// @brief Destroys the shell. This is a synchronous operation and + /// synchronous barrier blocks are introduced on the various + /// threads to ensure shutdown of all shell sub-components before + /// this method returns. + /// ~Shell(); + //------------------------------------------------------------------------------ + /// @return The settings used to launch this shell. + /// const Settings& GetSettings() const; + //------------------------------------------------------------------------------ + /// @brief If callers wish to interact directly with any shell + /// subcomponents, they must (on the platform thread) obtain a + /// task runner that the component is designed to run on and a + /// weak pointer to that component. They may then post a task to + /// that task runner, do the validity check on that task runner + /// before performing any operation on that component. This + /// accessor allows callers to access the task runners for this + /// shell. + /// + /// @return The task runners current in use by the shell. + /// const TaskRunners& GetTaskRunners() const; + //---------------------------------------------------------------------------- + /// @brief Rasterizers may only be accessed on the GPU task runner. + /// + /// @return A weak pointer to the rasterizer. + /// fml::WeakPtr GetRasterizer(); + //------------------------------------------------------------------------------ + /// @brief Engines may only be accessed on the UI thread. + /// + /// @return A weak pointer to the engine. + /// fml::WeakPtr GetEngine(); + //---------------------------------------------------------------------------- + /// @brief Platform views may only be accessed on the platform task + /// runner. + /// + /// @return A weak pointer to the platform view. + /// fml::WeakPtr GetPlatformView(); - DartVM* GetDartVM(); - // Embedders should call this under low memory conditions to free up // internal caches used. // // This method posts a task to the GPU threads to signal the Rasterizer to // free resources. + + //---------------------------------------------------------------------------- + /// @brief Used by embedders to notify that there is a low memory + /// warning. The shell will attempt to purge caches. Current, only + /// the rasterizer cache is purged. void NotifyLowMemoryWarning() const; + //---------------------------------------------------------------------------- + /// @brief Used by embedders to check if all shell subcomponents are + /// initialized. It is the embedder's responsibility to make this + /// call before accessing any other shell method. A shell that is + /// not setup must be discarded and another one created with + /// updated settings. + /// + /// @return Returns if the shell has been setup. Once set up, this does + /// not change for the life-cycle of the shell. + /// bool IsSetup() const; + //---------------------------------------------------------------------------- + /// @brief Captures a screenshot and optionally Base64 encodes the data + /// of the last layer tree rendered by the rasterizer in this + /// shell. + /// + /// @param[in] type The type of screenshot to capture. + /// @param[in] base64_encode If the screenshot data should be base64 + /// encoded. + /// + /// @return The screenshot result. + /// Rasterizer::Screenshot Screenshot(Rasterizer::ScreenshotType type, bool base64_encode); @@ -151,6 +304,8 @@ class Shell final : public PlatformView::Delegate, std::unique_ptr rasterizer, std::unique_ptr io_manager); + DartVM* GetDartVM(); + void ReportTimings(); // |PlatformView::Delegate|