зеркало из https://github.com/mozilla/gecko-dev.git
151 строка
8.2 KiB
Markdown
151 строка
8.2 KiB
Markdown
# Original Design
|
|
|
|
To understand the current design for clipping and positioning (transformations
|
|
and scrolling) in WebRender it can be useful to have a little background about
|
|
the original design for these features. The most important thing to remember is
|
|
that originally clipping, scrolling regions, and transformations were
|
|
properties of stacking contexts and they were completely _hierarchical_. This
|
|
goes a long way toward representing the majority of CSS content on the web, but
|
|
fails when dealing with important edges cases and features including:
|
|
1. Support for sticky positioned content
|
|
2. Scrolling areas that include content that is ordered both above and below
|
|
intersecting content from outside the scroll area.
|
|
3. Items in the same scrolling root, clipped by different clips one or more of
|
|
which are defined outside the scrolling root itself.
|
|
4. Completely non-hierarchical clipping situations, such as when items are
|
|
clipped by some clips in the hierarchy, but not others.
|
|
|
|
Design changes have been a step by step path from the original design to one
|
|
that can handle all CSS content.
|
|
|
|
# Current Design
|
|
|
|
All positioning and clipping is handled by the `SpatialTree`. The name is a
|
|
holdover from when this tree was a tree of `Layers` which handled both
|
|
positioning and clipping. Currently the `SpatialTree` holds:
|
|
1. A hierarchical collection of `SpatialNodes`, with the final screen
|
|
transformation of each node depending on the relative transformation of the
|
|
node combined with the transformations of all of its ancestors. These nodes
|
|
are responsible for positioning display list items and clips.
|
|
2. A collection of `ClipNodes` which specify a rectangular clip and, optionally,
|
|
a set of rounded rectangle clips and a masking image.
|
|
3. A collection of `ClipChains`. Each `ClipChain` is a list of `ClipNode`
|
|
elements. Every display list item has an assigned `ClipChain` which
|
|
specifies what `ClipNodes` are applied to that item.
|
|
|
|
The `SpatialNode` of each clip applied to an item is completely independent of
|
|
the `SpatialNode` applied to the item itself.
|
|
|
|
One holdover from the previous design is that both `ClipNode` and `SpatialNodes`
|
|
have a parent node, which is either a `SpatialNode` or a `ClipNode`. From this
|
|
node WebRender can determine both a parent `ClipNode` and a parent `SpatialNode`
|
|
by finding the first ancestor of that type. This is handled by the
|
|
`DisplayListFlattener`.
|
|
|
|
## `SpatialNode`
|
|
There are three types of `SpatialNodes`:
|
|
1. Reference frames which are created when content needs to apply
|
|
transformation or perspective properties to display list items. Reference
|
|
frames establish a new coordinate system, so internally all coordinates on
|
|
display list items are relative to the reference frame origin. Later
|
|
any non-reference frame positioning nodes that display list items belong
|
|
to can adjust this position relative to the reference frame origin.
|
|
2. Scrolling nodes are used to define scrolling areas. These nodes have scroll
|
|
offsets which are a 2D translation relative to ancestor nodes and, ultimately,
|
|
the reference frame origin.
|
|
3. Sticky frames are responsible for implementing position:sticky behavior.
|
|
This is also an 2D translation.
|
|
|
|
`SpatialNodes` are defined as items in the display list. After scene building
|
|
each node is traversed hierarchically during the `SpatialTree::update()` step.
|
|
Once reference frame transforms and relative offsets are calculated, a to screen
|
|
space transformation can be calculated for each `SpatialNode`. This transformation
|
|
is added the `TransformPalette` and becomes directly available to WebRender shaders.
|
|
|
|
In addition to screen space transformation calculation, the `SpatialNode` tree
|
|
is divided up into _compatible coordinate systems_. These are coordinate systems
|
|
which differ only by 2D translations from their parent system. These compatible
|
|
coordinate systems may even cross reference frame boundaries. The goal here is
|
|
to allow the application clipping rectangles from different compatible
|
|
coordinate systems without generating mask images.
|
|
|
|
## `ClipNode`
|
|
|
|
Each clip node holds a clip rectangle along with an optional collection of
|
|
rounded clip rectangles and a mask image. The fact that `ClipNodes` all have a
|
|
clip rectangle is important because it means that all content clipped by a
|
|
clip node has a bounding rectangle, which can be converted into a bounding
|
|
screen space rectangle. This rectangle is called the _outer rectangle_ of the
|
|
clip. `ClipNodes` may also have an _inner rectangle_, which is an area within
|
|
the boundaries of the _outer rectangle_ that is completely unclipped.
|
|
|
|
These rectangles are calculated during the `SpatialTree::update()` phase. In
|
|
addition, each `ClipNode` produces a template `ClipChainNode` used to build
|
|
the `ClipChains` which use that node.
|
|
|
|
## `ClipChains`
|
|
|
|
There are two ways that `ClipChains` are defined in WebRender. The first is
|
|
through using the API for manually specifying `ClipChains` via a parent
|
|
`ClipChain` and a list of `ClipNodes`. The second is through the hierarchy of a
|
|
`ClipNode` established by its parent node. Every `ClipNode` has a chain of
|
|
ancestor `SpatialNodes` and `ClipNodes`. The creation of a `ClipNode`
|
|
automatically defines a `ClipChain` for this hierarchy. This behavior is a
|
|
compatibility feature with the old completely hierarchical clipping architecture
|
|
and is still how Gecko and Servo create most of their `ClipChains`. These
|
|
hierarchical `ClipChains` are constructed during the `ClipNode::update()` step.
|
|
|
|
During `ClipChain` construction, WebRender tries to eliminate clips that will
|
|
not affect rendering, by looking at the combined _outer rectangle_ and _inner
|
|
rectangle_ of a `ClipChain` and the _outer rectangle_ and _inner rectangle_ of
|
|
any `ClipNode` appended to the chain. An example of the goal of this process is
|
|
to avoid having to render a mask for a large rounded rectangle when the rest of
|
|
the clip chain constrains the content to an area completely inside that
|
|
rectangle. Avoiding mask rasterization in this case and others has large
|
|
performance impacts on WebRender.
|
|
|
|
# Clipping and Positioning in the Display List
|
|
|
|
Each non-structural WebRender display list item has
|
|
* A `SpatialId` of a `SpatialNode` for positioning
|
|
* A `ClipId` of a `ClipNode` or a `ClipChain` for clipping
|
|
* An item-specific rectangular clip rectangle
|
|
|
|
The positioning node determines how that item is positioned. It's assumed that
|
|
the positioning node and the item are children of the same reference frame. The
|
|
clipping node determines how that item is clipped. This should be fully
|
|
independent of how the node is positioned and items can be clipped by any
|
|
`ClipChain` regardless of the reference frame of their member clips. Finally,
|
|
the item-specific clipping rectangle is applied directly to the item and should
|
|
never result in the creation of a clip mask itself.
|
|
|
|
## Converting user-exposed `ClipId`/`SpatialId` to internal indices
|
|
|
|
WebRender must access `ClipNodes` and `SpatialNodes` quite a bit when building
|
|
scenes and frames, so it tries to convert `ClipId`/`SpatialId`, which are already
|
|
per-pipeline indices, to global scene-wide indices. Internally this is a
|
|
conversion from `ClipId` into `ClipNodeIndex` or `ClipChainIndex`, and from
|
|
`SpatialId` into `SpatialNodeIndex`. In order to make this conversion cheaper, the
|
|
`DisplayListFlattner` assigns offsets for each pipeline and node type in the
|
|
scene-wide `SpatialTree`.
|
|
|
|
Nodes are added to their respective arrays sequentially as the display list is
|
|
processed during scene building. When encountering an iframe, the
|
|
`DisplayListFlattener` must start processing the nodes for that iframe's
|
|
pipeline, meaning that nodes are now being added out of order to the node arrays
|
|
of the `SpatialTree`. In this case, the `SpatialTree` fills in the gaps in
|
|
the node arrays with placeholder nodes.
|
|
|
|
# Hit Testing
|
|
|
|
Hit testing is the responsibility of the `HitTester` data structure. This
|
|
structure copies information necessary for hit testing from the
|
|
`SpatialTree`. This is done so that hit testing can still take place while a
|
|
new `SpatialTree` is under construction.
|
|
|
|
# Ideas for the Future
|
|
1. Expose the difference between `ClipId` and `ClipChainId` in the API.
|
|
2. Prevent having to duplicate the `SpatialTree` for hit testing.
|
|
3. Avoid having to create placeholder nodes in the `SpatialTree` while
|
|
processing iframes.
|