Skip to content

Conversation

@grebmeg
Copy link
Collaborator

@grebmeg grebmeg commented Mar 17, 2025

Overview

This PR addresses the visual artifacts (dotted/dashed lines) that appear when rendering the same scene multiple times using the vello_hybrid renderer.

image

Issue Description

Previously, when resetting the scene and re-rendering repeatedly, strange visual artifacts would appear at the boundaries between shapes - specifically dotted or dashed lines along what should be properly alpha sampled boundaries. These artifacts only appeared when:

  • The scene was reset and re-rendered in RedrawRequested event handler
  • The prepare method was called after each re-render

Root Cause

The root cause was a misalignment between the alpha values stored in the scene and the GPU alpha texture:

  • When Scene::reset was called, it correctly cleared the alphas array in the scene (reset added as a separate commit)
  • When render_svg was called, it populated the scene with new alpha values
  • However, Renderer::prepare method only created a new alpha texture when the strips buffer size increased
  • This led to a situation where new alpha values were being written to a texture sized for previous frames, or, if alphas weren’t properly reset, they retained stale data from previous frames, causing textures to grow larger each frame, meantime alpha data copying size was restricted to the current texture size.
  • The shader was calculating texture coordinates based on strip col values, but these values pointed to positions that might exceed the texture dimensions
  • Due to GPU texture addressing behavior, these out-of-bounds accesses "wrapped around" silently, causing the shader to sample completely unrelated alpha values

The critical issue was that we were only checking if the strips buffer needed to be recreated, but not independently verifying if the alpha texture needed to be recreated when only the alpha values changed.

Solution

Fixed the issue by modifying prepare method to:

  • Separately check if the strips buffer or alpha texture needs to be recreated
  • Create a new alpha texture whenever the required texture size exceeds the current texture size
  • Ensure both resources are properly sized for the current frame's data before writing to them
  • Add assertions to verify the alpha texture is large enough to hold all the alpha values

This ensures that the alpha texture always matches the current frame's alpha values, maintaining the correct relationship between strip col indices and texture coordinates, eliminating the visual artifacts.

Notes

  • Scene::reset method has been updated to properly (fully) reset the scene state by clearing all data

@grebmeg grebmeg requested a review from DJMcNab March 17, 2025 04:47
@grebmeg
Copy link
Collaborator Author

grebmeg commented Mar 17, 2025

cc @taj-p

Copy link
Member

@DJMcNab DJMcNab left a comment

Choose a reason for hiding this comment

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

This is reasonable.

I'd like to also make sure that @tomcur is happy with it Edit: Discussion with Tom has indicated that he will review once it has merged, if it gets in tonight

Comment on lines 143 to 146
let svg_filename = std::env::args().nth(1).expect("svg filename is first arg");
let svg: String =
std::fs::read_to_string(svg_filename).expect("error reading file");
let parsed_svg = PicoSvg::load(&svg, 1.0).expect("error parsing SVG");
Copy link
Member

Choose a reason for hiding this comment

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

It's not clear why these three lines are inside RedrawRequested

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agreed, I’ve moved it out to resumed.

Fixed in 79cb7a6

let height = surface.config.height;
self.scene.reset();

let render_scale = 5.0;
Copy link
Member

Choose a reason for hiding this comment

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

Ideally, now would be the time to change some parameter based on user input (e.g. scroll-wheel or a keyboard button to change the render_scale)

It doesn't need to have click/drag or anything else, but just something to validate that the change works correctly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I’ve added a basic zooming behavior — it looks like everything is working as expected. I’ll refine it later by adding support for proper transformations.

Added in c9235f5

Some(resources) => required_strips_size > resources.strips_buffer.size(),
None => true,
};
let (needs_new_strips_buffer, needs_new_alpha_texture) = match &self.resources {
Copy link
Member

Choose a reason for hiding this comment

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

Some of this logic is quite hard to follow; there's a lot more unwrap/expect than I'd normally expect (ha). That is, I feel like the three stages of "check if anything needs to be recreated, then recreate them, then access them", could be combined better.

That being said, I don't think that refactoring that now is helpful, and so I'm happy for this to land.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have a similar feeling but haven’t yet come up with a better approach to managing resource preparation from None to Some and subsequently validating whether recreation is necessary. One idea is to precreate all buffers/textures in new, eliminating the need to check for None and only requiring checks for resizing buffers/textures. I think I just need to spend more time refining this and will revisit it a bit later.

@grebmeg grebmeg added this pull request to the merge queue Mar 18, 2025
Merged via the queue into linebender:main with commit 6f22a89 Mar 18, 2025
17 checks passed
@grebmeg grebmeg deleted the gemberg-fix-alphas-rendering branch March 18, 2025 00:32
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