vello_common: Generalize the command recorder abstraction and add more metadata#1746
Conversation
e868164 to
ba64412
Compare
86cb568 to
d10a407
Compare
0cf5501 to
2ee9cff
Compare
d10a407 to
3f61c40
Compare
2ee9cff to
36e4384
Compare
grebmeg
left a comment
There was a problem hiding this comment.
LGTM! I especially like the Node abstraction — it makes draw and layer ordering much clearer.
| /// Tile-aligned dimensions of the root scene. | ||
| pub scene_size: SizeU16, | ||
| /// The nodes of the root layer. | ||
| pub nodes: Vec<Node>, |
There was a problem hiding this comment.
Have you considered representing the root as a separate layer containing its own nodes? That would make the recording a more uniform tree-like structure, with the root as the tree root and all other layers as children. It might simplify traversal and remove some root-specific handling, although the root’s distinct rendering semantics may outweigh that benefit.
There was a problem hiding this comment.
Yeah, in Vello Hybrid, the root requires some specific handling (because of blending). In my rewrite branch, the special casing hasn't really been a problem, so I think it's better to keep it!
| pub root_cmds: Vec<RecordedCmd>, | ||
| /// Recorder for a scene description. | ||
| #[derive(Debug)] | ||
| pub struct CommandRecorder<D> { |
There was a problem hiding this comment.
Would SceneRecorder be a more accurate name now? CommandRecorder suggests a flat command stream, but this type records a structured scene containing root nodes, nested layers, draws, bounds, and rendering metadata. The existing doc comment already describes it as a "recorder for a scene description".
There was a problem hiding this comment.
Yes, as mentioned, I think we could even just name it SceneGraph! However, I don't want to load this PR more than necessary. I will add a TODO!
| self.bucket_commands( | ||
| &layer.nodes, | ||
| draws, | ||
| layers, | ||
| strips, | ||
| encoded_paints, | ||
| filter_ctx, | ||
| ); |
There was a problem hiding this comment.
Should we be concerned about the recursion here? Could deeply nested regular layers overflow the stack, and can we avoid that with an iterative work stack?
There was a problem hiding this comment.
I suppose for adverserial scenarios. But I don't think this is urgent, let me add a TODO! You'd probably have to go hundreds (or at least multiple dozens) of layers deep to trigger this. But good catch!
…shift_part3 # Conflicts: # sparse_strips/vello_common/src/clip.rs # sparse_strips/vello_common/src/geometry.rs # sparse_strips/vello_common/src/record.rs # sparse_strips/vello_common/src/strip.rs # sparse_strips/vello_common/src/transforms.rs # sparse_strips/vello_common/src/util.rs # sparse_strips/vello_common/src/viewport.rs # sparse_strips/vello_cpu/src/coarse/bucketer.rs # sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs # sparse_strips/vello_cpu/src/dispatch/single_threaded.rs # sparse_strips/vello_cpu/src/render.rs # sparse_strips/vello_hybrid/src/scene.rs
…ebender#1744) This is the first PR in a series of three (perhaps four) PRs that serve as a preparation for the Vello Hybrid rewrite. This PR is easiest reviewed commit by commit, though there isn't that much to review in the first place. Apart from adjusting a few descriptions, it's mostly just moving some code around. The first 7 commits were done by manually copy-pasting code into the right location. The last commit that fixes compiler issues were done with AI assistance. ### PR train 1. linebender#1744 — Move some code from `vello_cpu` to `vello_common` 👈 2. linebender#1745 — Add more new/refactored code into `vello_common` 3. linebender#1746 — Generalize the command recorder abstraction and add more metadata 4. linebender#1751 — Introduce `Padding` and streamline clip handling
…bender#1745) This PR is based on linebender#1744 and introduces some actual changes, once again in preparation for the Vello Hybrid rewrite. The first commit adds some utility types like `Offset` and `Size`, which will layer on be used by Vello Hybrid (they are currently partially duplicated there, but I will just remove them later on). In theory they could just live in Vello Hybrid, but since we already have `RectU16` in vello_common I figured this is the best place. The second commit resolved a TODO and creates a new generic method that allows generating fill/alpha-fill sequences from a slice of strips. We also use this for the existing `strip_bbox` function now. I'm aware that this will result in a slight slowdown since we will do the clipping even though it isn't necessary to clip anything since we are just calculating the bbox, but I think it's better to accept this cost for less code duplication. The third commit extracts all of the transforms we currently have into a custom struct, so that, once again, they can more easily be used by Vello Hybrid. Fourth commit introduces a new `ViewportState` abstraction, that abstracts all of the complexity of handling different viewport so that Vello Hybrid can use this as well. Fifth commit adds a way of querying whether an encoded paint has any opacities directly. ### PR train 1. linebender#1744 — Move some code from `vello_cpu` to `vello_common` 2. linebender#1745 — Add more new/refactored code into `vello_common` 👈 3. linebender#1746 — Generalize the command recorder abstraction and add more metadata 4. linebender#1751 — Introduce `Padding` and streamline clip handling
…inebender#1751) This is part 4 in the prep commit series, based on top of linebender#1746. The first commit introduces a new `PaddingU16` type. On current main, we use kurbo `Rect` to represent the padding that is derived from a filter. For example, a Rect of (-2, -2, 2, 2,) represents an outward padding of 2 into each direction. This makes somewhat sense, however, we use that same idea to represent a padding using `RectU16`. I think in the latter case, it doesn't make that much sense anymore. A RectU16 with dimensions (2, 2, 2, 2) represents the same idea as for `Rect`. But in reality, this is simply a zero-sized rectangle. It doesn't make a lot of sense to use `RectU16` to represent the idea of a filter padding. Therefore, this commit adds an explicit padding type so that the semantics are clearer. Methods like `intersect` and `union` that exist on `RectU16` don't really make much sense if the rectangle represents a padding, like it currently can. I think using `Rect` in the first place is also questionable, but at least left/top padding is represented as negative coordinates, so it makes a bit more sense. This change is necessary because the follow-up commit changes how zero-sized rectangles are handled, but for example for offset filters, it would be valid to have a padding in only one direction, while all other sides are 0. The second commit introduces some changes to how we handle layer clips. To put it briefly, due to the fact that strips always have a fixed height of `Tile::HEIGHT` and a width with a multiple of `Tile::WIDTH`, we often need to ensure that bounding boxes are snapped to tile coordinates. Previously, this was done in various places at different times, so very inconsistently. For Vello CPU, we basically only needed this for filter layers, but for Vello Hybrid we will need to make sure that this is also the case for normal layers. - First, we instead of computing the bbox of clips based on the control points, we simply compute it based on the strips instead. Admittedly, this is probably going to be more expensive for rectangular clips, but on the other hand it's good if we can unify this. But if desired, I can change this back and just snap the control point bbox to tile coordinates again. - `RectU16::INVERTED` is now only used as an internal accumulator to represent "no bounding box yet". However, when returning it to the user we always return an empty rectangle instead. This way, we don't have to special case this everywhere. - The definition of `RectU16::intersect` is changed such that it cannot produce negative rectangles anymore, also aligning with the kurbo implementation. - `Drawable::bbox` must return tile-aligned bounding boxes. - Some other small fixes, for example to ensure that `snap_to_tile_coorindates` preserved empty bboxes. As a consequence, vello_cpu (and in the future vello_hybrid) do not need to do any snapping anymore. All of this is taken care of at the recorder level. There's some more optimizations we should do in the future to prevent zero-sized nodes from being emitted in the first place, but this change by itself is already large enough that I'm going to leave this as a TODO! I've tested the combination of all 4 PRs against my PDF suite, and didn't see any regressions! ### PR train 1. linebender#1744 — Move some code from `vello_cpu` to `vello_common` 2. linebender#1745 — Add more new/refactored code into `vello_common` 3. linebender#1746 — Generalize the command recorder abstraction and add more metadata 4. linebender#1751 — Introduce `Padding` and streamline clip handling 👈
This PR is based on #1745. It changes the semantics of the command recorder, turning it into something more akin to a scene graph (we should probably rename it in the future!).
Up until now, the command recorder was written in a way that is only "useful" to Vello CPU. Most notably, normal layers would be folded into their parent stream, and only filter layers had an explicit representation as an independent unit. For Vello Hybrid, this is not a suitable representation, because all layers need to be represented explicitly and be rendered independently from the other layers.
To achieve this goal, this PR implements some changes, split up into two commits.
The first commit represents a refactor of the overall representation. Most notably:
Vec<Node>, which is likely to be very small, I decided to remove theVecPoolfor this. I don't think it's worth it trying to reuse those across frames.The second commit ensures that we store some more metadata about the scene, which is needed for Vello Hybrid. Yes, we do pay some additional cost for Vello CPU here, but it's basically nothing.
PR train
vello_cputovello_common#1744 — Move some code fromvello_cputovello_commonvello_common#1745 — Add more new/refactored code intovello_commonPaddingtype and streamline clip handling #1751 — IntroducePaddingand streamline clip handling