mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-09 20:31:29 +11:00
Mostly working strokes
The fat line in coarse path rendering is not done, but when lines are thin that mostly looks ok. Onward to tiger!
This commit is contained in:
parent
92d6b1188f
commit
7ae5aa7491
|
@ -50,9 +50,12 @@ var<storage> paths: array<Path>;
|
|||
var<storage> tiles: array<Tile>;
|
||||
|
||||
@group(0) @binding(7)
|
||||
var<storage, read_write> bump: BumpAllocators;
|
||||
var<storage> info: array<u32>;
|
||||
|
||||
@group(0) @binding(8)
|
||||
var<storage, read_write> bump: BumpAllocators;
|
||||
|
||||
@group(0) @binding(9)
|
||||
var<storage, read_write> ptcl: array<u32>;
|
||||
|
||||
|
||||
|
@ -95,15 +98,23 @@ fn write_path(tile: Tile, linewidth: f32) {
|
|||
// TODO: take flags
|
||||
// TODO: handle stroke
|
||||
alloc_cmd(3u);
|
||||
if tile.segments != 0u {
|
||||
let fill = CmdFill(tile.segments, tile.backdrop);
|
||||
ptcl[cmd_offset] = CMD_FILL;
|
||||
ptcl[cmd_offset + 1u] = fill.tile;
|
||||
ptcl[cmd_offset + 2u] = u32(fill.backdrop);
|
||||
cmd_offset += 3u;
|
||||
if linewidth < 0.0 {
|
||||
if tile.segments != 0u {
|
||||
let fill = CmdFill(tile.segments, tile.backdrop);
|
||||
ptcl[cmd_offset] = CMD_FILL;
|
||||
ptcl[cmd_offset + 1u] = fill.tile;
|
||||
ptcl[cmd_offset + 2u] = u32(fill.backdrop);
|
||||
cmd_offset += 3u;
|
||||
} else {
|
||||
ptcl[cmd_offset] = CMD_SOLID;
|
||||
cmd_offset += 1u;
|
||||
}
|
||||
} else {
|
||||
ptcl[cmd_offset] = CMD_SOLID;
|
||||
cmd_offset += 1u;
|
||||
let stroke = CmdStroke(tile.segments, 0.5 * linewidth);
|
||||
ptcl[cmd_offset] = CMD_STROKE;
|
||||
ptcl[cmd_offset + 1u] = stroke.tile;
|
||||
ptcl[cmd_offset + 2u] = bitcast<u32>(stroke.half_width);
|
||||
cmd_offset += 3u;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,15 +298,14 @@ fn main(
|
|||
let drawtag = scene[config.drawtag_base + drawobj_ix];
|
||||
let dm = draw_monoids[drawobj_ix];
|
||||
let dd = config.drawdata_base + dm.scene_offset;
|
||||
// TODO: set up draw info from monoid
|
||||
let di = dm.info_offset;
|
||||
if clip_zero_depth == 0u {
|
||||
let tile_ix = sh_tile_base[el_ix] + sh_tile_stride[el_ix] * tile_y + tile_x;
|
||||
let tile = tiles[tile_ix];
|
||||
switch drawtag {
|
||||
// DRAWTAG_FILL_COLOR
|
||||
case 0x44u: {
|
||||
// TODO: get linewidth from draw object
|
||||
let linewidth = -1.0;
|
||||
let linewidth = bitcast<f32>(info[di]);
|
||||
let rgba_color = scene[dd];
|
||||
write_path(tile, linewidth);
|
||||
write_color(CmdColor(rgba_color));
|
||||
|
|
|
@ -51,6 +51,12 @@ fn read_fill(cmd_ix: u32) -> CmdFill {
|
|||
return CmdFill(tile, backdrop);
|
||||
}
|
||||
|
||||
fn read_stroke(cmd_ix: u32) -> CmdStroke {
|
||||
let tile = ptcl[cmd_ix + 1u];
|
||||
let half_width = bitcast<f32>(ptcl[cmd_ix + 2u]);
|
||||
return CmdStroke(tile, half_width);
|
||||
}
|
||||
|
||||
fn read_color(cmd_ix: u32) -> CmdColor {
|
||||
let rgba_color = ptcl[cmd_ix + 1u];
|
||||
return CmdColor(rgba_color);
|
||||
|
@ -105,6 +111,32 @@ fn fill_path(tile: Tile, xy: vec2<f32>) -> array<f32, PIXELS_PER_THREAD> {
|
|||
return area;
|
||||
}
|
||||
|
||||
fn stroke_path(seg: u32, half_width: f32, xy: vec2<f32>) -> array<f32, PIXELS_PER_THREAD> {
|
||||
var df: array<f32, PIXELS_PER_THREAD>;
|
||||
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
|
||||
df[i] = 1e9;
|
||||
}
|
||||
var segment_ix = seg;
|
||||
while segment_ix != 0u {
|
||||
let segment = segments[segment_ix];
|
||||
let delta = segment.delta;
|
||||
let dpos0 = xy + vec2<f32>(0.5, 0.5) - segment.origin;
|
||||
let scale = 1.0 / dot(delta, delta);
|
||||
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
|
||||
let dpos = vec2<f32>(dpos0.x + f32(i), dpos0.y);
|
||||
let t = clamp(dot(dpos, delta) * scale, 0.0, 1.0);
|
||||
// performance idea: hoist sqrt out of loop
|
||||
df[i] = min(df[i], length(delta * t - dpos));
|
||||
}
|
||||
segment_ix = segment.next;
|
||||
}
|
||||
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
|
||||
// reuse array; return alpha rather than distance
|
||||
df[i] = clamp(half_width + 0.5 - df[i], 0.0, 1.0);
|
||||
}
|
||||
return df;
|
||||
}
|
||||
|
||||
@compute @workgroup_size(4, 16)
|
||||
fn main(
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>,
|
||||
|
@ -132,6 +164,12 @@ fn main(
|
|||
area = fill_path(tile, xy);
|
||||
cmd_ix += 3u;
|
||||
}
|
||||
// CMD_STROKE
|
||||
case 2u: {
|
||||
let stroke = read_stroke(cmd_ix);
|
||||
area = stroke_path(stroke.tile, stroke.half_width, xy);
|
||||
cmd_ix += 3u;
|
||||
}
|
||||
// CMD_SOLID
|
||||
case 3u: {
|
||||
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
|
||||
|
|
|
@ -143,8 +143,10 @@ fn main(
|
|||
var tag_byte = (tag_word >> shift) & 0xffu;
|
||||
|
||||
let out = &path_bboxes[tm.path_ix];
|
||||
var linewidth: f32;
|
||||
if (tag_byte & PATH_TAG_PATH) != 0u {
|
||||
(*out).linewidth = -1.0; // TODO: plumb linewidth
|
||||
linewidth = bitcast<f32>(scene[config.linewidth_base + tm.linewidth_ix]);
|
||||
(*out).linewidth = linewidth;
|
||||
(*out).trans_ix = tm.trans_ix;
|
||||
}
|
||||
// Decode path data
|
||||
|
@ -195,6 +197,13 @@ fn main(
|
|||
p1 = mix(p1, p0, 1.0 / 3.0);
|
||||
}
|
||||
}
|
||||
if linewidth >= 0.0 {
|
||||
// See https://www.iquilezles.org/www/articles/ellipses/ellipses.htm
|
||||
// This is the correct bounding box, but we're not handling rendering
|
||||
// in the isotropic case, so it may mismatch.
|
||||
let stroke = 0.5 * linewidth * vec2<f32>(length(transform.matrx.xz), length(transform.matrx.yw));
|
||||
bbox += vec4<f32>(-stroke, stroke);
|
||||
}
|
||||
cubics[global_id.x] = Cubic(p0, p1, p2, p3, tm.path_ix, 0u);
|
||||
// Update bounding box using atomics only. Computing a monoid is a
|
||||
// potential future optimization.
|
||||
|
|
|
@ -30,6 +30,7 @@ struct Config {
|
|||
drawdata_base: u32,
|
||||
|
||||
transform_base: u32,
|
||||
linewidth_base: u32,
|
||||
}
|
||||
|
||||
// Geometry of tiles and bins
|
||||
|
|
|
@ -25,6 +25,7 @@ let PTCL_HEADROOM = 2u;
|
|||
// Tags for PTCL commands
|
||||
let CMD_END = 0u;
|
||||
let CMD_FILL = 1u;
|
||||
let CMD_STROKE = 2u;
|
||||
let CMD_SOLID = 3u;
|
||||
let CMD_COLOR = 5u;
|
||||
let CMD_JUMP = 11u;
|
||||
|
@ -37,6 +38,11 @@ struct CmdFill {
|
|||
backdrop: i32,
|
||||
}
|
||||
|
||||
struct CmdStroke {
|
||||
tile: u32,
|
||||
half_width: f32,
|
||||
}
|
||||
|
||||
struct CmdJump {
|
||||
new_ix: u32,
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ struct Config {
|
|||
drawtag_base: u32,
|
||||
drawdata_base: u32,
|
||||
transform_base: u32,
|
||||
linewidth_base: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -137,6 +138,8 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy
|
|||
scene.extend(&data.drawdata_stream);
|
||||
let transform_base = size_to_words(scene.len());
|
||||
scene.extend(bytemuck::cast_slice(&data.transform_stream));
|
||||
let linewidth_base = size_to_words(scene.len());
|
||||
scene.extend(bytemuck::cast_slice(&data.linewidth_stream));
|
||||
|
||||
let n_path = data.n_path;
|
||||
// TODO: calculate for real when we do rectangles
|
||||
|
@ -151,6 +154,7 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy
|
|||
drawtag_base,
|
||||
drawdata_base,
|
||||
transform_base,
|
||||
linewidth_base,
|
||||
};
|
||||
println!("{:?}", config);
|
||||
let scene_buf = recording.upload(scene);
|
||||
|
@ -284,6 +288,7 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy
|
|||
bin_data_buf,
|
||||
path_buf,
|
||||
tile_buf,
|
||||
info_buf,
|
||||
bump_buf,
|
||||
ptcl_buf,
|
||||
],
|
||||
|
|
|
@ -241,6 +241,7 @@ pub fn full_shaders(device: &Device, engine: &mut Engine) -> Result<FullShaders,
|
|||
BindType::BufReadOnly,
|
||||
BindType::BufReadOnly,
|
||||
BindType::BufReadOnly,
|
||||
BindType::BufReadOnly,
|
||||
BindType::Buffer,
|
||||
BindType::Buffer,
|
||||
],
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
//
|
||||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
use piet_scene::{Affine, Brush, Color, Fill, PathElement, Point, Scene, SceneBuilder};
|
||||
use piet_scene::{Affine, Brush, Color, Fill, PathElement, Point, Scene, SceneBuilder, Stroke};
|
||||
|
||||
pub fn gen_test_scene() -> Scene {
|
||||
let mut scene = Scene::default();
|
||||
|
@ -32,6 +32,19 @@ pub fn gen_test_scene() -> Scene {
|
|||
let transform = Affine::translate(50.0, 50.0);
|
||||
let brush = Brush::Solid(Color::rgba8(0xff, 0xff, 0x00, 0x80));
|
||||
builder.fill(Fill::NonZero, transform, &brush, None, &path);
|
||||
let transform = Affine::translate(100.0, 100.0);
|
||||
let style = Stroke {
|
||||
width: 1.0,
|
||||
join: piet_scene::Join::Round,
|
||||
miter_limit: 1.4,
|
||||
start_cap: piet_scene::Cap::Round,
|
||||
end_cap: piet_scene::Cap::Round,
|
||||
dash_pattern: [],
|
||||
dash_offset: 0.0,
|
||||
scale: true,
|
||||
};
|
||||
let brush = Brush::Solid(Color::rgb8(0xa0, 0x00, 0x00));
|
||||
builder.stroke(&style, transform, &brush, None, &path);
|
||||
scene
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue