Skip to content
Draft
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
119 changes: 118 additions & 1 deletion examples/scenes/src/test_scenes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ macro_rules! scene {
}

export_scenes!(
clipped_blend(clipped_blend),
splash_with_tiger(impls::splash_with_tiger(), "splash_with_tiger", false),
funky_paths(funky_paths),
stroke_styles(impls::stroke_styles(Affine::IDENTITY), "stroke_styles", false),
Expand Down Expand Up @@ -100,14 +101,130 @@ mod impls {
use rand::Rng;
use rand::{SeedableRng, rngs::StdRng};
use vello::kurbo::{
Affine, BezPath, Cap, Circle, Ellipse, Join, PathEl, Point, Rect, Shape, Stroke, Vec2,
Affine, BezPath, Cap, Circle, Ellipse, Join, PathEl, Point, Rect, Shape, Stroke, Triangle,
Vec2,
};
use vello::peniko::color::{AlphaColor, Lch, palette};
use vello::peniko::*;
use vello::*;

const FLOWER_IMAGE: &[u8] = include_bytes!("../../assets/splash-flower.jpg");

pub(super) fn clipped_blend(scene: &mut Scene, params: &mut SceneParams<'_>) {
params.resolution = Some(Vec2::new(1000., 1200.));
let transform = Affine::translate((10., 100.));
params.text.add_run(
&mut *scene,
None,
32.,
Color::WHITE,
transform.then_translate((10., -30.).into()),
None,
&Style::Fill(Fill::EvenOdd),
"No Clip",
);

scene.fill(
Fill::EvenOdd,
transform,
palette::css::BLUE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
scene.push_layer(
Mix::Multiply,
1.0,
transform,
&Triangle::from_coords((200., 0.), (0., 400.), (400., 400.)),
);
scene.fill(
Fill::EvenOdd,
transform,
palette::css::AQUAMARINE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
scene.pop_layer();

let transform = Affine::translate((0., 600.));
params.text.add_run(
&mut *scene,
None,
32.,
Color::WHITE,
transform.then_translate((10., -30.).into()),
None,
&Style::Fill(Fill::EvenOdd),
"Mix::Normal",
);

scene.fill(
Fill::EvenOdd,
transform,
palette::css::BLUE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
let layer_shape = Triangle::from_coords((200., 0.), (0., 400.), (400., 400.));
scene.push_layer(Mix::Normal, 1.0, transform, &layer_shape);
scene.push_layer(Mix::Multiply, 1.0, transform, &layer_shape);
scene.fill(
Fill::EvenOdd,
transform,
palette::css::AQUAMARINE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
scene.pop_layer();
scene.pop_layer();

let transform = Affine::translate((500., 600.));
params.text.add_run(
&mut *scene,
None,
32.,
Color::WHITE,
transform.then_translate((10., -30.).into()),
None,
&Style::Fill(Fill::EvenOdd),
"Mix::Clip",
);

scene.fill(
Fill::EvenOdd,
transform,
palette::css::BLUE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
let layer_shape = Triangle::from_coords((200., 0.), (0., 400.), (400., 400.));
scene.push_layer(Mix::Clip, 1.0, transform, &layer_shape);
scene.push_layer(
Mix::Clip,
1.0,
transform,
&Rect::from_origin_size((200.0, 200.0), (200.0, 200.0)),
);
scene.push_layer(Mix::Multiply, 1.0, transform, &layer_shape);
scene.fill(
Fill::EvenOdd,
transform,
palette::css::AQUAMARINE,
None,
&Rect::from_origin_size((0., 0.), (400., 400.)),
);
scene.pop_layer();
scene.pop_layer();
scene.fill(
Fill::EvenOdd,
transform,
palette::css::RED,
None,
&Rect::from_origin_size((0.0, 100.0), (400.0, 100.0)),
);
scene.pop_layer();
}

pub(super) fn emoji(scene: &mut Scene, params: &mut SceneParams<'_>) {
let text_size = 120. + 20. * (params.time * 2.).sin() as f32;
let s = "🎉🤠✅";
Expand Down
12 changes: 8 additions & 4 deletions vello_shaders/shader/coarse.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ fn write_image(info_offset: u32) {
cmd_offset += 2u;
}

fn write_begin_clip() {
fn write_begin_clip(flags: u32) {
alloc_cmd(1u);
ptcl[cmd_offset] = CMD_BEGIN_CLIP;
ptcl[cmd_offset] = CMD_BEGIN_CLIP | flags;
cmd_offset += 1u;
}

Expand Down Expand Up @@ -320,7 +320,6 @@ fn main(
let is_clip = (tag & 1u) != 0u;
var is_blend = false;
if is_clip {
let BLEND_CLIP = (128u << 8u) | 3u;
let scene_offset = draw_monoids[drawobj_ix].scene_offset;
let dd = config.drawdata_base + scene_offset;
let blend = scene[dd];
Expand Down Expand Up @@ -413,7 +412,12 @@ fn main(
if tile.segment_count_or_ix == 0u && tile.backdrop == 0 {
clip_zero_depth = clip_depth + 1u;
} else {
write_begin_clip();
let blend = scene[dd];
Copy link
Member

Choose a reason for hiding this comment

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

Handy that this was available. Seeing what the blend mode was at "write begin clip" time was my greatest worry in implementing this (as in I was worried it would be annoying)

if blend == BLEND_CLIP {
write_begin_clip(NON_ISOLATED_BLENDING_FLAG);
} else {
write_begin_clip(0);
}
render_blend_depth += 1u;
max_blend_depth = max(max_blend_depth, render_blend_depth);
}
Expand Down
16 changes: 11 additions & 5 deletions vello_shaders/shader/fine.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -958,10 +958,11 @@ fn main(
// main interpretation loop
while true {
let tag = ptcl[cmd_ix];
if tag == CMD_END {
let cmd = tag & 0xFFu;
if cmd == CMD_END {
break;
}
switch tag {
switch cmd {
case CMD_FILL: {
let fill = read_fill(cmd_ix);
#ifdef msaa
Expand All @@ -987,18 +988,23 @@ fn main(
cmd_ix += 2u;
}
case CMD_BEGIN_CLIP: {
Copy link
Member

Choose a reason for hiding this comment

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

My vague plan for implementing this was to turn it into a whole new command, as the operations are at least a little different. I presume you didn't want to do that to avoid the code duplication?

let is_non_isolated = (tag & NON_ISOLATED_BLENDING_FLAG) != 0;
var rgba_mul = vec4(0.0);
if is_non_isolated {
rgba_mul = vec4(1.0);
}
if clip_depth < BLEND_STACK_SPLIT {
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
blend_stack[clip_depth][i] = pack4x8unorm(rgba[i]);
rgba[i] = vec4(0.0);
}
rgba[i] *= rgba_mul;
Copy link
Member

Choose a reason for hiding this comment

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

You're sure the backdrop isn't now double counted, even if it has alpha? I think that a over a if a is partially transparent isn't equal to a.

}
} else {
let blend_in_scratch = clip_depth - BLEND_STACK_SPLIT;
let local_tile_ix = local_id.x * PIXELS_PER_THREAD + local_id.y * TILE_WIDTH;
let local_blend_start = blend_offset + blend_in_scratch * TILE_WIDTH * TILE_HEIGHT + local_tile_ix;
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
blend_spill[local_blend_start + i] = pack4x8unorm(rgba[i]);
rgba[i] = vec4(0.0);
rgba[i] *= rgba_mul;
Copy link
Member

Choose a reason for hiding this comment

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

Obviously this is assuming that doing a floating point multiply is extremely cheap, right?
At least in the non-isolated case, not doing anything at all is of course more ideal. Although that would of course involve hoisting the condition, leading to code duplication.

Copy link
Member

Choose a reason for hiding this comment

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

I presume that the multiply by one (or indeed by zero) can't cause rendering differences. Given that these are only colours, I can't see that it can.

}
}
clip_depth += 1u;
Expand Down
7 changes: 7 additions & 0 deletions vello_shaders/shader/shared/ptcl.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ const CMD_END_CLIP = 11u;
const CMD_JUMP = 12u;
const CMD_BLUR_RECT = 13u;

// Full blend state for clip + normal.
const BLEND_CLIP = (128u << 8u) | 3u;

// Flag set on CMD_BEGIN_CLIP to allow blending to punch through
// the layer.
const NON_ISOLATED_BLENDING_FLAG = 0x100u;

// The individual PTCL structs are written here, but read/write is by
// hand in the relevant shaders

Expand Down
Loading