diff --git a/Cargo.toml b/Cargo.toml index 78c5824..3b56fbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ + "crates/encoding", "crates/shaders", "integrations/vello_svg", @@ -40,17 +41,20 @@ hot_reload = [] buffer_labels = [] [dependencies] +bytemuck = { workspace = true } +fello = { workspace = true } +peniko = { workspace = true } wgpu = { workspace = true } raw-window-handle = "0.5" futures-intrusive = "0.5.0" parking_lot = "0.12" -bytemuck = { version = "1.12.1", features = ["derive"] } smallvec = "1.8.0" -fello = { git = "https://github.com/dfrg/fount", rev = "58a284eaae67512fb61cf76177c5d33238d79cb1" } -peniko = { git = "https://github.com/linebender/peniko", rev = "cafdac9a211a0fb2fec5656bd663d1ac770bcc81" } -guillotiere = "0.6.2" +vello_encoding = { path = "crates/encoding" } [workspace.dependencies] +bytemuck = { version = "1.12.1", features = ["derive"] } +fello = { git = "https://github.com/dfrg/fount", rev = "58a284eaae67512fb61cf76177c5d33238d79cb1" } +peniko = { git = "https://github.com/linebender/peniko", rev = "cafdac9a211a0fb2fec5656bd663d1ac770bcc81" } wgpu = "0.15" # Used for examples diff --git a/crates/encoding/Cargo.toml b/crates/encoding/Cargo.toml new file mode 100644 index 0000000..f11c4bc --- /dev/null +++ b/crates/encoding/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "vello_encoding" +version = "0.1.0" +edition = "2021" + +[dependencies] +bytemuck = { workspace = true } +fello = { workspace = true } +peniko = { workspace = true } +guillotiere = "0.6.2" diff --git a/crates/encoding/src/binning.rs b/crates/encoding/src/binning.rs new file mode 100644 index 0000000..bd76fb8 --- /dev/null +++ b/crates/encoding/src/binning.rs @@ -0,0 +1,12 @@ +// Copyright 2023 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use bytemuck::{Pod, Zeroable}; + +/// Binning header. +#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] +#[repr(C)] +pub struct BinHeader { + pub element_count: u32, + pub chunk_offset: u32, +} diff --git a/crates/encoding/src/clip.rs b/crates/encoding/src/clip.rs new file mode 100644 index 0000000..5a4e8f0 --- /dev/null +++ b/crates/encoding/src/clip.rs @@ -0,0 +1,43 @@ +// Copyright 2023 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use bytemuck::{Pod, Zeroable}; + +/// Clip stack element. +#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] +#[repr(C)] +pub struct ClipBic { + pub a: u32, + pub b: u32, +} + +/// Clip element. +#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] +#[repr(C)] +pub struct ClipElement { + pub parent_ix: u32, + _padding: [u8; 12], + pub bbox: [f32; 4], +} + +/// Clip resolution. +/// +/// This is an intermediate element used to match clips to associated paths +/// and is also used to connect begin and end clip pairs. +#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] +#[repr(C)] +pub struct Clip { + // Index of the draw object. + pub ix: u32, + /// This is a packed encoding of an enum with the sign bit as the tag. If positive, + /// this entry is a BeginClip and contains the associated path index. If negative, + /// it is an EndClip and contains the bitwise-not of the EndClip draw object index. + pub path_ix: i32, +} + +/// Clip bounding box. +#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] +#[repr(C)] +pub struct ClipBbox { + pub bbox: [f32; 4], +} diff --git a/crates/encoding/src/config.rs b/crates/encoding/src/config.rs new file mode 100644 index 0000000..ddd7e2d --- /dev/null +++ b/crates/encoding/src/config.rs @@ -0,0 +1,327 @@ +// Copyright 2023 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use super::{ + BinHeader, Clip, ClipBbox, ClipBic, ClipElement, Cubic, DrawBbox, DrawMonoid, Layout, Path, + PathBbox, PathMonoid, PathSegment, Tile, +}; +use bytemuck::{Pod, Zeroable}; +use std::mem; + +const TILE_WIDTH: u32 = 16; +const TILE_HEIGHT: u32 = 16; + +// TODO: Obtain these from the vello_shaders crate +pub(crate) const PATH_REDUCE_WG: u32 = 256; +const PATH_BBOX_WG: u32 = 256; +const PATH_COARSE_WG: u32 = 256; +const CLIP_REDUCE_WG: u32 = 256; + +/// Counters for tracking dynamic allocation on the GPU. +/// +/// This must be kept in sync with the struct in shader/shared/bump.wgsl +#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] +#[repr(C)] +pub struct BumpAllocators { + pub failed: u32, + // Final needed dynamic size of the buffers. If any of these are larger + // than the corresponding `_size` element reallocation needs to occur. + pub binning: u32, + pub ptcl: u32, + pub tile: u32, + pub segments: u32, + pub blend: u32, +} + +/// Uniform render configuration data used by all GPU stages. +/// +/// This data structure must be kept in sync with the definition in +/// shaders/shared/config.wgsl. +#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] +#[repr(C)] +pub struct ConfigUniform { + /// Width of the scene in tiles. + pub width_in_tiles: u32, + /// Height of the scene in tiles. + pub height_in_tiles: u32, + /// Width of the target in pixels. + pub target_width: u32, + /// Height of the target in pixels. + pub target_height: u32, + /// The base background color applied to the target before any blends. + pub base_color: u32, + /// Layout of packed scene data. + pub layout: Layout, + /// Size of binning buffer allocation (in u32s). + pub binning_size: u32, + /// Size of tile buffer allocation (in Tiles). + pub tiles_size: u32, + /// Size of segment buffer allocation (in PathSegments). + pub segments_size: u32, + /// Size of per-tile command list buffer allocation (in u32s). + pub ptcl_size: u32, +} + +/// CPU side setup and configuration. +#[derive(Default)] +pub struct RenderConfig { + /// GPU side configuration. + pub gpu: ConfigUniform, + /// Workgroup counts for all compute pipelines. + pub workgroup_counts: WorkgroupCounts, + /// Sizes of all buffer resources. + pub buffer_sizes: BufferSizes, +} + +impl RenderConfig { + pub fn new(layout: &Layout, width: u32, height: u32, base_color: &peniko::Color) -> Self { + let new_width = next_multiple_of(width, TILE_WIDTH); + let new_height = next_multiple_of(height, TILE_HEIGHT); + let width_in_tiles = new_width / TILE_WIDTH; + let height_in_tiles = new_height / TILE_HEIGHT; + let n_path_tags = layout.path_tags_size(); + let workgroup_counts = + WorkgroupCounts::new(layout, width_in_tiles, height_in_tiles, n_path_tags); + let buffer_sizes = BufferSizes::new(layout, &workgroup_counts, n_path_tags); + Self { + gpu: ConfigUniform { + width_in_tiles, + height_in_tiles, + target_width: width, + target_height: height, + base_color: base_color.to_premul_u32(), + binning_size: buffer_sizes.bin_data.len() - layout.bin_data_start, + tiles_size: buffer_sizes.tiles.len(), + segments_size: buffer_sizes.segments.len(), + ptcl_size: buffer_sizes.ptcl.len(), + layout: *layout, + }, + workgroup_counts, + buffer_sizes, + } + } +} + +/// Type alias for a workgroup size. +pub type WorkgroupSize = (u32, u32, u32); + +/// Computed sizes for all dispatches. +#[derive(Copy, Clone, Debug, Default)] +pub struct WorkgroupCounts { + pub use_large_path_scan: bool, + pub path_reduce: WorkgroupSize, + pub path_reduce2: WorkgroupSize, + pub path_scan1: WorkgroupSize, + pub path_scan: WorkgroupSize, + pub bbox_clear: WorkgroupSize, + pub path_seg: WorkgroupSize, + pub draw_reduce: WorkgroupSize, + pub draw_leaf: WorkgroupSize, + pub clip_reduce: WorkgroupSize, + pub clip_leaf: WorkgroupSize, + pub binning: WorkgroupSize, + pub tile_alloc: WorkgroupSize, + pub path_coarse: WorkgroupSize, + pub backdrop: WorkgroupSize, + pub coarse: WorkgroupSize, + pub fine: WorkgroupSize, +} + +impl WorkgroupCounts { + pub fn new( + layout: &Layout, + width_in_tiles: u32, + height_in_tiles: u32, + n_path_tags: u32, + ) -> Self { + let n_paths = layout.n_paths; + let n_draw_objects = layout.n_draw_objects; + let n_clips = layout.n_clips; + let path_tag_padded = align_up(n_path_tags, 4 * PATH_REDUCE_WG); + let path_tag_wgs = path_tag_padded / (4 * PATH_REDUCE_WG); + let use_large_path_scan = path_tag_wgs > PATH_REDUCE_WG; + let reduced_size = if use_large_path_scan { + align_up(path_tag_wgs, PATH_REDUCE_WG) + } else { + path_tag_wgs + }; + let draw_object_wgs = (n_draw_objects + PATH_BBOX_WG - 1) / PATH_BBOX_WG; + let path_coarse_wgs = (n_path_tags + PATH_COARSE_WG - 1) / PATH_COARSE_WG; + let clip_reduce_wgs = n_clips.saturating_sub(1) / CLIP_REDUCE_WG; + let clip_wgs = (n_clips + CLIP_REDUCE_WG - 1) / CLIP_REDUCE_WG; + let path_wgs = (n_paths + PATH_BBOX_WG - 1) / PATH_BBOX_WG; + let width_in_bins = (width_in_tiles + 15) / 16; + let height_in_bins = (height_in_tiles + 15) / 16; + Self { + use_large_path_scan, + path_reduce: (path_tag_wgs, 1, 1), + path_reduce2: (PATH_REDUCE_WG, 1, 1), + path_scan1: (reduced_size / PATH_REDUCE_WG, 1, 1), + path_scan: (path_tag_wgs, 1, 1), + bbox_clear: (draw_object_wgs, 1, 1), + path_seg: (path_coarse_wgs, 1, 1), + draw_reduce: (draw_object_wgs, 1, 1), + draw_leaf: (draw_object_wgs, 1, 1), + clip_reduce: (clip_reduce_wgs, 1, 1), + clip_leaf: (clip_wgs, 1, 1), + binning: (draw_object_wgs, 1, 1), + tile_alloc: (path_wgs, 1, 1), + path_coarse: (path_coarse_wgs, 1, 1), + backdrop: (path_wgs, 1, 1), + coarse: (width_in_bins, height_in_bins, 1), + fine: (width_in_tiles, height_in_tiles, 1), + } + } +} + +/// Typed buffer size primitive. +#[derive(Copy, Clone, Eq, Default, Debug)] +pub struct BufferSize { + len: u32, + _phantom: std::marker::PhantomData, +} + +impl BufferSize { + /// Creates a new buffer size from number of elements. + pub const fn new(len: u32) -> Self { + Self { + len, + _phantom: std::marker::PhantomData, + } + } + + /// Creates a new buffer size from size in bytes. + pub const fn from_size_in_bytes(size: u32) -> Self { + Self::new(size / mem::size_of::() as u32) + } + + /// Returns the number of elements. + pub const fn len(self) -> u32 { + self.len + } + + /// Returns the size in bytes. + pub const fn size_in_bytes(self) -> u32 { + mem::size_of::() as u32 * self.len + } + + /// Returns the size in bytes aligned up to the given value. + pub const fn aligned_in_bytes(self, alignment: u32) -> u32 { + align_up(self.size_in_bytes(), alignment) + } +} + +impl PartialEq for BufferSize { + fn eq(&self, other: &Self) -> bool { + self.len == other.len + } +} + +impl PartialOrd for BufferSize { + fn partial_cmp(&self, other: &Self) -> Option { + self.len.partial_cmp(&other.len) + } +} + +/// Computed sizes for all buffers. +#[derive(Copy, Clone, Debug, Default)] +pub struct BufferSizes { + // Known size buffers + pub path_reduced: BufferSize, + pub path_reduced2: BufferSize, + pub path_reduced_scan: BufferSize, + pub path_monoids: BufferSize, + pub path_bboxes: BufferSize, + pub cubics: BufferSize, + pub draw_reduced: BufferSize, + pub draw_monoids: BufferSize, + pub info: BufferSize, + pub clip_inps: BufferSize, + pub clip_els: BufferSize, + pub clip_bics: BufferSize, + pub clip_bboxes: BufferSize, + pub draw_bboxes: BufferSize, + pub bump_alloc: BufferSize, + pub bin_headers: BufferSize, + pub paths: BufferSize, + // Bump allocated buffers + pub bin_data: BufferSize, + pub tiles: BufferSize, + pub segments: BufferSize, + pub ptcl: BufferSize, +} + +impl BufferSizes { + pub fn new(layout: &Layout, workgroups: &WorkgroupCounts, n_path_tags: u32) -> Self { + let n_paths = layout.n_paths; + let n_draw_objects = layout.n_draw_objects; + let n_clips = layout.n_clips; + let path_tag_wgs = workgroups.path_reduce.0; + let reduced_size = if workgroups.use_large_path_scan { + align_up(path_tag_wgs, PATH_REDUCE_WG) + } else { + path_tag_wgs + }; + let path_reduced = BufferSize::new(reduced_size); + let path_reduced2 = BufferSize::new(PATH_REDUCE_WG); + let path_reduced_scan = BufferSize::new(path_tag_wgs); + let path_monoids = BufferSize::new(path_tag_wgs * PATH_REDUCE_WG); + let path_bboxes = BufferSize::new(n_paths); + let cubics = BufferSize::new(n_path_tags); + let draw_object_wgs = workgroups.draw_reduce.0; + let draw_reduced = BufferSize::new(draw_object_wgs); + let draw_monoids = BufferSize::new(n_draw_objects); + let info = BufferSize::new(layout.bin_data_start); + let clip_inps = BufferSize::new(n_clips); + let clip_els = BufferSize::new(n_clips); + let clip_bics = BufferSize::new(n_clips / CLIP_REDUCE_WG); + let clip_bboxes = BufferSize::new(n_clips); + let draw_bboxes = BufferSize::new(n_paths); + let bump_alloc = BufferSize::new(1); + let bin_headers = BufferSize::new(draw_object_wgs * 256); + let n_paths_aligned = align_up(n_paths, 256); + let paths = BufferSize::new(n_paths_aligned); + + // The following buffer sizes have been hand picked to accommodate the vello test scenes as + // well as paris-30k. These should instead get derived from the scene layout using + // reasonable heuristics. + let bin_data = BufferSize::new(1 << 18); + let tiles = BufferSize::new(1 << 21); + let segments = BufferSize::new(1 << 21); + let ptcl = BufferSize::new(1 << 23); + Self { + path_reduced, + path_reduced2, + path_reduced_scan, + path_monoids, + path_bboxes, + cubics, + draw_reduced, + draw_monoids, + info, + clip_inps, + clip_els, + clip_bics, + clip_bboxes, + draw_bboxes, + bump_alloc, + bin_headers, + paths, + bin_data, + tiles, + segments, + ptcl, + } + } +} + +const fn align_up(len: u32, alignment: u32) -> u32 { + len + (len.wrapping_neg() & (alignment - 1)) +} + +const fn next_multiple_of(val: u32, rhs: u32) -> u32 { + match val % rhs { + 0 => val, + r => val + (rhs - r), + } +} diff --git a/src/encoding/draw.rs b/crates/encoding/src/draw.rs similarity index 83% rename from src/encoding/draw.rs rename to crates/encoding/src/draw.rs index 64fdd16..e26dacd 100644 --- a/src/encoding/draw.rs +++ b/crates/encoding/src/draw.rs @@ -1,18 +1,5 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. +// Copyright 2022 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT use bytemuck::{Pod, Zeroable}; use peniko::{BlendMode, Color}; @@ -54,6 +41,13 @@ impl DrawTag { } } +/// Draw object bounding box. +#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] +#[repr(C)] +pub struct DrawBbox { + pub bbox: [f32; 4], +} + /// Draw data for a solid color. #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] #[repr(C)] @@ -131,7 +125,7 @@ impl DrawBeginClip { } /// Monoid for the draw tag stream. -#[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable, Default)] +#[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable, Default, Debug)] #[repr(C)] pub struct DrawMonoid { // The number of paths preceding this draw object. diff --git a/src/encoding/encoding.rs b/crates/encoding/src/encoding.rs similarity index 94% rename from src/encoding/encoding.rs rename to crates/encoding/src/encoding.rs index 03ca730..65dcbbb 100644 --- a/src/encoding/encoding.rs +++ b/crates/encoding/src/encoding.rs @@ -1,24 +1,9 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. - -use crate::encoding::DrawImage; +// Copyright 2022 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT use super::{ - resolve::Patch, DrawColor, DrawLinearGradient, DrawRadialGradient, DrawTag, Glyph, GlyphRun, - PathEncoder, PathTag, Transform, + resolve::Patch, DrawColor, DrawImage, DrawLinearGradient, DrawRadialGradient, DrawTag, Glyph, + GlyphRun, PathEncoder, PathTag, Transform, }; use fello::NormalizedCoord; @@ -170,9 +155,7 @@ impl Encoding { linewidths: self.linewidths.len(), } } -} -impl Encoding { /// Encodes a linewidth. pub fn encode_linewidth(&mut self, linewidth: f32) { if self.linewidths.last() != Some(&linewidth) { diff --git a/src/encoding/glyph.rs b/crates/encoding/src/glyph.rs similarity index 63% rename from src/encoding/glyph.rs rename to crates/encoding/src/glyph.rs index 6fe900e..116f02d 100644 --- a/src/encoding/glyph.rs +++ b/crates/encoding/src/glyph.rs @@ -1,18 +1,5 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. +// Copyright 2023 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT use std::ops::Range; diff --git a/src/encoding/glyph_cache.rs b/crates/encoding/src/glyph_cache.rs similarity index 77% rename from src/encoding/glyph_cache.rs rename to crates/encoding/src/glyph_cache.rs index f919afb..b1ff693 100644 --- a/src/encoding/glyph_cache.rs +++ b/crates/encoding/src/glyph_cache.rs @@ -1,18 +1,5 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. +// Copyright 2022 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT use std::collections::HashMap; @@ -59,11 +46,11 @@ impl GlyphCache { Style::Fill(Fill::EvenOdd) => encoding_cache.encode_linewidth(-2.0), Style::Stroke(stroke) => encoding_cache.encode_linewidth(stroke.width), } - let mut path = crate::glyph::PathEncoderPen(encoding_cache.encode_path(is_fill)); + let mut path = encoding_cache.encode_path(is_fill); scaler .outline(GlyphId::new(key.glyph_id as u16), &mut path) .ok()?; - if path.0.finish(false) == 0 { + if path.finish(false) == 0 { return None; } let end = encoding_cache.stream_offsets(); diff --git a/src/encoding/image_cache.rs b/crates/encoding/src/image_cache.rs similarity index 76% rename from src/encoding/image_cache.rs rename to crates/encoding/src/image_cache.rs index cd2734e..85b658e 100644 --- a/src/encoding/image_cache.rs +++ b/crates/encoding/src/image_cache.rs @@ -1,18 +1,5 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. +// Copyright 2022 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT use guillotiere::{size2, AtlasAllocator}; use peniko::Image; diff --git a/crates/encoding/src/lib.rs b/crates/encoding/src/lib.rs new file mode 100644 index 0000000..38da4a6 --- /dev/null +++ b/crates/encoding/src/lib.rs @@ -0,0 +1,38 @@ +// Copyright 2023 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Raw scene encoding. + +mod binning; +mod clip; +mod config; +mod draw; +mod encoding; +mod glyph; +mod glyph_cache; +mod image_cache; +mod math; +mod monoid; +mod path; +mod ramp_cache; +mod resolve; + +pub use binning::BinHeader; +pub use clip::{Clip, ClipBbox, ClipBic, ClipElement}; +pub use config::{ + BufferSize, BufferSizes, BumpAllocators, ConfigUniform, RenderConfig, WorkgroupCounts, + WorkgroupSize, +}; +pub use draw::{ + DrawBbox, DrawBeginClip, DrawColor, DrawImage, DrawLinearGradient, DrawMonoid, + DrawRadialGradient, DrawTag, +}; +pub use encoding::{Encoding, StreamOffsets}; +pub use glyph::{Glyph, GlyphRun}; +pub use math::Transform; +pub use monoid::Monoid; +pub use path::{ + Cubic, Path, PathBbox, PathEncoder, PathMonoid, PathSegment, PathSegmentType, PathTag, Tile, +}; +pub use ramp_cache::Ramps; +pub use resolve::{Layout, Patch, Resolver}; diff --git a/src/encoding/math.rs b/crates/encoding/src/math.rs similarity index 76% rename from src/encoding/math.rs rename to crates/encoding/src/math.rs index b0afbb9..9ab9935 100644 --- a/src/encoding/math.rs +++ b/crates/encoding/src/math.rs @@ -1,18 +1,5 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. +// Copyright 2022 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT use std::ops::Mul; diff --git a/crates/encoding/src/monoid.rs b/crates/encoding/src/monoid.rs new file mode 100644 index 0000000..29768b1 --- /dev/null +++ b/crates/encoding/src/monoid.rs @@ -0,0 +1,15 @@ +// Copyright 2022 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/// Interface for a monoid. The default value must be the identity of +/// the monoid. +pub trait Monoid: Default { + /// The source value for constructing the monoid. + type SourceValue; + + /// Creates a monoid from a given value. + fn new(value: Self::SourceValue) -> Self; + + /// Combines two monoids. This operation must be associative. + fn combine(&self, other: &Self) -> Self; +} diff --git a/src/encoding/path.rs b/crates/encoding/src/path.rs similarity index 88% rename from src/encoding/path.rs rename to crates/encoding/src/path.rs index 760eb32..6c3cc5f 100644 --- a/src/encoding/path.rs +++ b/crates/encoding/src/path.rs @@ -1,18 +1,5 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. +// Copyright 2022 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT use bytemuck::{Pod, Zeroable}; use peniko::kurbo::Shape; @@ -20,7 +7,7 @@ use peniko::kurbo::Shape; use super::Monoid; /// Path segment. -#[derive(Clone, Copy, Debug, Zeroable, Pod)] +#[derive(Clone, Copy, Debug, Zeroable, Pod, Default)] #[repr(C)] pub struct PathSegment { pub origin: [f32; 2], @@ -170,6 +157,19 @@ impl Monoid for PathMonoid { } } +/// Cubic path segment. +#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] +#[repr(C)] +pub struct Cubic { + pub p0: [f32; 2], + pub p1: [f32; 2], + pub p2: [f32; 2], + pub p3: [f32; 2], + pub stroke: [f32; 2], + pub path_ix: u32, + pub flags: u32, +} + /// Path bounding box. #[derive(Copy, Clone, Pod, Zeroable, Default, Debug)] #[repr(C)] @@ -188,6 +188,27 @@ pub struct PathBbox { pub trans_ix: u32, } +/// Tiled path object. +#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] +#[repr(C)] +pub struct Path { + /// Bounding box in tiles. + pub bbox: [f32; 4], + /// Offset (in u32s) to tile rectangle. + pub tiles: u32, + _padding: [u32; 3], +} + +/// Tile object. +#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)] +#[repr(C)] +pub struct Tile { + /// Accumulated backdrop at the left edge of the tile. + pub backdrop: i32, + /// Index of first path segment. + pub segments: u32, +} + /// Encoder for path segments. pub struct PathEncoder<'a> { tags: &'a mut Vec, @@ -381,3 +402,25 @@ impl<'a> PathEncoder<'a> { self.n_encoded_segments } } + +impl fello::scale::Pen for PathEncoder<'_> { + fn move_to(&mut self, x: f32, y: f32) { + self.move_to(x, y) + } + + fn line_to(&mut self, x: f32, y: f32) { + self.line_to(x, y) + } + + fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) { + self.quad_to(cx0, cy0, x, y) + } + + fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) { + self.cubic_to(cx0, cy0, cx1, cy1, x, y) + } + + fn close(&mut self) { + self.close() + } +} diff --git a/src/encoding/ramp_cache.rs b/crates/encoding/src/ramp_cache.rs similarity index 86% rename from src/encoding/ramp_cache.rs rename to crates/encoding/src/ramp_cache.rs index 96f5c49..8b0eaa7 100644 --- a/src/encoding/ramp_cache.rs +++ b/crates/encoding/src/ramp_cache.rs @@ -1,18 +1,5 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. +// Copyright 2022 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT use std::collections::HashMap; diff --git a/src/encoding/resolve.rs b/crates/encoding/src/resolve.rs similarity index 91% rename from src/encoding/resolve.rs rename to crates/encoding/src/resolve.rs index 0077b5c..0f72939 100644 --- a/src/encoding/resolve.rs +++ b/crates/encoding/src/resolve.rs @@ -1,18 +1,5 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. +// Copyright 2022 The Vello authors +// SPDX-License-Identifier: Apache-2.0 OR MIT use std::ops::Range; @@ -25,7 +12,6 @@ use super::{ ramp_cache::{RampCache, Ramps}, DrawTag, Encoding, PathTag, StreamOffsets, Transform, }; -use crate::shaders; /// Layout of a packed encoding. #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] @@ -66,6 +52,12 @@ impl Layout { bytemuck::cast_slice(&data[start..end]) } + pub fn path_tags_size(&self) -> u32 { + let start = self.path_tag_base * 4; + let end = self.path_data_base * 4; + end - start + } + /// Returns the path tag stream in chunks of 4. pub fn path_tags_chunked<'a>(&self, data: &'a [u8]) -> &'a [u32] { let start = self.path_tag_base as usize * 4; @@ -108,35 +100,6 @@ impl Layout { } } -/// Scene configuration. -/// -/// This data structure must be kept in sync with the definition in -/// shaders/shared/config.wgsl. -#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] -#[repr(C)] -pub struct Config { - /// Width of the scene in tiles. - pub width_in_tiles: u32, - /// Height of the scene in tiles. - pub height_in_tiles: u32, - /// Width of the target in pixels. - pub target_width: u32, - /// Height of the target in pixels. - pub target_height: u32, - /// The base background color applied to the target before any blends. - pub base_color: u32, - /// Layout of packed scene data. - pub layout: Layout, - /// Size of binning buffer allocation (in u32s). - pub binning_size: u32, - /// Size of tile buffer allocation (in Tiles). - pub tiles_size: u32, - /// Size of segment buffer allocation (in PathSegments). - pub segments_size: u32, - /// Size of per-tile command list buffer allocation (in u32s). - pub ptcl_size: u32, -} - /// Resolver for late bound resources. #[derive(Default)] pub struct Resolver { @@ -166,13 +129,15 @@ impl Resolver { self.resolve_pending_images(); let data = packed; data.clear(); - let mut layout = Layout::default(); - layout.n_paths = encoding.n_paths; - layout.n_clips = encoding.n_clips; + let mut layout = Layout { + n_paths: encoding.n_paths, + n_clips: encoding.n_clips, + ..Layout::default() + }; // Compute size of data buffer let n_path_tags = encoding.path_tags.len() + sizes.path_tags + encoding.n_open_clips as usize; - let path_tag_padded = align_up(n_path_tags, 4 * shaders::PATHTAG_REDUCE_WG); + let path_tag_padded = align_up(n_path_tags, 4 * crate::config::PATH_REDUCE_WG); let capacity = path_tag_padded + slice_size_in_bytes(&encoding.path_data, sizes.path_data) + slice_size_in_bytes( diff --git a/examples/scenes/src/simple_text.rs b/examples/scenes/src/simple_text.rs index c6371af..717d0f1 100644 --- a/examples/scenes/src/simple_text.rs +++ b/examples/scenes/src/simple_text.rs @@ -17,10 +17,9 @@ use std::sync::Arc; use vello::{ - encoding::Glyph, fello::meta::MetadataProvider, fello::raw::FontRef, - glyph::GlyphContext, + glyph::{Glyph, GlyphContext}, kurbo::Affine, peniko::{Blob, Brush, BrushRef, Font, StyleRef}, SceneBuilder, diff --git a/src/encoding.rs b/src/encoding.rs deleted file mode 100644 index 2925256..0000000 --- a/src/encoding.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. - -//! Raw scene encoding. - -mod draw; -mod encoding; -mod glyph; -mod glyph_cache; -mod image_cache; -mod math; -mod monoid; -mod path; -mod ramp_cache; -mod resolve; - -pub use draw::{ - DrawBeginClip, DrawColor, DrawImage, DrawLinearGradient, DrawMonoid, DrawRadialGradient, - DrawTag, -}; -pub use encoding::{Encoding, StreamOffsets}; -pub use glyph::{Glyph, GlyphRun}; -pub use math::Transform; -pub use monoid::Monoid; -pub use path::{PathBbox, PathEncoder, PathMonoid, PathSegment, PathSegmentType, PathTag}; -pub use ramp_cache::Ramps; -pub use resolve::{Config, Layout, Patch, Resolver}; diff --git a/src/encoding/monoid.rs b/src/encoding/monoid.rs deleted file mode 100644 index 37bca92..0000000 --- a/src/encoding/monoid.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. - -/// Interface for a monoid. The default value must be the identity of -/// the monoid. -pub trait Monoid: Default { - /// The source value for constructing the monoid. - type SourceValue; - - /// Creates a monoid from a given value. - fn new(value: Self::SourceValue) -> Self; - - /// Combines two monoids. This operation must be associative. - fn combine(&self, other: &Self) -> Self; -} diff --git a/src/engine.rs b/src/engine.rs index 1e6253d..28772cf 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -330,7 +330,7 @@ impl Engine { let format = proxy.format.to_wgpu(); queue.write_texture( wgpu::ImageCopyTexture { - texture: &texture, + texture, mip_level: 0, origin: wgpu::Origin3d { x: *x, y: *y, z: 0 }, aspect: TextureAspect::All, diff --git a/src/glyph.rs b/src/glyph.rs index b398e6f..b388227 100644 --- a/src/glyph.rs +++ b/src/glyph.rs @@ -16,20 +16,21 @@ //! Support for glyph rendering. -use fello::scale::Pen; - -use crate::encoding::{Encoding, PathEncoder}; use crate::scene::{SceneBuilder, SceneFragment}; -use peniko::kurbo::Affine; -use peniko::{Brush, Color, Fill, Style}; - -use fello::{ - raw::types::GlyphId, - raw::FontRef, - scale::{Context, Scaler}, - FontKey, Setting, Size, +use { + fello::{ + raw::types::GlyphId, + raw::FontRef, + scale::{Context, Pen, Scaler}, + FontKey, Setting, Size, + }, + peniko::kurbo::Affine, + peniko::{Brush, Color, Fill, Style}, + vello_encoding::Encoding, }; +pub use vello_encoding::Glyph; + /// General context for creating scene fragments for glyph outlines. pub struct GlyphContext { ctx: Context, @@ -105,9 +106,9 @@ impl<'a> GlyphProvider<'a> { Style::Fill(Fill::EvenOdd) => encoding.encode_linewidth(-2.0), Style::Stroke(stroke) => encoding.encode_linewidth(stroke.width), } - let mut path = PathEncoderPen(encoding.encode_path(matches!(style, Style::Fill(_)))); + let mut path = encoding.encode_path(matches!(style, Style::Fill(_))); self.scaler.outline(GlyphId::new(gid), &mut path).ok()?; - if path.0.finish(false) != 0 { + if path.finish(false) != 0 { Some(()) } else { None @@ -144,27 +145,3 @@ impl Pen for BezPathPen { self.0.close_path() } } - -pub(crate) struct PathEncoderPen<'a>(pub PathEncoder<'a>); - -impl Pen for PathEncoderPen<'_> { - fn move_to(&mut self, x: f32, y: f32) { - self.0.move_to(x, y) - } - - fn line_to(&mut self, x: f32, y: f32) { - self.0.line_to(x, y) - } - - fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) { - self.0.quad_to(cx0, cy0, x, y) - } - - fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) { - self.0.cubic_to(cx0, cy0, cx1, cy1, x, y) - } - - fn close(&mut self) { - self.0.close() - } -} diff --git a/src/lib.rs b/src/lib.rs index d61e176..b0f35a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,8 +27,6 @@ pub use peniko::kurbo; #[doc(hidden)] pub use fello; -pub mod encoding; - pub mod glyph; pub mod util; @@ -138,7 +136,7 @@ impl Renderer { if target.width != width || target.height != height { target = TargetTexture::new(device, width, height); } - self.render_to_texture(device, queue, scene, &target.view, ¶ms)?; + self.render_to_texture(device, queue, scene, &target.view, params)?; let blit = self .blit .as_ref() diff --git a/src/render.rs b/src/render.rs index d96815e..600aded 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,27 +1,16 @@ //! Take an encoded scene and create a graph to render it -use bytemuck::{Pod, Zeroable}; - use crate::{ - encoding::{Config, Encoding, Layout}, engine::{BufProxy, ImageFormat, ImageProxy, Recording, ResourceProxy}, - shaders::{self, FullShaders, Shaders}, + shaders::{self, FullShaders}, RenderParams, Scene, }; +use vello_encoding::{Encoding, WorkgroupSize}; /// State for a render in progress. pub struct Render { - /// Size of binning and info combined buffer in u32 units - binning_info_size: u32, - /// Size of tiles buf in tiles - tiles_size: u32, - /// Size of segments buf in segments - segments_size: u32, - /// Size of per-tile command list in u32 units - ptcl_size: u32, - width_in_tiles: u32, - height_in_tiles: u32, - fine: Option, + fine_wg_count: Option, + fine_resources: Option, } /// Resources produced by pipeline, needed for fine rasterization. @@ -38,128 +27,6 @@ struct FineResources { out_image: ImageProxy, } -const TAG_MONOID_SIZE: u64 = 12; -const TAG_MONOID_FULL_SIZE: u64 = 20; -const PATH_BBOX_SIZE: u64 = 24; -const CUBIC_SIZE: u64 = 48; -const DRAWMONOID_SIZE: u64 = 16; -const CLIP_BIC_SIZE: u64 = 8; -const CLIP_EL_SIZE: u64 = 32; -const CLIP_INP_SIZE: u64 = 8; -const CLIP_BBOX_SIZE: u64 = 16; -const PATH_SIZE: u64 = 32; -const DRAW_BBOX_SIZE: u64 = 16; -const BUMP_SIZE: u64 = std::mem::size_of::() as u64; -const BIN_HEADER_SIZE: u64 = 8; -const TILE_SIZE: u64 = 8; -const SEGMENT_SIZE: u64 = 24; - -fn size_to_words(byte_size: usize) -> u32 { - (byte_size / std::mem::size_of::()) as u32 -} - -pub const fn next_multiple_of(val: u32, rhs: u32) -> u32 { - match val % rhs { - 0 => val, - r => val + (rhs - r), - } -} - -// This must be kept in sync with the struct in shader/shared/bump.wgsl -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] -struct BumpAllocators { - failed: u32, - // Final needed dynamic size of the buffers. If any of these are larger than the corresponding `_size` element - // reallocation needs to occur - binning: u32, - ptcl: u32, - tile: u32, - segments: u32, - blend: u32, -} - -#[allow(unused)] -fn render(scene: &Scene, shaders: &Shaders) -> (Recording, BufProxy) { - let mut recording = Recording::default(); - let data = scene.data(); - let n_pathtag = data.path_tags.len(); - let pathtag_padded = align_up(n_pathtag, 4 * shaders::PATHTAG_REDUCE_WG); - let pathtag_wgs = pathtag_padded / (4 * shaders::PATHTAG_REDUCE_WG as usize); - let mut scene: Vec = Vec::with_capacity(pathtag_padded); - let pathtag_base = size_to_words(scene.len()); - scene.extend(bytemuck::cast_slice(&data.path_tags)); - scene.resize(pathtag_padded, 0); - let pathdata_base = size_to_words(scene.len()); - scene.extend(&data.path_data); - - let config = Config { - width_in_tiles: 64, - height_in_tiles: 64, - target_width: 64 * 16, - target_height: 64 * 16, - layout: Layout { - path_tag_base: pathtag_base, - path_data_base: pathdata_base, - ..Default::default() - }, - ..Default::default() - }; - let scene_buf = recording.upload("scene", scene); - let config_buf = recording.upload_uniform("config", bytemuck::bytes_of(&config)); - - let reduced_buf = BufProxy::new(pathtag_wgs as u64 * TAG_MONOID_SIZE, "reduced_buf"); - // TODO: really only need pathtag_wgs - 1 - recording.dispatch( - shaders.pathtag_reduce, - (pathtag_wgs as u32, 1, 1), - [config_buf, scene_buf, reduced_buf], - ); - - let tagmonoid_buf = BufProxy::new( - pathtag_wgs as u64 * shaders::PATHTAG_REDUCE_WG as u64 * TAG_MONOID_SIZE, - "tagmonoid_buf", - ); - recording.dispatch( - shaders.pathtag_scan, - (pathtag_wgs as u32, 1, 1), - [config_buf, scene_buf, reduced_buf, tagmonoid_buf], - ); - - let path_coarse_wgs = - (n_pathtag as u32 + shaders::PATH_COARSE_WG - 1) / shaders::PATH_COARSE_WG; - // TODO: more principled size calc - let tiles_buf = BufProxy::new(4097 * 8, "tiles_buf"); - let segments_buf = BufProxy::new(256 * 24, "segments_buf"); - recording.clear_all(tiles_buf); - recording.dispatch( - shaders.path_coarse, - (path_coarse_wgs, 1, 1), - [ - config_buf, - scene_buf, - tagmonoid_buf, - tiles_buf, - segments_buf, - ], - ); - recording.dispatch( - shaders.backdrop, - (config.height_in_tiles, 1, 1), - [config_buf, tiles_buf], - ); - let out_buf_size = config.width_in_tiles * config.height_in_tiles * 256; - let out_buf = BufProxy::new(out_buf_size as u64, "out_buf"); - recording.dispatch( - shaders.fine, - (config.width_in_tiles, config.height_in_tiles, 1), - [config_buf, tiles_buf, segments_buf, out_buf], - ); - - recording.download(out_buf); - (recording, out_buf) -} - pub fn render_full( scene: &Scene, shaders: &FullShaders, @@ -184,21 +51,11 @@ pub fn render_encoding_full( (recording, out_image.into()) } -pub fn align_up(len: usize, alignment: u32) -> usize { - len + (len.wrapping_neg() & (alignment as usize - 1)) -} - impl Render { pub fn new() -> Self { - // These sizes are adequate for paris-30k but should probably be dialed down. Render { - binning_info_size: (1 << 20) / 4, - tiles_size: (1 << 24) / TILE_SIZE as u32, - segments_size: (1 << 26) / SEGMENT_SIZE as u32, - ptcl_size: (1 << 25) / 4 as u32, - width_in_tiles: 0, - height_in_tiles: 0, - fine: None, + fine_wg_count: None, + fine_resources: None, } } @@ -213,7 +70,8 @@ impl Render { params: &RenderParams, robust: bool, ) -> Recording { - use crate::encoding::Resolver; + use vello_encoding::{RenderConfig, Resolver}; + let mut recording = Recording::default(); let mut resolver = Resolver::new(); let mut packed = vec![]; @@ -234,29 +92,6 @@ impl Render { } else { ImageProxy::new(images.width, images.height, ImageFormat::Rgba8) }; - // TODO: calculate for real when we do rectangles - let n_pathtag = layout.path_tags(&packed).len(); - let pathtag_padded = align_up(n_pathtag, 4 * shaders::PATHTAG_REDUCE_WG); - let n_paths = layout.n_paths; - let n_drawobj = layout.n_paths; - let n_clip = layout.n_clips; - - let new_width = next_multiple_of(params.width, 16); - let new_height = next_multiple_of(params.height, 16); - - let info_size = layout.bin_data_start; - let config = crate::encoding::Config { - width_in_tiles: new_width / 16, - height_in_tiles: new_height / 16, - target_width: params.width, - target_height: params.height, - base_color: params.base_color.to_premul_u32(), - binning_size: self.binning_info_size - info_size, - tiles_size: self.tiles_size, - segments_size: self.segments_size, - ptcl_size: self.ptcl_size, - layout: layout, - }; for image in images.images { recording.write_image( image_atlas, @@ -267,52 +102,54 @@ impl Render { image.0.data.data(), ); } - // println!("{:?}", config); + + let cpu_config = + RenderConfig::new(&layout, params.width, params.height, ¶ms.base_color); + let buffer_sizes = &cpu_config.buffer_sizes; + let wg_counts = &cpu_config.workgroup_counts; + let scene_buf = ResourceProxy::Buf(recording.upload("scene", packed)); - let config_buf = - ResourceProxy::Buf(recording.upload_uniform("config", bytemuck::bytes_of(&config))); + let config_buf = ResourceProxy::Buf( + recording.upload_uniform("config", bytemuck::bytes_of(&cpu_config.gpu)), + ); let info_bin_data_buf = ResourceProxy::new_buf( - (info_size + config.binning_size) as u64 * 4, + buffer_sizes.bin_data.size_in_bytes() as u64, "info_bin_data_buf", ); - let tile_buf = ResourceProxy::new_buf(config.tiles_size as u64 * TILE_SIZE, "tile_buf"); + let tile_buf = + ResourceProxy::new_buf(buffer_sizes.tiles.size_in_bytes().into(), "tile_buf"); let segments_buf = - ResourceProxy::new_buf(config.segments_size as u64 * SEGMENT_SIZE, "segments_buf"); - let ptcl_buf = ResourceProxy::new_buf(config.ptcl_size as u64 * 4, "ptcl_buf"); - - let pathtag_wgs = pathtag_padded / (4 * shaders::PATHTAG_REDUCE_WG as usize); - let pathtag_large = pathtag_wgs > shaders::PATHTAG_REDUCE_WG as usize; - let reduced_size = if pathtag_large { - align_up(pathtag_wgs, shaders::PATHTAG_REDUCE_WG) - } else { - pathtag_wgs - }; - let reduced_buf = - ResourceProxy::new_buf(reduced_size as u64 * TAG_MONOID_FULL_SIZE, "reduced_buf"); + ResourceProxy::new_buf(buffer_sizes.segments.size_in_bytes().into(), "segments_buf"); + let ptcl_buf = ResourceProxy::new_buf(buffer_sizes.ptcl.size_in_bytes().into(), "ptcl_buf"); + let reduced_buf = ResourceProxy::new_buf( + buffer_sizes.path_reduced.size_in_bytes().into(), + "reduced_buf", + ); // TODO: really only need pathtag_wgs - 1 recording.dispatch( shaders.pathtag_reduce, - (pathtag_wgs as u32, 1, 1), + wg_counts.path_reduce, [config_buf, scene_buf, reduced_buf], ); let mut pathtag_parent = reduced_buf; let mut large_pathtag_bufs = None; - if pathtag_large { - let reduced2_size = shaders::PATHTAG_REDUCE_WG as usize; - let reduced2_buf = - ResourceProxy::new_buf(reduced2_size as u64 * TAG_MONOID_FULL_SIZE, "reduced2_buf"); + if wg_counts.use_large_path_scan { + let reduced2_buf = ResourceProxy::new_buf( + buffer_sizes.path_reduced2.size_in_bytes().into(), + "reduced2_buf", + ); recording.dispatch( shaders.pathtag_reduce2, - (reduced2_size as u32, 1, 1), + wg_counts.path_reduce2, [reduced_buf, reduced2_buf], ); let reduced_scan_buf = ResourceProxy::new_buf( - pathtag_wgs as u64 * TAG_MONOID_FULL_SIZE, + buffer_sizes.path_reduced_scan.size_in_bytes().into(), "reduced_scan_buf", ); recording.dispatch( shaders.pathtag_scan1, - (reduced_size as u32 / shaders::PATHTAG_REDUCE_WG, 1, 1), + wg_counts.path_scan1, [reduced_buf, reduced2_buf, reduced_scan_buf], ); pathtag_parent = reduced_scan_buf; @@ -320,17 +157,17 @@ impl Render { } let tagmonoid_buf = ResourceProxy::new_buf( - pathtag_wgs as u64 * shaders::PATHTAG_REDUCE_WG as u64 * TAG_MONOID_FULL_SIZE, + buffer_sizes.path_monoids.size_in_bytes().into(), "tagmonoid_buf", ); - let pathtag_scan = if pathtag_large { + let pathtag_scan = if wg_counts.use_large_path_scan { shaders.pathtag_scan_large } else { shaders.pathtag_scan }; recording.dispatch( pathtag_scan, - (pathtag_wgs as u32, 1, 1), + wg_counts.path_scan, [config_buf, scene_buf, pathtag_parent, tagmonoid_buf], ); recording.free_resource(reduced_buf); @@ -338,20 +175,20 @@ impl Render { recording.free_resource(reduced2); recording.free_resource(reduced_scan); } - let drawobj_wgs = (n_drawobj + shaders::PATH_BBOX_WG - 1) / shaders::PATH_BBOX_WG; - let path_bbox_buf = - ResourceProxy::new_buf(n_paths as u64 * PATH_BBOX_SIZE, "path_bbox_buf"); + let path_bbox_buf = ResourceProxy::new_buf( + buffer_sizes.path_bboxes.size_in_bytes().into(), + "path_bbox_buf", + ); recording.dispatch( shaders.bbox_clear, - (drawobj_wgs, 1, 1), + wg_counts.bbox_clear, [config_buf, path_bbox_buf], ); - let cubic_buf = ResourceProxy::new_buf(n_pathtag as u64 * CUBIC_SIZE, "cubic_buf"); - let path_coarse_wgs = - (n_pathtag as u32 + shaders::PATH_COARSE_WG - 1) / shaders::PATH_COARSE_WG; + let cubic_buf = + ResourceProxy::new_buf(buffer_sizes.cubics.size_in_bytes().into(), "cubic_buf"); recording.dispatch( shaders.pathseg, - (path_coarse_wgs, 1, 1), + wg_counts.path_seg, [ config_buf, scene_buf, @@ -360,19 +197,26 @@ impl Render { cubic_buf, ], ); - let draw_reduced_buf = - ResourceProxy::new_buf(drawobj_wgs as u64 * DRAWMONOID_SIZE, "draw_reduced_buf"); + let draw_reduced_buf = ResourceProxy::new_buf( + buffer_sizes.draw_reduced.size_in_bytes().into(), + "draw_reduced_buf", + ); recording.dispatch( shaders.draw_reduce, - (drawobj_wgs, 1, 1), + wg_counts.draw_reduce, [config_buf, scene_buf, draw_reduced_buf], ); - let draw_monoid_buf = - ResourceProxy::new_buf(n_drawobj as u64 * DRAWMONOID_SIZE, "draw_monoid_buf"); - let clip_inp_buf = ResourceProxy::new_buf(n_clip as u64 * CLIP_INP_SIZE, "clip_inp_buf"); + let draw_monoid_buf = ResourceProxy::new_buf( + buffer_sizes.draw_monoids.size_in_bytes().into(), + "draw_monoid_buf", + ); + let clip_inp_buf = ResourceProxy::new_buf( + buffer_sizes.clip_inps.size_in_bytes().into(), + "clip_inp_buf", + ); recording.dispatch( shaders.draw_leaf, - (drawobj_wgs, 1, 1), + wg_counts.draw_leaf, [ config_buf, scene_buf, @@ -384,16 +228,16 @@ impl Render { ], ); recording.free_resource(draw_reduced_buf); - let clip_el_buf = ResourceProxy::new_buf(n_clip as u64 * CLIP_EL_SIZE, "clip_el_buf"); + let clip_el_buf = + ResourceProxy::new_buf(buffer_sizes.clip_els.size_in_bytes().into(), "clip_el_buf"); let clip_bic_buf = ResourceProxy::new_buf( - (n_clip / shaders::CLIP_REDUCE_WG) as u64 * CLIP_BIC_SIZE, + buffer_sizes.clip_bics.size_in_bytes().into(), "clip_bic_buf", ); - let clip_wg_reduce = n_clip.saturating_sub(1) / shaders::CLIP_REDUCE_WG; - if clip_wg_reduce > 0 { + if wg_counts.clip_reduce.0 > 0 { recording.dispatch( shaders.clip_reduce, - (clip_wg_reduce, 1, 1), + wg_counts.clip_reduce, [ config_buf, clip_inp_buf, @@ -403,12 +247,14 @@ impl Render { ], ); } - let clip_wg = (n_clip + shaders::CLIP_REDUCE_WG - 1) / shaders::CLIP_REDUCE_WG; - let clip_bbox_buf = ResourceProxy::new_buf(n_clip as u64 * CLIP_BBOX_SIZE, "clip_bbox_buf"); - if clip_wg > 0 { + let clip_bbox_buf = ResourceProxy::new_buf( + buffer_sizes.clip_bboxes.size_in_bytes().into(), + "clip_bbox_buf", + ); + if wg_counts.clip_leaf.0 > 0 { recording.dispatch( shaders.clip_leaf, - (clip_wg, 1, 1), + wg_counts.clip_leaf, [ config_buf, clip_inp_buf, @@ -423,20 +269,20 @@ impl Render { recording.free_resource(clip_inp_buf); recording.free_resource(clip_bic_buf); recording.free_resource(clip_el_buf); - let draw_bbox_buf = - ResourceProxy::new_buf(n_paths as u64 * DRAW_BBOX_SIZE, "draw_bbox_buf"); - let bump_buf = BufProxy::new(BUMP_SIZE, "bump_buf"); - let width_in_bins = (config.width_in_tiles + 15) / 16; - let height_in_bins = (config.height_in_tiles + 15) / 16; + let draw_bbox_buf = ResourceProxy::new_buf( + buffer_sizes.draw_bboxes.size_in_bytes().into(), + "draw_bbox_buf", + ); + let bump_buf = BufProxy::new(buffer_sizes.bump_alloc.size_in_bytes().into(), "bump_buf"); let bin_header_buf = ResourceProxy::new_buf( - (256 * drawobj_wgs) as u64 * BIN_HEADER_SIZE, + buffer_sizes.bin_headers.size_in_bytes().into(), "bin_header_buf", ); recording.clear_all(bump_buf); let bump_buf = ResourceProxy::Buf(bump_buf); recording.dispatch( shaders.binning, - (drawobj_wgs, 1, 1), + wg_counts.binning, [ config_buf, draw_monoid_buf, @@ -453,12 +299,11 @@ impl Render { recording.free_resource(clip_bbox_buf); // Note: this only needs to be rounded up because of the workaround to store the tile_offset // in storage rather than workgroup memory. - let n_path_aligned = align_up(n_paths as usize, 256); - let path_buf = ResourceProxy::new_buf(n_path_aligned as u64 * PATH_SIZE, "path_buf"); - let path_wgs = (n_paths + shaders::PATH_BBOX_WG - 1) / shaders::PATH_BBOX_WG; + let path_buf = + ResourceProxy::new_buf(buffer_sizes.paths.size_in_bytes().into(), "path_buf"); recording.dispatch( shaders.tile_alloc, - (path_wgs, 1, 1), + wg_counts.tile_alloc, [ config_buf, scene_buf, @@ -471,7 +316,7 @@ impl Render { recording.free_resource(draw_bbox_buf); recording.dispatch( shaders.path_coarse, - (path_coarse_wgs, 1, 1), + wg_counts.path_coarse, [ config_buf, scene_buf, @@ -487,12 +332,12 @@ impl Render { recording.free_resource(cubic_buf); recording.dispatch( shaders.backdrop, - (path_wgs, 1, 1), + wg_counts.backdrop, [config_buf, path_buf, tile_buf], ); recording.dispatch( shaders.coarse, - (width_in_bins, height_in_bins, 1), + wg_counts.coarse, [ config_buf, scene_buf, @@ -510,9 +355,8 @@ impl Render { recording.free_resource(bin_header_buf); recording.free_resource(path_buf); let out_image = ImageProxy::new(params.width, params.height, ImageFormat::Rgba8); - self.width_in_tiles = config.width_in_tiles; - self.height_in_tiles = config.height_in_tiles; - self.fine = Some(FineResources { + self.fine_wg_count = Some(wg_counts.fine); + self.fine_resources = Some(FineResources { config_buf, bump_buf, tile_buf, @@ -532,10 +376,11 @@ impl Render { /// Run fine rasterization assuming the coarse phase succeeded. pub fn record_fine(&mut self, shaders: &FullShaders, recording: &mut Recording) { - let fine = self.fine.take().unwrap(); + let fine_wg_count = self.fine_wg_count.take().unwrap(); + let fine = self.fine_resources.take().unwrap(); recording.dispatch( shaders.fine, - (self.width_in_tiles, self.height_in_tiles, 1), + fine_wg_count, [ fine.config_buf, fine.tile_buf, @@ -561,10 +406,16 @@ impl Render { /// This is going away, as the caller will add the output image to the bind /// map. pub fn out_image(&self) -> ImageProxy { - self.fine.as_ref().unwrap().out_image + self.fine_resources.as_ref().unwrap().out_image } pub fn bump_buf(&self) -> BufProxy { - *self.fine.as_ref().unwrap().bump_buf.as_buf().unwrap() + *self + .fine_resources + .as_ref() + .unwrap() + .bump_buf + .as_buf() + .unwrap() } } diff --git a/src/scene.rs b/src/scene.rs index ce777b8..647bf89 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -17,8 +17,7 @@ use fello::NormalizedCoord; use peniko::kurbo::{Affine, Rect, Shape}; use peniko::{BlendMode, BrushRef, Color, Fill, Font, Image, Stroke, StyleRef}; - -use crate::encoding::{Encoding, Glyph, GlyphRun, Patch, Transform}; +use vello_encoding::{Encoding, Glyph, GlyphRun, Patch, Transform}; /// Encoded definition of a scene and associated resources. #[derive(Default)] diff --git a/src/shaders.rs b/src/shaders.rs index 01c3b38..d1cc0dc 100644 --- a/src/shaders.rs +++ b/src/shaders.rs @@ -24,12 +24,6 @@ use wgpu::Device; use crate::engine::{BindType, Engine, Error, ImageFormat, ShaderId}; -pub const PATHTAG_REDUCE_WG: u32 = 256; -pub const PATH_BBOX_WG: u32 = 256; -pub const PATH_COARSE_WG: u32 = 256; -pub const PATH_DRAWOBJ_WG: u32 = 256; -pub const CLIP_REDUCE_WG: u32 = 256; - macro_rules! shader { ($name:expr) => {&{ let shader = include_str!(concat!( @@ -56,14 +50,6 @@ macro_rules! shader { }}; } -pub struct Shaders { - pub pathtag_reduce: ShaderId, - pub pathtag_scan: ShaderId, - pub path_coarse: ShaderId, - pub backdrop: ShaderId, - pub fine: ShaderId, -} - // Shaders for the full pipeline pub struct FullShaders { pub pathtag_reduce: ShaderId, @@ -85,70 +71,6 @@ pub struct FullShaders { pub fine: ShaderId, } -pub fn init_shaders(device: &Device, engine: &mut Engine) -> Result { - let imports = SHARED_SHADERS - .iter() - .copied() - .collect::>(); - let empty = HashSet::new(); - let pathtag_reduce = engine.add_shader( - device, - "pathtag_reduce", - preprocess::preprocess(shader!("pathtag_reduce"), &empty, &imports).into(), - &[BindType::Uniform, BindType::BufReadOnly, BindType::Buffer], - )?; - let pathtag_scan = engine.add_shader( - device, - "pathtag_scan", - preprocess::preprocess(shader!("pathtag_scan"), &empty, &imports).into(), - &[ - BindType::Uniform, - BindType::BufReadOnly, - BindType::BufReadOnly, - BindType::Buffer, - ], - )?; - let path_coarse_config = HashSet::new(); - // path_coarse_config.add("cubics_out"); - - let path_coarse = engine.add_shader( - device, - "path_coarse", - preprocess::preprocess(shader!("path_coarse"), &path_coarse_config, &imports).into(), - &[ - BindType::Uniform, - BindType::BufReadOnly, - BindType::BufReadOnly, - BindType::Buffer, - BindType::Buffer, - ], - )?; - let backdrop = engine.add_shader( - device, - "backdrop", - preprocess::preprocess(shader!("backdrop"), &empty, &imports).into(), - &[BindType::Uniform, BindType::Buffer], - )?; - let fine = engine.add_shader( - device, - "fine", - preprocess::preprocess(shader!("fine"), &empty, &imports).into(), - &[ - BindType::Uniform, - BindType::BufReadOnly, - BindType::BufReadOnly, - BindType::Buffer, - ], - )?; - Ok(Shaders { - pathtag_reduce, - pathtag_scan, - path_coarse, - backdrop, - fine, - }) -} - pub fn full_shaders(device: &Device, engine: &mut Engine) -> Result { let imports = SHARED_SHADERS .iter()