(Update and) Integrate Rendering README into code (#10389)
* (Update and) Integrate Rendering README into code * Add mising whitespace * Review comments and other touch-ups
This commit is contained in:
parent
0e97637299
commit
498cfc67c3
@ -1,112 +0,0 @@
|
||||
Flutter Rendering Layer
|
||||
=======================
|
||||
|
||||
This document is intended to describe some of the core designs of the
|
||||
Flutter rendering layer.
|
||||
|
||||
Layout
|
||||
------
|
||||
|
||||
Paint
|
||||
-----
|
||||
|
||||
Compositing
|
||||
-----------
|
||||
|
||||
Semantics
|
||||
---------
|
||||
|
||||
The last phase of a frame is the Semantics phase. This only occurs if
|
||||
a semantics server has been installed, for example if the user is
|
||||
using an accessibility tool.
|
||||
|
||||
Each frame, the semantics phase starts with a call to the
|
||||
`PipelineOwner.flushSemantics()` method from the `Renderer` binding's
|
||||
`beginFrame()` method.
|
||||
|
||||
Each node marked as needing semantics (which initially is just the
|
||||
root node, as scheduled by `scheduleInitialSemantics()`), in depth
|
||||
order, has its semantics updated by calling `_updateSemantics()`.
|
||||
|
||||
The `_updateSemantics()` method calls `_getSemantics()` to obtain an
|
||||
`_InterestingSemanticsFragment`, and then calls `compile()` on that
|
||||
fragment to obtain a `SemanticsNode` which becomes the value of the
|
||||
`RenderObject`'s `_semantics` field. **This is essentially a two-pass
|
||||
walk of the render tree. The first pass determines the shape of the
|
||||
output tree, and the second creates the nodes of this tree and hooks
|
||||
them together.** The second walk is a sparse walk; it only walks the
|
||||
nodes that are interesting for the purpose of semantics.
|
||||
|
||||
`_getSemantics()` is the core function that walks the render tree to
|
||||
obtain the semantics. It collects semantic annotators for this
|
||||
`RenderObject`, then walks its children collecting
|
||||
`_SemanticsFragment`s for them, and then returns an appropriate
|
||||
`_SemanticsFragment` object that describes the `RenderObject`'s
|
||||
semantics.
|
||||
|
||||
Semantic annotators are functions that, given a `SemanticsNode`, set
|
||||
some flags or strings on the object. They are obtained from
|
||||
`getSemanticsAnnotators()`. For example, here is how `RenderParagraph`
|
||||
annotates the `SemanticsNode` with its text:
|
||||
|
||||
```dart
|
||||
Iterable<SemanticsAnnotator> getSemanticsAnnotators() sync* {
|
||||
yield (SemanticsNode node) {
|
||||
node.label = text.toPlainText();
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
A `_SemanticsFragment` object is a node in a short-lived tree which is
|
||||
used to create the final `SemanticsNode` tree that is sent to the
|
||||
semantics server. These objects have a list of semantic annotators,
|
||||
and a list of `_SemanticsFragment` children.
|
||||
|
||||
There are several `_SemanticsFragment` classes. The `_getSemantics()`
|
||||
method picks its return value as follows:
|
||||
|
||||
* `_CleanSemanticsFragment` is used to represent a `RenderObject` that
|
||||
has a `SemanticsNode` and which is in no way dirty. This class has
|
||||
no children and no annotators, and when compiled, it returns the
|
||||
`SemanticsNode` that the `RenderObject` already has.
|
||||
|
||||
* `_RootSemanticsFragment`* is used to represent the `RenderObject`
|
||||
found at the top of the render tree. This class always compiles to a
|
||||
`SemanticsNode` with ID 0.
|
||||
|
||||
* `_ConcreteSemanticsFragment`* is used to represent a `RenderObject`
|
||||
that has `hasSemantics` set to true. It returns the `SemanticsNode`
|
||||
for that `RenderObject`.
|
||||
|
||||
* `_ImplicitSemanticsFragment`* is used to represent a `RenderObject`
|
||||
that does not have `hasSemantics` set to true, but which does have
|
||||
some semantic annotators. When it is compiled, if the nearest
|
||||
ancestor `_SemanticsFragment` that isn't also an
|
||||
`_ImplicitSemanticsFragment` is a `_RootSemanticsFragment` or a
|
||||
`_ConcreteSemanticsFragment`, then the `SemanticsNode` from that
|
||||
object is reused. Otherwise, a new one is created.
|
||||
|
||||
* `_ForkingSemanticsFragment` is used to represent a `RenderObject`
|
||||
that introduces no semantics of its own, but which has two or more
|
||||
descendants that do introduce semantics (and which are not ancestors
|
||||
or descendants of each other).
|
||||
|
||||
* For `RenderObject` nodes that introduce no semantics but which have
|
||||
a (single) child that does, the `_SemanticsFragment` of the child is
|
||||
returned.
|
||||
|
||||
* For `RenderObject` nodes that introduce no semantics and have no
|
||||
descendants that introduce semantics, `null` is returned.
|
||||
|
||||
The classes marked with an asterisk * above are the
|
||||
`_InterestingSemanticsFragment` classes.
|
||||
|
||||
When the `_SemanticsFragment` tree is then compiled, the
|
||||
`SemanticsNode` objects are created (if necessary), the semantic
|
||||
annotators are run on each `SemanticsNode`, the geometry (matrix,
|
||||
size, and clip) is applied, and the children are updated.
|
||||
|
||||
As part of this, the code clears out the `_semantics` field of any
|
||||
`RenderObject` that previously had a `SemanticsNode` but no longer
|
||||
does. This is done as part of the first walk where possible, and as
|
||||
part of the second otherwise.
|
@ -652,6 +652,12 @@ class _SemanticsGeometry {
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the shape of the semantic tree.
|
||||
///
|
||||
/// A [_SemanticsFragment] object is a node in a short-lived tree which is used
|
||||
/// to create the final [SemanticsNode] tree that is sent to the semantics
|
||||
/// server. These objects have a [SemanticsAnnotator], and a list of
|
||||
/// [_SemanticsFragment] children.
|
||||
abstract class _SemanticsFragment {
|
||||
_SemanticsFragment({
|
||||
@required RenderObject renderObjectOwner,
|
||||
@ -689,9 +695,11 @@ abstract class _SemanticsFragment {
|
||||
String toString() => '$runtimeType#$hashCode';
|
||||
}
|
||||
|
||||
/// Represents a subtree that doesn't need updating, it already has a
|
||||
/// SemanticsNode and isn't dirty. (We still update the matrix, since
|
||||
/// that comes from the (dirty) ancestors.)
|
||||
/// Represents a [RenderObject] which is in no way dirty.
|
||||
///
|
||||
/// This class has no children and no annotators, and when compiled, it returns
|
||||
/// the [SemanticsNode] that the [RenderObject] already has. (We still update
|
||||
/// the matrix, since that comes from the (dirty) ancestors.)
|
||||
class _CleanSemanticsFragment extends _SemanticsFragment {
|
||||
_CleanSemanticsFragment({
|
||||
@required RenderObject renderObjectOwner
|
||||
@ -750,6 +758,9 @@ abstract class _InterestingSemanticsFragment extends _SemanticsFragment {
|
||||
_SemanticsGeometry createSemanticsGeometryForChild(_SemanticsGeometry geometry);
|
||||
}
|
||||
|
||||
/// Represents the [RenderObject] found at the top of the render tree.
|
||||
///
|
||||
/// This class always compiles to a [SemanticsNode] with ID 0.
|
||||
class _RootSemanticsFragment extends _InterestingSemanticsFragment {
|
||||
_RootSemanticsFragment({
|
||||
RenderObject renderObjectOwner,
|
||||
@ -780,6 +791,9 @@ class _RootSemanticsFragment extends _InterestingSemanticsFragment {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a RenderObject that has [isSemanticBoundary] set to `true`.
|
||||
///
|
||||
/// It returns the SemanticsNode for that [RenderObject].
|
||||
class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment {
|
||||
_ConcreteSemanticsFragment({
|
||||
RenderObject renderObjectOwner,
|
||||
@ -808,6 +822,13 @@ class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a RenderObject that does not have [isSemanticBoundary] set to
|
||||
/// `true`, but which does have some semantic annotators.
|
||||
///
|
||||
/// When it is compiled, if the nearest ancestor [_SemanticsFragment] that isn't
|
||||
/// also an [_ImplicitSemanticsFragment] is a [_RootSemanticsFragment] or a
|
||||
/// [_ConcreteSemanticsFragment], then the [SemanticsNode] from that object is
|
||||
/// reused. Otherwise, a new one is created.
|
||||
class _ImplicitSemanticsFragment extends _InterestingSemanticsFragment {
|
||||
_ImplicitSemanticsFragment({
|
||||
RenderObject renderObjectOwner,
|
||||
@ -851,6 +872,9 @@ class _ImplicitSemanticsFragment extends _InterestingSemanticsFragment {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a [RenderObject] that introduces no semantics of its own, but
|
||||
/// which has two or more descendants that do introduce semantics
|
||||
/// (and which are not ancestors or descendants of each other).
|
||||
class _ForkingSemanticsFragment extends _SemanticsFragment {
|
||||
_ForkingSemanticsFragment({
|
||||
RenderObject renderObjectOwner,
|
||||
@ -1180,7 +1204,11 @@ class PipelineOwner {
|
||||
bool _debugDoingSemantics = false;
|
||||
final List<RenderObject> _nodesNeedingSemantics = <RenderObject>[];
|
||||
|
||||
/// Update the semantics for all render objects.
|
||||
/// Update the semantics for render objects marked as needing a semantics
|
||||
/// update.
|
||||
///
|
||||
/// Initially, only the root node, as scheduled by [scheduleInitialSemantics],
|
||||
/// needs a semantics update.
|
||||
///
|
||||
/// This function is one of the core stages of the rendering pipeline. The
|
||||
/// semantics are compiled after painting and only after
|
||||
@ -2486,6 +2514,13 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the semantic information of the render object.
|
||||
///
|
||||
/// This is essentially a two-pass walk of the render tree. The first pass
|
||||
/// determines the shape of the output tree (captured in
|
||||
/// [_SemanticsFragment]s), and the second creates the nodes of this tree and
|
||||
/// hooks them together. The second walk is a sparse walk; it only walks the
|
||||
/// nodes that are interesting for the purpose of semantics.
|
||||
void _updateSemantics() {
|
||||
try {
|
||||
assert(_needsSemanticsUpdate);
|
||||
@ -2500,6 +2535,12 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Core function that walks the render tree to obtain the semantics.
|
||||
///
|
||||
/// It collects semantic annotators for this RenderObject, then walks its
|
||||
/// children collecting [_SemanticsFragments] for them, and then returns an
|
||||
/// appropriate [_SemanticsFragment] object that describes the RenderObject's
|
||||
/// semantics.
|
||||
_SemanticsFragment _getSemanticsFragment() {
|
||||
// early-exit if we're not dirty and have our own semantics
|
||||
if (!_needsSemanticsUpdate && isSemanticBoundary) {
|
||||
@ -2533,17 +2574,21 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
||||
if (annotator != null)
|
||||
return new _ImplicitSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children);
|
||||
_semantics = null;
|
||||
if (children == null)
|
||||
if (children == null) {
|
||||
// Introduces no semantics and has no descendants that introduce semantics.
|
||||
return null;
|
||||
}
|
||||
if (children.length > 1)
|
||||
return new _ForkingSemanticsFragment(renderObjectOwner: this, children: children);
|
||||
assert(children.length == 1);
|
||||
return children.single;
|
||||
}
|
||||
|
||||
/// Called when collecting the semantics of this node. Subclasses
|
||||
/// that have children that are not semantically relevant (e.g.
|
||||
/// because they are invisible) should skip those children here.
|
||||
/// Called when collecting the semantics of this node.
|
||||
///
|
||||
/// The implementation has to return the children in paint order skipping all
|
||||
/// children that are not semantically relevant (e.g. because they are
|
||||
/// invisible).
|
||||
///
|
||||
/// The default implementation mirrors the behavior of
|
||||
/// [visitChildren()] (which is supposed to walk all the children).
|
||||
|
Loading…
x
Reference in New Issue
Block a user