ATTENTION: This document describes the behavior of Scenic's GFX API which is currently being replaced by the Flatland API. Workstation currently uses Flatland only, and Smart Display will be converted to use Flatland as well. If working with Flatland, please refer to the Flatland documentation.
This document describes the lifecycle of the basic resources that a client adds to and removes from the scene graph. It focuses on resources that are attached as Nodes and Views, and is a reference for Scenic's internal handling of these resources.
Many Resources follow the Node lifecycle: Resources are created and added to the ResourceMap. Resources are kept alive by reference counting. As such, inter-scene graph references will keep resources in the scene graph alive, even if the client calls ReleaseResource
. The release command removes the Resource from the client‘s internally-managed ResourceMap
, which means that the client cannot apply any future commands to that Resource. This document highlights how the scene graph can keep Resources added to it alive, with the key exception of the View resource -- which is solely held onto via the client’s ResourceMap.
This follows a simple embedded-embeddee client pair, and assumes the clients have set up all necessary resources to add Nodes and Views to the global, retained scene graph.
Note: the code shown below is typically handled by the UI C++ SDK wrappers. All code examples show FIDL commands, as they are seen by Scenic.
Say the embedder client (Client A) has created a root Scene node in its Session, accessed via root_id
. Client A can create children and add that to the root node via the following commands:
CreateScene(root_id); CreateEntityNode(entity_node_id); AddChild(root_id, entity_node_id);
Internally, this creates a Node, adds it to the Session's ResourceMap, and sets the EntityNode as the child of the root node in the scene graph:
Client A can apply commands to the EntityNode as long as it maintains a reference to it in the ResourceMap. For example:
SetTranslation(entity_node_id, {0, h/2, 0}); CreateShapeNode(shape_node_id); CreateShape(shape_node_id, triangle); AddChild(entity_node_id, shape_node_id);
Releasing the Resource releases it from the ResourceMap. It does not release it from the Scene graph, due to a strong reference from the parent. Client A can release the Resources backing the “triangle dialog”, and it will still remain on the screen:
ReleaseResource(entity_node_id);
To remove the triangle from the screen, the client would have to explicitly detach the nodes from the scene graph. When the Resource
is removed from both the ResourceMap and from the scene graph, the resource is destroyed.
DetachChildren(root_id);
To embed a View from another Session, Client A must make a ViewHolder
Resource, and add it as a child of a node to add it to the scene graph.
CreateEntityNode(entity_node_id); AddChild(root_id, entity_node_id); CreateViewHolder(view_holder_id, view_holder_token); AddChild(entity_node_id, view_holder_id);
The ViewHolder follows the same lifecycle rules as a Node, described above. It will remain part of the scene graph as long as it is connected to something in the scene graph. However, the client cannot add children to the ViewHolder: instead, its corresponding View is linked by Scenic.
The view_token
from the ViewHolder/View token pair is passed to the embedded Session (Client B). When Client B creates a View from that token, a View
is created and added to the client‘s ResourceMap. Scenic creates links between the View to the ViewHolder to establish this cross-Session connection. The View then creates a “phantom ViewNode
”, and sets that as the child of the ViewHolder. The ViewNode represents Client B’s root node in the scene graph.
CreateView(view_id, view_token);
Client B can then add children to the View, just like it can to Nodes. Under the hood, the ViewNode maintains the children's connections to the scene graph:
CreateShapeNode(shape_node_id); CreateShape(shape_node_id, rectangle); AddChild(view_id, shape_node_id);
A View is a viable Resource and added to the scene as long as it is in the client's ResourceMap. It differs from a traditional Node because the scene graph does not maintain a strong reference to the View Resource. To perform removal of a View, Client B must command Scenic to release the View resource. Unlike a node, the client does not have to command Scenic to detach it from the scene graph. Releasing the View Resource destroys the View and its phantom ViewNode, and detaches the View and its subtree from the global scene graph:
ReleaseResource(view_id);
Note: if either the View or ViewHolder is destroyed, its pair is delivered a disconnected event (i.e.
fuchsia.ui.gfx.ViewHolderDisconnected
orfuchsia.ui.gfx.ViewDisconnected
, respectively).
A ViewHolder is treated as another child node in a Session, and so follows the same lifecycle rules. If a ViewHolder is released as a Resource, and detached from the scene graph, the ViewHolder is destroyed.
Say that Client B has not released its View. When the ViewHolder is destroyed, this breaks any link to the View, and destroys the strong reference to the child ViewNode. The embedded View is thus detached from the scene. The embedded Session and embedded View's subtree may still be intact, though no longer visible.
Detach(view_holder_id); ReleaseResource(view_holder_id);
Note: Any embedded Sessions are notified if they are detached from the scene via the
fuchsia.ui.gfx.ViewDetachedFromSceneEvent
.