Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clippy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ trivial-copy-size-limit = 16

# END LINEBENDER LINT SET

doc-valid-idents = ["MotionMark", "WebGPU", "PostScript", ".."]
doc-valid-idents = ["MotionMark", "WebGPU", "PostScript", "ClearType", ".."]
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

124 changes: 121 additions & 3 deletions sparse_strips/vello_api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,129 @@ Full documentation at https://github.com/orium/cargo-rdme -->
<!-- Intra-doc links used in lib.rs should be evaluated here.
See https://linebender.org/blog/doc-include/ for related discussion. -->

<!-- N.B. These links are intentionally incomplete, as the below doc will need to be rewritten -->

<!-- cargo-rdme start -->

A stub crate.
Planned to provide an abstraction between different renderers in the future.
This abstraction is not yet designed.
Vello API is the rendering API of the 2D renderers in the Vello project.

There are currently two [supported Vello renderers](#renderers), each with different tradeoffs.
This crate allows you to write the majority of your application logic to support either of those renderers.
These renderers are [Vello CPU](todo) and [Vello Hybrid](todo).

# Usage

TODO: Mention Renderer trait when it exists. Otherwise, this code isn't really usable yet.
TODO: This is a stub just to have an outline to push.

# Renderers

The Vello renderers which support this API are:

- Vello CPU, an extremely portable 2D renderer which does not require a GPU.
It is one of the fastest CPU-only 2D renderers in Rust.
- Vello Hybrid, which runs the most compute intensive portions of rendering on the GPU, improving performance over Vello CPU.
It will have wide compatibility with most devices, so long as they have a GPU, including running well on the web.
<!-- We might also have, to be determined:
- Vello Classic, which performs almost all rendering on the GPU, which gives great performance on devices with decent GPUs.
However, it cannot run well on devices with weak GPUs, or in contexts without support for compute shaders, such as the web.
It also has unavoidably high memory usage, and can silently fail to render if the scene gets too big.
-->

Currently, Vello CPU is much more mature, and so most consumers should currently prefer that implementation.
We hope that in the not-too distant future, application developers will be able to migrate to Vello Hybrid.
We expect headless use cases (such as screenshot tests or server-rendered previews) to prefer Vello CPU, due to
its cross-platform consistency and lower latency.

Note that even once Vello Hybrid is more stable, applications using Vello Hybrid might need to support falling
back to Vello CPU for compatibility or performance reasons.

This abstraction is tailored for the Vello renderers, as we believe that these have a sufficiently broad coverage of the trade-off
space to be viable for any consumer.
Vello API guarantees identical rendering between renderers which implement it, barring subpixel differences due to precision/different rounding.
This doesn't apply to renderer-specific features.
<!-- TODO: Is ^ true? -->

# Abstraction Boundaries

The abstractions in this crate are focused on 2D rendering, and the resources required to perform that.
In particular, this does not abstract over strategies for:

- creating the renderer (which can require more context, such as a wgpu `Device`); nor
- bringing external content into the renderer (for example, already resident GPU textures); nor
- presenting rendered content to an operating system window.

These functionalities are however catered for where applicable by APIs on the specific renderers.
The renderer API supports downcasting to the specific renderer, so that these extensions can be called.
Each supported renderer will/does have examples showing how to achieve this yourself.

# Text

Vello API does not handle text/glyph rendering itself.
This allows for improved resource sharing of intermediate text layout data, for hinting and ink splitting underlines.

Text can be rendered to Vello API scenes using the "Parley Draw" crate.
Note that this crate is not currently implemented; design work is ongoing.
We also support rendering using using traditional glyph atlases, which may be preferred by some consumers.
This is especially useful to achieve subpixel rendering, such as ClearType, which Vello doesn't currently support directly.

# Unimplemented Features

NOTE: This section is not complete; in particular, we have only pushed a half-version of this API to make review more scoped.

The current version of Vello API is a minimal viable product for exploration and later expansion.
As such, there are several features which we expect to be included in this API, but which are not yet exposed in this crate.
These are categorised as follows:

## Out of scope/Renderer specific

<!-- This section can be removed once the other three classes are empty -->
As discussed above, some features are out-of-scope, as they have concerns which need to be handled individually by each renderer.
This includes:

- Rendering directly to a surface.
- Importing "external" textures (e.g. from a `wgpu::Texture`)

## Excluded for expedience

- Renderer specific painting commands (i.e. using downcasting).
This is intended to be an immediate follow-up to the MVP landing.
- Pushing/popping clip paths (i.e. non-isolated clipping).
This feature should be easy to restore, although it isn't clear how it will work with "Vello GPU", i.e. Hybrid with GPU rasterisation
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
This feature should be easy to restore, although it isn't clear how it will work with "Vello GPU", i.e. Hybrid with GPU rasterisation
This feature should be easy to restore, although it isn't clear how it will work with "Vello GPU", i.e. Hybrid with GPU sparse strip rendering

Copy link
Member Author

@DJMcNab DJMcNab Jan 23, 2026

Choose a reason for hiding this comment

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

Do we have dictionary somewhere of what these terms mean in the pipeline? I was treating "rasterisation" as being the "convert flattened lines to alphas". However, I see that this terminology is more consistent with Raph's RustWeek slides, so I'll change it in my follow-up (edit: #1375)

- Downloading rendered textures back to the CPU/host.
This is currently supported through individual methods on the renderers, but we hope to have a portable API for coordinating this.
- Anti-aliasing threshold.
- Fill rules for clip paths.
- Proper error types; we currently return `()`. This is to ensure that these error types are explicitly interim.
- Texture resizing; this might not be viably possible
- More explicit texture atlas support
- Proper handling of where `TextureHandle` and `TextureId` should be passed.
- "Unsetting" the brush; this is mostly useful for append style operations, which may unexpectedly change the brush.
- Dynamic version of `PaintScene`, allowing `dyn PaintScene` for cases where that would be relevant.

## Not cross-renderer

There are some features which are only implemented in one of our target renderers, so cannot yet be included in the generalised API.
As an interim solution, we intend to expose these features as renderer specific extensions (see the "excluded for expedience section").

For Vello CPU, these are (i.e. Vello Hybrid does not implement these):

- Masks
- Filter effects
- Non-isolated blending (this is "supported" by Vello Hybrid, but currently silently ignored)
- Blurred rounded rectangles (note that currently this is actually included in the abstraction, despite this status)

There are currently no such features the other way around (i.e. which only Vello Hybrid supports).

## Not implemented

- Path caching. This feature is intended to allow re-using paths efficiently, primarily for glyphs.
- Blurred rounded rectangle paints in custom shapes (e.g. to exclude the unblurred parts).
(TODO: This actually does exist as a method, but no renderer implements it; we should maybe remove that method?)
- Mipmaps

For even more detail on some of these, see the `design.md` file.
Note however that file is very uncurated.

<!-- cargo-rdme end -->

Expand Down
141 changes: 138 additions & 3 deletions sparse_strips/vello_api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,144 @@
// Copyright 2025 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! A stub crate.
//! Planned to provide an abstraction between different renderers in the future.
//! This abstraction is not yet designed.
// After you edit the crate's doc comment, run this command, then check README.md for any missing links
// cargo rdme --workspace-project=vello_api

// These docs are written for a more complete version of Vello API.
Copy link
Contributor

Choose a reason for hiding this comment

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

A lot of this documentation feels like it might be better suited to being in either:

  • The PR description
  • A dedicated .md file design document

with rustdoc reserved for user-facing documentation.

(I certainly don't think we could ship it in this state)

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't have the mental bandwidth to rewrite these docs at the moment. That's especially true when I believe this is likely to further change again, around textures being added. I agree that it's important, but I really would like to avoid blocking this PR on it. See also below.

I certainly don't think we could ship it in this state

From my perspective, this PR is being submitted as a base for further collaboration and scoped review, rather than as something to be shipped as-is. It definitely needs a lot more follow-up before release.


//! Vello API is the rendering API of the 2D renderers in the Vello project.
//!
//! There are currently two [supported Vello renderers](#renderers), each with different tradeoffs.
//! This crate allows you to write the majority of your application logic to support either of those renderers.
//! These renderers are [Vello CPU](todo) and [Vello Hybrid](todo).
//!
//! # Usage
//!
//! TODO: Mention Renderer trait when it exists. Otherwise, this code isn't really usable yet.
//! TODO: This is a stub just to have an outline to push.
//!
//! # Renderers
//!
//! The Vello renderers which support this API are:
//!
//! - Vello CPU, an extremely portable 2D renderer which does not require a GPU.
//! It is one of the fastest CPU-only 2D renderers in Rust.
//! - Vello Hybrid, which runs the most compute intensive portions of rendering on the GPU, improving performance over Vello CPU.
//! It will have wide compatibility with most devices, so long as they have a GPU, including running well on the web.
//! <!-- We might also have, to be determined:
//! - Vello Classic, which performs almost all rendering on the GPU, which gives great performance on devices with decent GPUs.
//! However, it cannot run well on devices with weak GPUs, or in contexts without support for compute shaders, such as the web.
//! It also has unavoidably high memory usage, and can silently fail to render if the scene gets too big.
//! -->
//!
//! Currently, Vello CPU is much more mature, and so most consumers should currently prefer that implementation.
//! We hope that in the not-too distant future, application developers will be able to migrate to Vello Hybrid.
//! We expect headless use cases (such as screenshot tests or server-rendered previews) to prefer Vello CPU, due to
//! its cross-platform consistency and lower latency.
//!
//! Note that even once Vello Hybrid is more stable, applications using Vello Hybrid might need to support falling
//! back to Vello CPU for compatibility or performance reasons.
//!
//! This abstraction is tailored for the Vello renderers, as we believe that these have a sufficiently broad coverage of the trade-off
//! space to be viable for any consumer.
//! Vello API guarantees identical rendering between renderers which implement it, barring subpixel differences due to precision/different rounding.
//! This doesn't apply to renderer-specific features.
//! <!-- TODO: Is ^ true? -->
//!
//! # Abstraction Boundaries
//!
//! The abstractions in this crate are focused on 2D rendering, and the resources required to perform that.
//! In particular, this does not abstract over strategies for:
//!
//! - creating the renderer (which can require more context, such as a wgpu `Device`); nor
//! - bringing external content into the renderer (for example, already resident GPU textures); nor
//! - presenting rendered content to an operating system window.
//!
//! These functionalities are however catered for where applicable by APIs on the specific renderers.
//! The renderer API supports downcasting to the specific renderer, so that these extensions can be called.
//! Each supported renderer will/does have examples showing how to achieve this yourself.
//!
//! # Text
//!
//! Vello API does not handle text/glyph rendering itself.
//! This allows for improved resource sharing of intermediate text layout data, for hinting and ink splitting underlines.
//!
//! Text can be rendered to Vello API scenes using the "Parley Draw" crate.
//! Note that this crate is not currently implemented; design work is ongoing.
//! We also support rendering using using traditional glyph atlases, which may be preferred by some consumers.
//! This is especially useful to achieve subpixel rendering, such as ClearType, which Vello doesn't currently support directly.
Copy link
Contributor

Choose a reason for hiding this comment

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

How soon do you we anticipate Parley Draw being available? In my mind it could still be several month away. As such, I wonder if it would be worth temporarily adding a method to the PaintScene trait that mirrors the glyph rendering API that vello_cpu and vello_hybrid currently have. The idea being that we would remove it once we have an implementation available for the Parley Draw approach.

(we could also optionally consider having a transitional period where both APIs are available)
(this is essentially the approach that AnyRender has taken)

Copy link
Member Author

Choose a reason for hiding this comment

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

The story here isn't at all clear from my perspective, and is changing as we get image atlasing and path caching actually working. To start off with, I'm not going to add text in this PR, again mostly for ease of review.
A few notes:

  1. I know that we do have a Parley Draw crate already in the Parley repo. I haven't looked into what form that has taken due to a lack of time, but I presume you have more context to know that isn't what we have here.
  2. The code to build text support on top of the API in this crate is not an awful lot of code (if we ignore bitmap glyphs). As such, my short-term proposal is to implement a naïve, cacheless "Parley Draw" in this repo as something like "Vello Interim Text".
  3. Adding glyph support in the form you propose to this version of Vello API is not the path I'd recommend us taking. Essentially, I don't think it's actually any quicker than getting "Vello Interim Text" working.

Essentially, all of this needs more conversation to actually work out what problem Parley Draw is solving/will be solving. That isn't for this PR. I realise that "the long-term story should become clearer in a couple of months" isn't a useful message for consumers. The timeline being so long is an unfortunate side effect of the timing of my changing role; I'm having to focus my remaining time on this contract carefully, and Parley Draw is one of the things falling off as a result.

Copy link
Contributor

@nicoburns nicoburns Jan 19, 2026

Choose a reason for hiding this comment

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

Looks like we currently have a copy-paste of the Vello code in Parley Draw. I don't think anyone has actually used this code in anger.


I'm happy to go down the "get Parley Draw working route". We should just bear in mind that this won't be very useful until we have that.


It occurs to me that taking "render glyphs" out of the abstraction will make it much easier to implement rendering backends (e.g. iced_wgpu, tiny-skia implement everything except glyph rasterization), so that's a nice benefit of this approach.

Copy link
Contributor

@taj-p taj-p Jan 21, 2026

Choose a reason for hiding this comment

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

For context, we're investing heavily in Parley Draw. @grebmeg is adding glyph caching to Parley Draw and @valadaptive has been implementing a bunch of fixes, performance improvements, refactoring Parley tests to use Parley Draw for rendering, and underline support.

The API can be used today for Vello CPU. Canva will be migrating our text rendering to use Parley Draw in the coming 1 to 3 weeks.

I see Parley Draw's API evolving a number of ways. As it pertains to Vello API, it is likely possible to implement a Vello API backend for Parley Draw. But, we also need to consult with other owners on these topics (you two included).

But, there are some design and implementation decisions we need to discuss. Alex's ongoing and impactful work into glyph caching has surfaced some questions we will want to answer - for example, supporting sampling from alpha textures and performantly applying paint to those alphas.

I am also in no doubt that Valadaptive's ongoing work will surface even more questions that need to be answered.

All said, the API is expected to undergo significant iteration. So, consumers beware! 😆 ⚠️

//!
//! # Unimplemented Features
//!
//! NOTE: This section is not complete; in particular, we have only pushed a half-version of this API to make review more scoped.
//!
//! The current version of Vello API is a minimal viable product for exploration and later expansion.
//! As such, there are several features which we expect to be included in this API, but which are not yet exposed in this crate.
//! These are categorised as follows:
//!
//! ## Out of scope/Renderer specific
//!
//! <!-- This section can be removed once the other three classes are empty -->
//! As discussed above, some features are out-of-scope, as they have concerns which need to be handled individually by each renderer.
//! This includes:
//!
//! - Rendering directly to a surface.
//! - Importing "external" textures (e.g. from a `wgpu::Texture`)
//!
//! ## Excluded for expedience
//!
//! - Renderer specific painting commands (i.e. using downcasting).
//! This is intended to be an immediate follow-up to the MVP landing.
//! - Pushing/popping clip paths (i.e. non-isolated clipping).
//! This feature should be easy to restore, although it isn't clear how it will work with "Vello GPU", i.e. Hybrid with GPU rasterisation
//! - Downloading rendered textures back to the CPU/host.
//! This is currently supported through individual methods on the renderers, but we hope to have a portable API for coordinating this.
//! - Anti-aliasing threshold.
//! - Fill rules for clip paths.
//! - Proper error types; we currently return `()`. This is to ensure that these error types are explicitly interim.
//! - Texture resizing; this might not be viably possible
//! - More explicit texture atlas support
//! - Proper handling of where `TextureHandle` and `TextureId` should be passed.
//! - "Unsetting" the brush; this is mostly useful for append style operations, which may unexpectedly change the brush.
//! - Dynamic version of `PaintScene`, allowing `dyn PaintScene` for cases where that would be relevant.
//!
//! ## Not cross-renderer
//!
//! There are some features which are only implemented in one of our target renderers, so cannot yet be included in the generalised API.
//! As an interim solution, we intend to expose these features as renderer specific extensions (see the "excluded for expedience section").
//!
//! For Vello CPU, these are (i.e. Vello Hybrid does not implement these):
//!
//! - Masks
//! - Filter effects
//! - Non-isolated blending (this is "supported" by Vello Hybrid, but currently silently ignored)
//! - Blurred rounded rectangles (note that currently this is actually included in the abstraction, despite this status)
//!
//! There are currently no such features the other way around (i.e. which only Vello Hybrid supports).
//!
//! ## Not implemented
//!
//! - Path caching. This feature is intended to allow re-using paths efficiently, primarily for glyphs.
//! - Blurred rounded rectangle paints in custom shapes (e.g. to exclude the unblurred parts).
//! (TODO: This actually does exist as a method, but no renderer implements it; we should maybe remove that method?)
//! - Mipmaps
//!
//! For even more detail on some of these, see the `design.md` file.
//! Note however that file is very uncurated.
#![forbid(unsafe_code)]
#![no_std]
#![expect(clippy::result_unit_err, reason = "This code is very experimental.")]

extern crate alloc;

mod painter;

pub mod paths;
pub mod scene;
pub mod texture;

pub use self::painter::{PaintScene, StandardBrush};
pub use self::scene::Scene;

pub use ::peniko;
Loading