(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 {
|
abstract class _SemanticsFragment {
|
||||||
_SemanticsFragment({
|
_SemanticsFragment({
|
||||||
@required RenderObject renderObjectOwner,
|
@required RenderObject renderObjectOwner,
|
||||||
@ -689,9 +695,11 @@ abstract class _SemanticsFragment {
|
|||||||
String toString() => '$runtimeType#$hashCode';
|
String toString() => '$runtimeType#$hashCode';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a subtree that doesn't need updating, it already has a
|
/// Represents a [RenderObject] which is in no way dirty.
|
||||||
/// SemanticsNode and isn't dirty. (We still update the matrix, since
|
///
|
||||||
/// that comes from the (dirty) ancestors.)
|
/// 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 {
|
class _CleanSemanticsFragment extends _SemanticsFragment {
|
||||||
_CleanSemanticsFragment({
|
_CleanSemanticsFragment({
|
||||||
@required RenderObject renderObjectOwner
|
@required RenderObject renderObjectOwner
|
||||||
@ -750,6 +758,9 @@ abstract class _InterestingSemanticsFragment extends _SemanticsFragment {
|
|||||||
_SemanticsGeometry createSemanticsGeometryForChild(_SemanticsGeometry geometry);
|
_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 {
|
class _RootSemanticsFragment extends _InterestingSemanticsFragment {
|
||||||
_RootSemanticsFragment({
|
_RootSemanticsFragment({
|
||||||
RenderObject renderObjectOwner,
|
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 {
|
class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment {
|
||||||
_ConcreteSemanticsFragment({
|
_ConcreteSemanticsFragment({
|
||||||
RenderObject renderObjectOwner,
|
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 {
|
class _ImplicitSemanticsFragment extends _InterestingSemanticsFragment {
|
||||||
_ImplicitSemanticsFragment({
|
_ImplicitSemanticsFragment({
|
||||||
RenderObject renderObjectOwner,
|
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 {
|
class _ForkingSemanticsFragment extends _SemanticsFragment {
|
||||||
_ForkingSemanticsFragment({
|
_ForkingSemanticsFragment({
|
||||||
RenderObject renderObjectOwner,
|
RenderObject renderObjectOwner,
|
||||||
@ -1180,7 +1204,11 @@ class PipelineOwner {
|
|||||||
bool _debugDoingSemantics = false;
|
bool _debugDoingSemantics = false;
|
||||||
final List<RenderObject> _nodesNeedingSemantics = <RenderObject>[];
|
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
|
/// This function is one of the core stages of the rendering pipeline. The
|
||||||
/// semantics are compiled after painting and only after
|
/// 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() {
|
void _updateSemantics() {
|
||||||
try {
|
try {
|
||||||
assert(_needsSemanticsUpdate);
|
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() {
|
_SemanticsFragment _getSemanticsFragment() {
|
||||||
// early-exit if we're not dirty and have our own semantics
|
// early-exit if we're not dirty and have our own semantics
|
||||||
if (!_needsSemanticsUpdate && isSemanticBoundary) {
|
if (!_needsSemanticsUpdate && isSemanticBoundary) {
|
||||||
@ -2533,17 +2574,21 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
|||||||
if (annotator != null)
|
if (annotator != null)
|
||||||
return new _ImplicitSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children);
|
return new _ImplicitSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children);
|
||||||
_semantics = null;
|
_semantics = null;
|
||||||
if (children == null)
|
if (children == null) {
|
||||||
|
// Introduces no semantics and has no descendants that introduce semantics.
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
if (children.length > 1)
|
if (children.length > 1)
|
||||||
return new _ForkingSemanticsFragment(renderObjectOwner: this, children: children);
|
return new _ForkingSemanticsFragment(renderObjectOwner: this, children: children);
|
||||||
assert(children.length == 1);
|
assert(children.length == 1);
|
||||||
return children.single;
|
return children.single;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when collecting the semantics of this node. Subclasses
|
/// Called when collecting the semantics of this node.
|
||||||
/// that have children that are not semantically relevant (e.g.
|
///
|
||||||
/// because they are invisible) should skip those children here.
|
/// 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
|
/// The default implementation mirrors the behavior of
|
||||||
/// [visitChildren()] (which is supposed to walk all the children).
|
/// [visitChildren()] (which is supposed to walk all the children).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user