1,295
edits
(→Roc) |
|||
| Line 45: | Line 45: | ||
== Roc == | == Roc == | ||
=== Display list processing === | |||
Taking a step back, let me write down how I think layout and display lists could/should build and maintain a layer tree. | |||
Currently when we paint we only build a display list for the items that intersect the bounding rectangle of the invalid area. Instead let's assume we have to build a display list for the entire visible window area. This is not likely to be a performance problem. This does not mean we actually have to repaint the entire visible window every time, it just means we figure out what would be painted. | |||
Then traversing the display list lets us build a new layer tree. The trick is that we want to identify layers that can be reused, or partially reused, from the previous tree and incorporated into the new tree. (Perhaps we construct the new tree from the old tree by imperative mutation, or perhaps we construct the new tree from scratch in a functional manner.) | |||
There are three kinds of layers: | |||
1) Explicit container layers that we create because we need to take advantage of hardware acceleration. Example: element with transform, element with opacity. | |||
2) Explicit leaf layers that we create because we need to take advantage of hardware acceleration. Example: video YUV layer, image element with transform or opacity. | |||
3) Implicit "in between" layers that represent all the content rendered between two explicit layers, or before the first explicit layer of a container, or after the last explicit layer of a container. These are always leaves. All cairo-rendered content is in these layers. | |||
Observation: An explicit layer is always associated with a single frame. (Although a single frame may render more than just its layer, e.g. an nsVideoFrame can have background, border and outline display items as well as the video YUV layer.) We can record in such a frame its explicit layer(s), if any. I think the maximum number of explicit layers a frame might need is 2: one "intrinsic" layer, e.g. the YUV layer for video, and one "extrinsic" layer, e.g. the layer induced by transforms or opacity. For example an nsVideoFrame with borders and a 3D transform would need two layers. I think each frame can have 0 or 1 explicit container layers and 0 or 1 explicit leaf layers. Any kind of frame can have an explicit container layer, but only certain types of frames have explicit leaf layers. | |||
Observation: in-between layers can span arbitrary chunks of the frame tree. Worse, different parts of the same frame can be at different z-levels, and since an in-between layer represents a run of z-contiguous display items, different display items of the same frame can be in different in-between layers, and a single in-between layer can contain display items for frames very far apart in the frame tree. HOWEVER, every in-between layer is either the layer before some explicit layer, or the last layer of some container. | |||
The display list system actually builds a tree of lists. A display item corresponding to an explicit container layer will have a child list with the container contents. If we treat the outermost list for the window as the children of a root container layer, then our job is, given a display list representing the children of a container layer, determine the child layers: | |||
* For any items in the list that represent the explicit container layer for a frame, that layer will be a child layer. We can retrieve this layer if it's cached in the frame. But we need to recursively descend into these display items to determine their child layers. | |||
* For any items in the list that represent the explicit leaf layer for a frame, that layer will be a child layer. We can retrieve this layer if it's cached in the frame. | |||
* For each run of consecutive items in the list that are not an explicit container or leaf layer, we have one implicit layer. It seems painful to attach this layer to any frame or another layer. However, we can keep a list of them in their container layer. | |||
** An item in the display list might not have an explicit layer, but might contain a child list containing an item with an explicit layer, etc. This is a problem. We basically need to disable explicit layers for children in this situation, OR guarantee this never happens, by ensuring that all our display items that contain their own display lists can be implemented as explicit layers. For example, consider an element with an SVG filter applied that contains an element with a 3D transform. If we're going to accelerate the 3D transform and do it off the main thread, we need also need the compositing-thread to apply the SVG filter (preferably accelerated), so SVG filters need to be supported by the layer API. What needs to be supported: | |||
*** Rectangle clipping (easy) | |||
*** Opacity (easy) | |||
*** Transforms (easy) | |||
*** SVG filters (hard) | |||
*** SVG path clipping (unsure, probably easy if we're willing to rasterize the path ahead of time) | |||
*** SVG mask (easy) | |||
*** SVG foreignObject inside arbitrary SVG gunk ... hmm. Maybe if we support all the above, *and* render SVG using display lists (which we currently don't), that would be enough. | |||
The hard remaining problem is to know how to reuse or update the implicit layers belonging to a container. The content rendered in those layers may have been invalidated. We also need to figure out how to relate the N implicit layers currently associated with the container to the M new implicit layers. For example, if an explicit layer has gone away then M might be less than N, the implicit layers before and after it have "merged". The good news is that any content in the implicit layers that changes will cause the frame for the container to receive an nsIFrame::InvalidateInternal, so we can know the region of the implicit layers that's changed, although it will be hard to know which implicit layers changed ... we can probably just repaint that region of all of them. Proposal: | |||
* Match the explicit layer children that were already children of the container to the explicit layer children in the new display list. They have to be in the same order before and after. (In general there are multiple solutions to this problem. E.g. if we had explicit layers A, B, C before and now we have B, A, C, we can select A, C as matching, or B, C as matching.) | |||
* Divide up the display list into the runs of consecutive child display items that were not matched in the previous step. For each such run, let's say between explicit layers L1 and L2: | |||
** Reuse the old implicit layer children between L1 and L2 for the new implicit layers in this run. It won't matter how they're reassigned as long as they're kept in the same order. Some new layers might need to be created and leftovers destroyed. | |||
** Union together the areas covered by explicit layers that were in the container layer between L1 and L2 (exclusive) (these are the old layers that have been removed from this run) | |||
** Add the areas covered by explicit layers that are in the display list between L1 and L2 (exclusive) (these are the new layers that have appeared in this run) | |||
** Add the areas invalidated by InvalidateInternal as observed by this container layer's frame | |||
** Repaint the resulting area in *every* implicit layer in the run. We're done! | |||
** The common case should be that the explicit layer children currently in the container are exactly the same list in the same order as the explicit layer children in the new display list, in which case the above steps will find no explicit layer children between L1 and L2 and there will be exactly one implicit layer child in each run. | |||
For this to work well we need to be able to distinguish invalidates that need to update implicit layers of a frame with a container layer from other invalidates of that frame. We can do this using an extra invalidation flag, say INVALIDATE_IMPLICIT_LAYER. When a frame with a container layer gets an invalidate from a child with that flag, it adds the invalid area to the region of its implicit layers that needs to be updated, and removes the flag before passing the invalidate upwards. | |||
Another thing we'll need for this to work well is to be able to control the size of the implicit layers. Each time we update an implicit layer we can calculate a new bounding-box and resize the implicit layer to that size. Often the size will be 0, but for simplicity it will probably be easier to manipulate zero-size layers than try to eliminate them at the display list level. | |||
=== Old proposal === | |||
I may need to revise this quite a bit based on the previous section. | |||
// Reference counted, safe-for-cross-thread-use layer class. | // Reference counted, safe-for-cross-thread-use layer class. | ||
edits