CPU: feature flag u8 and f32 pipelines (#1294)
Adds feature flags to `vello_cpu`s `u8` and `f32` pipelines so that you
can save on binary size if you're only planning on using one of them.
Disabling the `f32` pipeline knocks ~60kb off the binary size for me
~250kb -> ~190kb.
---------
Signed-off-by: Nico Burns <nico@nicoburns.com>
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7d7bcf8..70dfc91 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -146,13 +146,19 @@
save-if: ${{ github.event_name != 'merge_group' }}
- name: cargo clippy (no_std)
- run: cargo hack clippy ${{ env.RUST_NO_STD_PKGS }} --locked --optional-deps --each-feature --ignore-unknown-features --features libm --exclude-features ${{ env.FEATURES_DEPENDING_ON_STD }} --target x86_64-unknown-none -- -D warnings
+ run: cargo hack clippy ${{ env.RUST_NO_STD_PKGS }} --locked --optional-deps --each-feature --ignore-unknown-features --features libm,u8_pipeline,f32_pipeline --exclude-features ${{ env.FEATURES_DEPENDING_ON_STD }} --target x86_64-unknown-none -- -D warnings
- name: cargo clippy
- run: cargo hack clippy --workspace --locked --optional-deps --each-feature --ignore-unknown-features --features std -- -D warnings
+ run: cargo hack clippy --workspace --locked --optional-deps --each-feature --ignore-unknown-features --features std,u8_pipeline,f32_pipeline -- -D warnings
+
+ - name: cargo clippy (vello_cpu - u8 pipeline)
+ run: cargo clippy -p vello_cpu --locked --no-default-features --features std,text,png,u8_pipeline
+
+ - name: cargo clippy (vello_cpu - f32 pipeline)
+ run: cargo clippy -p vello_cpu --locked --no-default-features --features std,text,png,f32_pipeline
- name: cargo clippy (auxiliary)
- run: cargo hack clippy --workspace --locked --optional-deps --each-feature --ignore-unknown-features --features std --tests --benches --examples -- -D warnings
+ run: cargo hack clippy --workspace --locked --optional-deps --each-feature --ignore-unknown-features --features std,u8_pipeline,f32_pipeline --tests --benches --examples -- -D warnings
clippy-stable-wasm:
name: cargo clippy (wasm32)
@@ -178,13 +184,13 @@
save-if: ${{ github.event_name != 'merge_group' }}
- name: cargo clippy (no_std)
- run: cargo hack clippy ${{ env.RUST_NO_STD_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features libm --exclude-features ${{ env.FEATURES_DEPENDING_ON_STD }} -- -D warnings
+ run: cargo hack clippy ${{ env.RUST_NO_STD_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features libm,u8_pipeline,f32_pipeline --exclude-features ${{ env.FEATURES_DEPENDING_ON_STD }} -- -D warnings
- name: cargo clippy
- run: cargo hack clippy --workspace ${{ env.NO_WASM_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features std -- -D warnings
+ run: cargo hack clippy --workspace ${{ env.NO_WASM_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features std,u8_pipeline,f32_pipeline -- -D warnings
- name: cargo clippy (auxiliary)
- run: cargo hack clippy --workspace ${{ env.NO_WASM_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features std --tests --benches --examples -- -D warnings
+ run: cargo hack clippy --workspace ${{ env.NO_WASM_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features std,u8_pipeline,f32_pipeline --tests --benches --examples -- -D warnings
prime-lfs-cache:
name: Prime LFS Cache
@@ -422,10 +428,10 @@
save-if: ${{ github.event_name != 'merge_group' }}
- name: cargo check (no_std)
- run: cargo hack check ${{ env.RUST_NO_STD_PKGS }} --locked --optional-deps --each-feature --ignore-unknown-features --features libm --exclude-features ${{ env.FEATURES_DEPENDING_ON_STD }} --target x86_64-unknown-none
+ run: cargo hack check ${{ env.RUST_NO_STD_PKGS }} --locked --optional-deps --each-feature --ignore-unknown-features --features libm,u8_pipeline,f32_pipeline --exclude-features ${{ env.FEATURES_DEPENDING_ON_STD }} --target x86_64-unknown-none
- name: cargo check
- run: cargo hack check ${{ env.RUST_MIN_VER_PKGS }} --locked --optional-deps --each-feature --ignore-unknown-features --features std
+ run: cargo hack check ${{ env.RUST_MIN_VER_PKGS }} --locked --optional-deps --each-feature --ignore-unknown-features --features std,u8_pipeline,f32_pipeline
check-msrv-wasm:
name: cargo check (msrv) (wasm32)
@@ -450,7 +456,7 @@
save-if: ${{ github.event_name != 'merge_group' }}
- name: cargo check
- run: cargo hack check ${{ env.RUST_MIN_VER_PKGS }} ${{ env.NO_WASM_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features std
+ run: cargo hack check ${{ env.RUST_MIN_VER_PKGS }} ${{ env.NO_WASM_PKGS }} --locked --target wasm32-unknown-unknown --optional-deps --each-feature --ignore-unknown-features --features std,u8_pipeline,f32_pipeline
doc:
name: cargo doc
diff --git a/sparse_strips/vello_cpu/Cargo.toml b/sparse_strips/vello_cpu/Cargo.toml
index f31c4f2..1d7842b 100644
--- a/sparse_strips/vello_cpu/Cargo.toml
+++ b/sparse_strips/vello_cpu/Cargo.toml
@@ -26,7 +26,7 @@
thread_local = { workspace = true, optional = true }
[features]
-default = ["std", "png", "text"]
+default = ["std", "png", "text", "u8_pipeline"]
# Get floating point functions from the standard library (likely using your target’s libc).
std = ["vello_common/std"]
# Use floating point implementations from libm.
@@ -45,6 +45,11 @@
# Add support for text rendering
text = ["vello_common/text"]
+# Speed focussed rendering using u8 math
+u8_pipeline = []
+# Quality focussed rendering using f32 math
+f32_pipeline = []
+
[lints]
workspace = true
diff --git a/sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs b/sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs
index 3b4fa80..8a91a7c 100644
--- a/sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs
+++ b/sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs
@@ -5,7 +5,7 @@
use crate::dispatch::Dispatcher;
use crate::dispatch::multi_threaded::cost::{COST_THRESHOLD, estimate_render_task_cost};
use crate::dispatch::multi_threaded::worker::Worker;
-use crate::fine::{F32Kernel, Fine, FineKernel, U8Kernel};
+use crate::fine::{Fine, FineKernel};
use crate::kurbo::{Affine, BezPath, PathEl, Stroke};
use crate::peniko::{BlendMode, Fill};
use crate::region::Regions;
@@ -163,6 +163,7 @@
dispatcher
}
+ #[cfg(feature = "f32_pipeline")]
fn rasterize_f32(
&self,
buffer: &mut [u8],
@@ -170,9 +171,11 @@
height: u16,
encoded_paints: &[EncodedPaint],
) {
+ use crate::fine::F32Kernel;
dispatch!(self.level, simd => self.rasterize_with::<_, F32Kernel>(simd, buffer, width, height, encoded_paints));
}
+ #[cfg(feature = "u8_pipeline")]
fn rasterize_u8(
&self,
buffer: &mut [u8],
@@ -180,6 +183,7 @@
height: u16,
encoded_paints: &[EncodedPaint],
) {
+ use crate::fine::U8Kernel;
dispatch!(self.level, simd => self.rasterize_with::<_, U8Kernel>(simd, buffer, width, height, encoded_paints));
}
@@ -569,6 +573,21 @@
) {
assert!(self.flushed, "attempted to rasterize before flushing");
+ // Only u8 pipeline enabled
+ #[cfg(all(feature = "u8_pipeline", not(feature = "f32_pipeline")))]
+ {
+ let _ = render_mode;
+ self.rasterize_u8(buffer, width, height, encoded_paints);
+ }
+ // Only f32 pipeline enabled
+ #[cfg(all(feature = "f32_pipeline", not(feature = "u8_pipeline")))]
+ {
+ let _ = render_mode;
+ self.rasterize_f32(buffer, width, height, encoded_paints);
+ }
+
+ // Both pipelines enabled
+ #[cfg(all(feature = "f32_pipeline", feature = "u8_pipeline"))]
match render_mode {
RenderMode::OptimizeSpeed => self.rasterize_u8(buffer, width, height, encoded_paints),
RenderMode::OptimizeQuality => {
diff --git a/sparse_strips/vello_cpu/src/dispatch/single_threaded.rs b/sparse_strips/vello_cpu/src/dispatch/single_threaded.rs
index d508979..2f6a50c 100644
--- a/sparse_strips/vello_cpu/src/dispatch/single_threaded.rs
+++ b/sparse_strips/vello_cpu/src/dispatch/single_threaded.rs
@@ -3,7 +3,7 @@
use crate::RenderMode;
use crate::dispatch::Dispatcher;
-use crate::fine::{F32Kernel, Fine, FineKernel, U8Kernel};
+use crate::fine::{Fine, FineKernel};
use crate::kurbo::{Affine, BezPath, Stroke};
use crate::layer_manager::LayerManager;
use crate::peniko::{BlendMode, Fill};
@@ -12,7 +12,7 @@
use vello_common::coarse::{Cmd, LayerKind, MODE_CPU, Wide, WideTilesBbox};
use vello_common::color::palette::css::TRANSPARENT;
use vello_common::encode::EncodedPaint;
-use vello_common::fearless_simd::{Level, Simd, dispatch};
+use vello_common::fearless_simd::{Level, Simd};
use vello_common::filter_effects::Filter;
use vello_common::mask::Mask;
use vello_common::paint::{Paint, PremulColor};
@@ -87,6 +87,7 @@
///
/// This dispatches to the appropriate SIMD implementation based on the
/// configured level, using f32 for intermediate calculations.
+ #[cfg(feature = "f32_pipeline")]
fn rasterize_f32(
&self,
buffer: &mut [u8],
@@ -94,6 +95,8 @@
height: u16,
encoded_paints: &[EncodedPaint],
) {
+ use crate::fine::F32Kernel;
+ use vello_common::fearless_simd::dispatch;
dispatch!(self.level, simd => self.rasterize_with::<_, F32Kernel>(simd, buffer, width, height, encoded_paints));
}
@@ -101,6 +104,7 @@
///
/// This dispatches to the appropriate SIMD implementation based on the
/// configured level, using u8 for intermediate calculations to maximize speed.
+ #[cfg(feature = "u8_pipeline")]
fn rasterize_u8(
&self,
buffer: &mut [u8],
@@ -108,6 +112,8 @@
height: u16,
encoded_paints: &[EncodedPaint],
) {
+ use crate::fine::U8Kernel;
+ use vello_common::fearless_simd::dispatch;
dispatch!(self.level, simd => self.rasterize_with::<_, U8Kernel>(simd, buffer, width, height, encoded_paints));
}
@@ -526,7 +532,22 @@
height: u16,
encoded_paints: &[EncodedPaint],
) {
- // Select precision based on render mode.
+ // If only the u8 pipeline is enabled, then use it
+ #[cfg(all(feature = "u8_pipeline", not(feature = "f32_pipeline")))]
+ {
+ let _ = render_mode;
+ self.rasterize_u8(buffer, width, height, encoded_paints);
+ }
+
+ // If only the f32 pipeline is enabled, then use it
+ #[cfg(all(feature = "f32_pipeline", not(feature = "u8_pipeline")))]
+ {
+ let _ = render_mode;
+ self.rasterize_f32(buffer, width, height, encoded_paints);
+ }
+
+ // If both pipelines are enabled, select precision based on render mode parameter.
+ #[cfg(all(feature = "u8_pipeline", feature = "f32_pipeline"))]
match render_mode {
RenderMode::OptimizeSpeed => {
// Use u8 precision for faster rendering.
@@ -537,6 +558,13 @@
self.rasterize_f32(buffer, width, height, encoded_paints);
}
}
+
+ #[cfg(all(not(feature = "u8_pipeline"), not(feature = "f32_pipeline")))]
+ {
+ // This case never gets hit because there is a compile_error in the root.
+ // But have this code disables some warnings and makes the compile error easier to read
+ let _ = (buffer, render_mode, width, height, encoded_paints);
+ }
}
fn generate_wide_cmd(&mut self, strip_buf: &[Strip], paint: Paint, blend_mode: BlendMode) {
diff --git a/sparse_strips/vello_cpu/src/lib.rs b/sparse_strips/vello_cpu/src/lib.rs
index 3c13b44..28dbf4b 100644
--- a/sparse_strips/vello_cpu/src/lib.rs
+++ b/sparse_strips/vello_cpu/src/lib.rs
@@ -120,6 +120,9 @@
#[cfg(feature = "std")]
extern crate std;
+#[cfg(all(not(feature = "u8_pipeline"), not(feature = "f32_pipeline")))]
+compile_error!("vello_cpu must have at least one of the u8 or f32 pipelines enabled");
+
mod render;
mod dispatch;
diff --git a/sparse_strips/vello_sparse_tests/Cargo.toml b/sparse_strips/vello_sparse_tests/Cargo.toml
index 012ad60..aa6dbd5 100644
--- a/sparse_strips/vello_sparse_tests/Cargo.toml
+++ b/sparse_strips/vello_sparse_tests/Cargo.toml
@@ -17,7 +17,7 @@
[dependencies]
vello_api = { workspace = true }
vello_common = { workspace = true, features = ["std"] }
-vello_cpu = { workspace = true, features = ["multithreading", "std"] }
+vello_cpu = { workspace = true, features = ["multithreading", "std", "f32_pipeline"] }
vello_hybrid = { workspace = true }
wgpu = { workspace = true, default-features = true }
pollster = { workspace = true }