Skip to content

vello_cpu: Add ImageResolver for resolving opaque IDs at rasterization time#1451

Open
grebmeg wants to merge 1 commit intomainfrom
gemberg/glyph-cache-image-resolver
Open

vello_cpu: Add ImageResolver for resolving opaque IDs at rasterization time#1451
grebmeg wants to merge 1 commit intomainfrom
gemberg/glyph-cache-image-resolver

Conversation

@grebmeg
Copy link
Collaborator

@grebmeg grebmeg commented Feb 18, 2026

This PR introduces ImageResolver trait and an image registry on RenderContext, enabling deferred resolution of ImageSource::OpaqueId images during vello_cpu rasterization. This decouples render command encoding from image data ownership, supporting patterns like dynamic sprite atlases and glyph caches where the backing pixmap may be updated between encoding and rendering.

image

@grebmeg grebmeg force-pushed the gemberg/glyph-cache-image-resolver branch from 0c1cefb to c4775c9 Compare February 18, 2026 05:59
@grebmeg grebmeg force-pushed the gemberg/glyph-cache-image-resolver branch from c4775c9 to b10f8b3 Compare February 18, 2026 06:16
@waywardmonkeys
Copy link
Collaborator

What's the story for the vello_hybrid version of this?

This seems like it is sitting on the edge of the discussions about resource management.

(That said, this looks like a decent direction for vello_cpu ... but I think we have to be thinking about the family of renderers as well.)

Comment on lines +386 to +388
// Note: Multi-threaded dispatcher does not support ImageSource::OpaqueId.
// Images with OpaqueId will panic at rasterization time.
let noop_resolver = NoOpImageResolver;
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we use the NoOpImageResolver anyway, why does it panic? Or does it already panic in its current state (i.e. before this PR)?

Comment on lines +79 to +85
/// Image registry for resolving `ImageSource::OpaqueId` to pixmap data.
///
/// This allows decoupling render commands from image data, enabling
/// patterns like spritesheet rendering.
image_registry: HashMap<u32, Arc<Pixmap>>,
/// Counter for generating unique image IDs.
next_image_id: u32,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit, but it might be worth encapsulating the two into a custom struct?

Comment on lines +438 to +440
/// If the paint is an image with `ImageSource::OpaqueId`, it will be
/// resolved to the corresponding pixmap at rasterization time.
/// Make sure to register images with [`register_image`](Self::register_image) first.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This needs a warning that this doesn't work with multi-threading enabled.

Comment on lines +149 to +152
/// Resolve an `ImageId` to its pixmap data.
///
/// Returns `None` if the image ID is not found in the registry.
fn resolve(&self, id: ImageId) -> Option<Arc<Pixmap>>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is my intuition correct that, at least in vello_cpu, this is called once at rasterization time and then cached, instead of being called individually in each wide tile? If so, might be worth documenting this (i.e. that this will be called once at rasterization time).

}

/// Update an existing image in the registry with new pixmap data.
pub fn update_image(&mut self, id: ImageId, pixmap: Arc<Pixmap>) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the use case for this? Wouldn't it be better to just create a new image?

/// Saved state for recording operations.
#[derive(Debug)]
struct RenderState {
pub struct RenderState {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does this need to be public now?

Comment on lines +682 to +699
pub fn take_current_state(&mut self) -> RenderState {
RenderState {
paint: self.paint.clone(),
paint_transform: self.paint_transform,
transform: self.transform,
fill_rule: self.fill_rule,
stroke: core::mem::take(&mut self.stroke),
}
}

/// Restore the saved rendering state.
pub fn restore_state(&mut self, state: RenderState) {
self.transform = state.transform;
self.fill_rule = state.fill_rule;
self.stroke = state.stroke;
self.paint = state.paint;
self.paint_transform = state.paint_transform;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here, do those need to be public? If I'm not mistaken they are only used inside vello_cpu.

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.

3 participants

Comments