Skip to content

Comments

vello_common: move subpath closing logic into flatten#1340

Merged
tomcur merged 1 commit intolinebender:mainfrom
tomcur:subpath-closing
Jan 5, 2026
Merged

vello_common: move subpath closing logic into flatten#1340
tomcur merged 1 commit intolinebender:mainfrom
tomcur:subpath-closing

Conversation

@tomcur
Copy link
Member

@tomcur tomcur commented Jan 2, 2026

This is just a logic change and slight simplification of the lines representation. By moving the burden of closing input subpaths to flattening itself, this will allow, e.g., culling geometry at the level of Béziers. Before, the closing of subpaths lived as a post-processing step after flattening.

The actual change that starts using this for something more exciting is in #1341.

This by itself does not really bring down timings (plus noise makes it hard to measure).

flatten/Ghostscript_Tiger
                        time:   [203.93 µs 204.28 µs 204.68 µs]
                        change: [-2.1022% -1.8481% -1.5684%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 12 outliers among 100 measurements (12.00%)
  10 (10.00%) high mild
  2 (2.00%) high severe
flatten/paris-30k       time:   [12.222 ms 12.298 ms 12.384 ms]
                        change: [+0.7747% +1.3966% +2.2290%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 13 outliers among 100 measurements (13.00%)
  13 (13.00%) high severe

This is just a logic change and slight simplification of the lines
representation. By moving the burden of closing input subpaths to
flattening itself, this will allow to, e.g., cull geometry at the level
of Bèziers. Before, the closing of subpaths lived as a post-processing
step after flattening.

The actual change that starts using this for something more exciting is
in a follow-up PR.

This by itself does not really bring down timings (plus noise makes it
hard to measure).

```
flatten/Ghostscript_Tiger
                        time:   [203.93 µs 204.28 µs 204.68 µs]
                        change: [-2.1022% -1.8481% -1.5684%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 12 outliers among 100 measurements (12.00%)
  10 (10.00%) high mild
  2 (2.00%) high severe
flatten/paris-30k       time:   [12.222 ms 12.298 ms 12.384 ms]
                        change: [+0.7747% +1.3966% +2.2290%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 13 outliers among 100 measurements (13.00%)
  13 (13.00%) high severe
```
Copy link
Collaborator

@LaurenzV LaurenzV left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not seeing anything that speaks against this, but it's not entirely clear to me why it's necessary. If culling of cubic's happens before flattening, why does it matter whether we close unclosed sub-paths during flattening or afterwards? 🤔

@tomcur
Copy link
Member Author

tomcur commented Jan 5, 2026

why does it matter whether we close unclosed sub-paths during flattening or afterwards? 🤔

We need to ensure open input subpaths get closed at some point (as we've been doing before), but then want to allow open subpaths later in rendering for culling reasons.

Say we we have a path like the following, with the box describing the viewport and * marking the path.

                  * * * * *
                *         *
   ---------- * -----     *
   |        *       |     *
   |      *         |     *
   |    *           |     *
   |  *             |     *
   |*               |     *
  *|                |     *
*  |                |     *
*  |                |     *
*  ------------------     *
*                         *
*                         *
*                         *
*                         *
*                         *
*                         *
*                         *
* * * * * * * * * * * * * *

The two elements of that path that matter for rendering are the diagonal line and the left-of-viewport vertical line, and we'd want to render as follows.

                  *
                * 
   ---------- * -----
   |        *░░░░░░░|
   |      *░░░░░░░░░|
   |    *░░░░░░░░░░░|
   |  *░░░░░░░░░░░░░|
   |*░░░░░░░░░░░░░░░|
  *|░░░░░░░░░░░░░░░░|
*  |░░░░░░░░░░░░░░░░|
*  |░░░░░░░░░░░░░░░░|
*  ------------------
*
*
* 
*
*
*
*

If we cull the three elements that do not matter, and as a post-processing step enforce paths to be closed, we instead get

                  *
                **
   ---------- * *----
   |        *░░*    |
   |      *░░░*     |
   |    *░░░░*      |
   |  *░░░░░*       |
   |*░░░░░░*        |
  *|░░░░░░*         |
*  |░░░░░*          |
*  |░░░░*           |
*  --- * ------------
*     *
*    *
*   *
*  *
* *
**
*

@tomcur tomcur added this pull request to the merge queue Jan 5, 2026
Merged via the queue into linebender:main with commit cc2dd70 Jan 5, 2026
17 checks passed
@tomcur tomcur deleted the subpath-closing branch January 5, 2026 13:04
@tomcur
Copy link
Member Author

tomcur commented Jan 5, 2026

I've added the above as docstrings to #1341.

github-merge-queue bot pushed a commit that referenced this pull request Feb 20, 2026
On top of #1340.

tldr: If only some of Tiger's whiskers are visible, this results in -90%
and -60% timings on flattening and tiling.

This conservatively checks whether Bézier path elements we're about to
flatten are outside the viewport. If they are fully to the right, top,
or bottom of the viewport, the Bézier does not impact pixel coverage or
coarse winding at all, and can be ignored.

If it is fully to the left, it does impact pixel coverage and coarse
winding, but only the element's start and endpoint y-values matter, not
the exact shape, meaning we can just yield a line rather than finely
flattening.

The following two Ghostscript Tigers have their viewboxes reduced to `50
50 100 100` and `90 90 20 20`, down from `0 0 200 200`. Their flattening
time is reduced by 52% and 90% respectively, and their tiling time by
22% and 60%.

<details>
<summary>Expand for the Tiger files</summary>


`Ghostscript_Tiger-viewboxed.svg`


![Ghostscript_Tiger-viewboxed](https://github.com/user-attachments/assets/d4f51327-78c4-421a-863e-7f049e6f8c28)

`Ghostscript_Tiger-viewboxed-extreme.svg`


![Ghostscript_Tiger-viewboxed-extreme](https://github.com/user-attachments/assets/61a6bb3b-b058-4f1e-9517-ca41db5bee96)

</details>

If more or less everything ends up being in the viewport, the additional
calculation is wasted and increases flattening time by ~3%. This is of
course workload-dependent.


Flattening timings:

```
flatten/Ghostscript_Tiger
                        time:   [209.94 µs 210.21 µs 210.51 µs]
                        change: [+2.6850% +3.1753% +3.6309%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
  4 (4.00%) high mild
  1 (1.00%) high severe
flatten/Ghostscript_Tiger-viewboxed
                        time:   [97.189 µs 97.287 µs 97.399 µs]
                        change: [-52.787% -52.650% -52.514%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  6 (6.00%) high mild
  2 (2.00%) high severe
flatten/Ghostscript_Tiger-viewboxed-extreme
                        time:   [19.722 µs 19.741 µs 19.761 µs]
                        change: [-90.311% -90.280% -90.255%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  7 (7.00%) high mild
  1 (1.00%) high severe
flatten/paris-30k       time:   [12.740 ms 12.764 ms 12.788 ms]
                        change: [+2.6014% +3.3631% +4.0837%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 3 outliers among 100 measurements (3.00%)
  3 (3.00%) high mild
```

Tiling timings:

```
tile/Ghostscript_Tiger  time:   [175.39 µs 175.79 µs 176.28 µs]
                        change: [-0.4403% -0.0016% +0.4400%] (p = 1.00 > 0.05)
                        No change in performance detected.
Found 1 outliers among 50 measurements (2.00%)
  1 (2.00%) high mild
tile/Ghostscript_Tiger-viewboxed
                        time:   [78.932 µs 79.147 µs 79.409 µs]
                        change: [-23.209% -22.803% -22.369%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 50 measurements (10.00%)
  1 (2.00%) high mild
  4 (8.00%) high severe
tile/Ghostscript_Tiger-viewboxed-extreme
                        time:   [13.378 µs 13.390 µs 13.405 µs]
                        change: [-60.417% -60.306% -60.199%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 6 outliers among 50 measurements (12.00%)
  2 (4.00%) high mild
  4 (8.00%) high severe
tile/paris-30k          time:   [20.970 ms 21.001 ms 21.034 ms]
                        change: [-0.4881% -0.2397% +0.0108%] (p = 0.07 > 0.05)
                        No change in performance detected.
Found 1 outliers among 50 measurements (2.00%)
  1 (2.00%) high mild
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants