Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
df77e5e
repaint benchmarks
jrmoulton Mar 19, 2026
71a70b1
imaging: retained scene
jrmoulton Mar 18, 2026
32a27b7
transform invalidation
jrmoulton Mar 18, 2026
019533b
fix drag
jrmoulton Mar 18, 2026
0aff75e
request paint on set local bounds
jrmoulton Mar 18, 2026
53ebb09
fix clip
jrmoulton Mar 18, 2026
261e1ce
move display list to window state
jrmoulton Mar 19, 2026
e2d519c
paint debug and bump understory
jrmoulton Mar 19, 2026
16726b9
widget gal canvas request paint
jrmoulton Mar 19, 2026
3d286a4
move towards blink
jrmoulton Mar 19, 2026
2bc15f0
paint property tree
jrmoulton Mar 19, 2026
e0a699a
damage readiness
jrmoulton Mar 19, 2026
0524844
damage aware replay
jrmoulton Mar 19, 2026
2e01338
compositor module
jrmoulton Mar 20, 2026
ba6e256
pull in subduction
jrmoulton Mar 20, 2026
bfb8111
subduction wgpu
jrmoulton Mar 20, 2026
4a1e59b
raster + composite split
jrmoulton Mar 20, 2026
23656cf
get rid of z-index in renderer
jrmoulton Mar 20, 2026
8ef4735
use box tree bounds
jrmoulton Mar 20, 2026
5c9b39d
differentiate layer kinds
jrmoulton Mar 20, 2026
32d8c99
changes
jrmoulton Mar 21, 2026
ef76d07
changes
jrmoulton Mar 23, 2026
0de24f9
changes
jrmoulton Mar 24, 2026
87d88fa
changes
jrmoulton Mar 24, 2026
cbd766b
rendering overhaul
jrmoulton Mar 24, 2026
e6d6019
skia renderer crate
jrmoulton Mar 24, 2026
fbd7267
upgrade display list
jrmoulton Mar 24, 2026
330cbad
cpu render into
jrmoulton Mar 26, 2026
17f515f
renderer backend cleanup
jrmoulton Mar 27, 2026
337af1c
merge
jrmoulton Mar 30, 2026
86eabb1
working
jrmoulton Mar 30, 2026
a63da5f
simplify
jrmoulton Mar 31, 2026
3b12cf1
renderer state cleanup
jrmoulton Apr 1, 2026
68bd6d5
image style inspector
jrmoulton Apr 1, 2026
c225110
svg image styling
jrmoulton Apr 1, 2026
e9a2eec
svg renderer update
jrmoulton Apr 1, 2026
9cad81f
fix animation timing
jrmoulton Apr 1, 2026
4ecfa94
removed custom scene
jrmoulton Apr 1, 2026
cc2da48
works great
jrmoulton Apr 3, 2026
a522cc4
profiler
jrmoulton Apr 9, 2026
08c7dfa
frame scheduling redesign
jrmoulton Apr 10, 2026
0682aac
add frame clock
jrmoulton Apr 10, 2026
6c1ed59
renderer and vger updates
jrmoulton Apr 10, 2026
8841e3f
wgpu render target
jrmoulton Apr 12, 2026
2d73c6a
remove display list code
jrmoulton Apr 12, 2026
bf32b5f
renderer pool cleanup
jrmoulton Apr 12, 2026
2fe7ef1
minimap
jrmoulton Apr 12, 2026
37f38e7
better time spans
jrmoulton Apr 13, 2026
a7c4366
fix editor paint invalidate
jrmoulton Apr 13, 2026
f64b7fa
early run work
jrmoulton Apr 14, 2026
5f5adb7
scheduling all running smooth
jrmoulton Apr 14, 2026
48cf32f
cv display link working
jrmoulton Apr 14, 2026
0e39ed4
schedule fix docs
jrmoulton Apr 14, 2026
5c53922
frame schedule tuning
jrmoulton Apr 14, 2026
0a6bf15
window frame refactor
jrmoulton Apr 14, 2026
5d4055e
render pipeline cleanup
jrmoulton Apr 14, 2026
af7dbcd
app render split
jrmoulton Apr 14, 2026
363e962
frame clock pacing
jrmoulton Apr 15, 2026
89c4263
remove app scheduling
jrmoulton Apr 15, 2026
cc3af7c
frame clock dependency
jrmoulton Apr 15, 2026
028657d
window render cleanup
jrmoulton Apr 15, 2026
7c5a516
profiler timing cleanup
jrmoulton Apr 15, 2026
d943b5f
gpu completion callbacks
jrmoulton Apr 15, 2026
1afcf76
window present timing
jrmoulton Apr 15, 2026
3614753
renderer queue timing
jrmoulton Apr 16, 2026
16ab0dd
app frame timing
jrmoulton Apr 16, 2026
1198e1e
renderer handle cleanup
jrmoulton Apr 16, 2026
a7ea608
widget gallery pacing
jrmoulton Apr 16, 2026
37a4b3d
move vger adapter
jrmoulton Apr 16, 2026
5bdcbef
pefect frame pacing
jrmoulton Apr 17, 2026
5afadde
cached svg
jrmoulton Apr 18, 2026
4f8465d
font embolden
jrmoulton Apr 21, 2026
5b99d94
working external surface
jrmoulton Apr 25, 2026
37901ae
improved timing on external surface
jrmoulton Apr 25, 2026
66ff1d0
move timing
jrmoulton Apr 26, 2026
a02c87c
better layer promotion
jrmoulton Apr 26, 2026
a4d6abd
inspector frame timing
jrmoulton Apr 28, 2026
53b9ce0
external surface compositor
jrmoulton Apr 29, 2026
14d2075
shader source images
jrmoulton Apr 30, 2026
cb4678b
display list batching
jrmoulton May 1, 2026
d8427bd
renderer simplification
jrmoulton May 1, 2026
0a55541
compositor surfaces
jrmoulton May 2, 2026
a3d23ca
frame source hud
jrmoulton May 2, 2026
a57fd77
gpu timing profiler
jrmoulton May 3, 2026
6e3d8d4
surface provider updates
jrmoulton May 3, 2026
a58c7e3
ui driver split
jrmoulton May 4, 2026
a94978b
threaded ui runtime
jrmoulton May 4, 2026
876a513
effects brush overhaul
jrmoulton May 4, 2026
3c541ae
external image brushes
jrmoulton May 5, 2026
99c2dde
compositor layer sync
jrmoulton May 5, 2026
22540a9
frame pacing integration
jrmoulton May 5, 2026
eeb2ec0
metal capture timing
jrmoulton May 6, 2026
ef77e17
gpu render profiling
jrmoulton May 6, 2026
f60bd33
rename image view
jrmoulton May 6, 2026
1488639
image brush support
jrmoulton May 6, 2026
262176b
gradient image brushes
jrmoulton May 6, 2026
bfe1490
composition plan split
jrmoulton May 7, 2026
612487a
scene render plan
jrmoulton May 8, 2026
94d889f
brush transform cleanup
jrmoulton May 8, 2026
d03fc47
default shortcut cleanup
jrmoulton May 9, 2026
d655b80
transformed layout brush
jrmoulton May 11, 2026
3a22166
remove Floem from alias
jrmoulton May 11, 2026
fc67b86
switch to git versions of imaging and subduction
jrmoulton May 11, 2026
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
7,960 changes: 0 additions & 7,960 deletions Cargo.lock

This file was deleted.

95 changes: 67 additions & 28 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
[workspace]
resolver = "3"
members = [
"renderer",
"skia",
"vello",
"vger",
"tiny_skia",
"reactive",
"editor-core",
"examples/*",
Expand All @@ -14,11 +9,6 @@ members = [
]
default-members = [
".",
"renderer",
"skia",
"vello",
"vger",
"tiny_skia",
"reactive",
"editor-core",
"ui-events-winit",
Expand Down Expand Up @@ -55,10 +45,10 @@ strum_macros = { version = "0.28" }
# Image format features are defined via new features
image = { version = "0.25", default-features = false }
imbl = "7.0"
resvg = { version = "0.46" }
tiny-skia = { version = "0.11.4" }
raw-window-handle = "0.6.0"
unicode-segmentation = "1.10"
wgpu = { version = "27.0" }
wgpu = { version = "28.0" }
parking_lot = { version = "0.12" }
swash = { version = "0.2" }
muda = { version = "0.17.1", default-features = false }
Expand All @@ -67,13 +57,20 @@ ui-events = { version = "0.3", features = ["kurbo"] }
ui-events-winit = { path = "./ui-events-winit", package = "ui-events-floem-winit" }
dpi = { version = "0.1.2", default-features = false }
rustc-hash = "2.1"
imaging = { git = "https://github.com/forest-rs/imaging", rev = "ce53c4bceb10fbb653cb17bd4504ddae223ae169", package = "imaging" }
imaging_wgpu = { git = "https://github.com/forest-rs/imaging", rev = "ce53c4bceb10fbb653cb17bd4504ddae223ae169", package = "imaging_wgpu", features = ["wgpu-28"] }
svg_imaging = { git = "https://github.com/forest-rs/imaging", rev = "ce53c4bceb10fbb653cb17bd4504ddae223ae169", package = "svg_imaging" }
understory_frame_pacing = { git = "https://github.com/jrmoulton/understory", rev = "404febe54512", package = "understory_frame_pacing" }

[dependencies]
slotmap = "1.0"
sha2 = "0.10"
bitflags = "2.9"
indexmap = "2"
rustc-hash.workspace = true
imaging = { workspace = true, features = ["std"] }
imaging_wgpu = { workspace = true }
svg_imaging.workspace = true
smallvec = "1.15"
educe = "0.6"
taffy = { version = "0.9.2", features = ["grid"] }
Expand All @@ -84,25 +81,32 @@ unicode-segmentation = { workspace = true }
peniko = { workspace = true }
imbl.workspace = true
parley = { workspace = true }
fontique = { workspace = true }
serde = { workspace = true, optional = true }
serde_json = { workspace = true }
lapce-xi-rope = { workspace = true, optional = true }
strum = { workspace = true, optional = true }
strum_macros = { workspace = true, optional = true }
paste = "1.0"
floem_renderer = { path = "renderer", version = "0.2.0" }
floem_skia_renderer = { path = "skia", version = "0.2.0", optional = true }
floem_vello_renderer = { path = "vello", version = "0.2.0", optional = true }
floem_vger_renderer = { path = "vger", version = "0.2.0", optional = true }
floem_tiny_skia_renderer = { path = "tiny_skia", version = "0.2.0" }
floem_vger_renderer = { path = "../vger-rs", package = "floem-vger", optional = true }
imaging_vello = { git = "https://github.com/forest-rs/imaging", rev = "ce53c4bceb10fbb653cb17bd4504ddae223ae169", package = "imaging_vello", optional = true }
imaging_skia = { git = "https://github.com/forest-rs/imaging", rev = "ce53c4bceb10fbb653cb17bd4504ddae223ae169", package = "imaging_skia", optional = true, features = ["gpu"] }
imaging_tiny_skia = { git = "https://github.com/forest-rs/imaging", rev = "ce53c4bceb10fbb653cb17bd4504ddae223ae169", package = "imaging_tiny_skia", optional = true }
imaging_vello_cpu = { git = "https://github.com/forest-rs/imaging", rev = "ce53c4bceb10fbb653cb17bd4504ddae223ae169", package = "imaging_vello_cpu", optional = true }
imaging_vello_hybrid = { git = "https://github.com/forest-rs/imaging", rev = "ce53c4bceb10fbb653cb17bd4504ddae223ae169", package = "imaging_vello_hybrid", optional = true }
floem_reactive = { path = "reactive", version = "0.2.0" }
floem-editor-core = { path = "editor-core", version = "0.2.0", optional = true }
subduction_core = { git = "https://github.com/forest-rs/subduction", rev = "c684827b4a4e", package = "subduction_core", features = ["std"] }
subduction_render = { git = "https://github.com/forest-rs/subduction", rev = "c684827b4a4e", package = "subduction_render", features = ["std"] }
subduction = { git = "https://github.com/forest-rs/subduction", rev = "c684827b4a4e", package = "subduction" }
copypasta = { version = "0.10", default-features = false, features = [
"wayland",
"x11",
] }
parking_lot = { workspace = true }
swash = { workspace = true, features = ["scale"] }
image = { workspace = true }
softbuffer = "0.4.1"
winit = { workspace = true }
futures = { version = "0.3.31" }
crossbeam = { version = "0.8", optional = true }
Expand All @@ -114,11 +118,19 @@ fluent-bundle = { version = "0.16", optional = true }
unic-langid = { version = "0.9", optional = true }
sys-locale = { version = "0.3.2", optional = true }
ouroboros = { version = "0.18", optional = true }
understory_box_tree = { git = "https://github.com/jrmoulton/understory", rev = "2c2abb8c4bd9", package = "understory_box_tree" }
understory_event_state = { git = "https://github.com/jrmoulton/understory", rev = "2c2abb8c4bd9", package = "understory_event_state" }
understory_index = { git = "https://github.com/jrmoulton/understory", rev = "2c2abb8c4bd9", package = "understory_index" }
understory_focus = { git = "https://github.com/jrmoulton/understory", rev = "2c2abb8c4bd9", package = "understory_focus" }
understory_virtual_list = { git = "https://github.com/jrmoulton/understory", rev = "2c2abb8c4bd9", package = "understory_virtual_list" }
understory_box_tree = { git = "https://github.com/jrmoulton/understory", rev = "404febe54512", package = "understory_box_tree" }
understory_event_state = { git = "https://github.com/jrmoulton/understory", rev = "404febe54512", package = "understory_event_state" }
understory_index = { git = "https://github.com/jrmoulton/understory", rev = "404febe54512", package = "understory_index" }
understory_focus = { git = "https://github.com/jrmoulton/understory", rev = "404febe54512", package = "understory_focus" }
understory_virtual_list = { git = "https://github.com/jrmoulton/understory", rev = "404febe54512", package = "understory_virtual_list" }
understory_view2d = { git = "https://github.com/jrmoulton/understory", rev = "404febe54512", package = "understory_view2d" }
understory_frame_pacing.workspace = true

# understory_box_tree = { path = "../understory/understory_box_tree", package = "understory_box_tree" }
# understory_event_state = { path = "../understory/understory_event_state", package = "understory_event_state" }
# understory_index = { path = "../understory/understory_index", package = "understory_index" }
# understory_focus = { path = "../understory/understory_focus", package = "understory_focus" }
# understory_virtual_list = { path = "../understory/understory_virtual_list", package = "understory_virtual_list" }

[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies]
muda = { workspace = true }
Expand All @@ -133,23 +145,37 @@ wgpu = { workspace = true }

[target.'cfg(target_os = "windows")'.dependencies]
clipboard-win = "5.4"
subduction_backend_windows = { git = "https://github.com/forest-rs/subduction", rev = "c684827b4a4e", package = "subduction_backend_windows" }

[target.'cfg(target_os = "macos")'.dependencies]
block = "0.1"
metal = "0.33"
objc2 = { version = "0.6", default-features = false }
objc2-foundation = { version = "0.3", default-features = false }
objc2-app-kit = { version = "0.3", features = [
"NSApplication",
"NSResponder",
"NSView",
"objc2-quartz-core",
] }
dispatch2 = {version = "0.3.0"}
subduction_backend_apple = { git = "https://github.com/forest-rs/subduction", rev = "c684827b4a4e", package = "subduction_backend_apple", default-features = false, features = ["appkit", "ca-display-link", "cametal-display-link", "cv-display-link", "wgpu"] }

[features]
default = ["editor", "default-image-formats", "vger", "serde"]
skia = ["dep:floem_skia_renderer"]
vello = ["dep:floem_vello_renderer"]
default = [
"editor",
"default-image-formats",
"serde",
"vger",
"vello-cpu"
]
vello = ["dep:imaging_vello"]
vger = ["dep:floem_vger_renderer"]
# tiny-skia = ["dep:floem_tiny_skia_renderer"]
vello-hybrid = ["dep:imaging_vello_hybrid"]
vello-cpu = ["dep:imaging_vello_cpu"]
skia = ["dep:imaging_skia"]
skia-cpu = ["dep:imaging_skia"]
tiny-skia = ["dep:imaging_tiny_skia"]
serde = [
"dep:serde",
"winit/serde",
Expand Down Expand Up @@ -184,14 +210,27 @@ image-tiff = ["image/tiff"]
image-webp = ["image/webp"]
tokio = ["dep:tokio"]
# rfd (file dialog) async runtime
crossbeam = ["dep:crossbeam", "floem_renderer/crossbeam"]
crossbeam = ["dep:crossbeam"]
localization = ["dep:fluent-bundle", "dep:unic-langid", "dep:sys-locale", "dep:ouroboros"]

[dev-dependencies]
criterion = { version = "0.8.1", features = ["html_reports"] }
floem_renderer = { path = "renderer" }
peniko = { workspace = true }

[patch.crates-io]
kurbo = { git = "https://github.com/linebender/kurbo.git", rev = "6836244be0b280ff68728f100fca0a5fcf62638a", default-features = false }
# naga = { path = "../wgpu/naga" }
# wgpu = { path = "../wgpu/wgpu" }
# wgpu-core = { path = "../wgpu/wgpu-core" }
# wgpu-hal = { path = "../wgpu/wgpu-hal" }
# wgpu-macros = { path = "../wgpu/wgpu-macros" }
# wgpu-types = { path = "../wgpu/wgpu-types" }
vello_common = { path = "../vello/sparse_strips/vello_common" }
vello_hybrid = { path = "../vello/sparse_strips/vello_hybrid" }

[patch."https://github.com/forest-rs/imaging"]
glifo = { git = "https://github.com/linebender/vello", rev = "1cf944ed46500194dcc6ee40392a626e0ec4c8a0", package = "glifo" }

[[bench]]
name = "event_dispatch"
harness = false
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn counter_view() -> impl IntoView {

Inspired by [Xilem](https://github.com/linebender/xilem), [Leptos](https://github.com/leptos-rs/leptos) and [rui](https://github.com/audulus/rui), Floem aims to be a high performance declarative UI library with a highly ergonomic API.

- **Cross-platform**: Floem supports Windows, macOS and Linux with rendering using [wgpu](https://github.com/gfx-rs/wgpu) via [vger](https://github.com/audulus/vger-rs) or [vello](https://github.com/linebender/xilem), or via AnyRender's GPU-backed [Skia](https://github.com/dioxuslabs/anyrender) renderer. In case a GPU is unavailable, a CPU renderer powered by [tiny-skia](https://github.com/RazrFalcon/tiny-skia) will be used.
- **Cross-platform**: Floem supports Windows, macOS and Linux with rendering using [wgpu](https://github.com/gfx-rs/wgpu) via [vger](https://github.com/audulus/vger-rs) or [vello](https://github.com/linebender/xilem), via the Ganesh-backed `skia` renderer, or via the raster `skia-cpu` and [tiny-skia](https://github.com/RazrFalcon/tiny-skia) CPU paths.
- **Fine-grained reactivity**: The entire library is built around reactive primitives inspired by [leptos_reactive](https://crates.io/crates/leptos_reactive). The reactive "signals" allow you to keep your UI up-to-date with minimal effort, all while maintaining very high performance.
- **Performance**: The view tree is constructed only once, safeguarding you from accidentally creating a bottleneck in a view generation function that slows down your entire application. Floem also provides tools to help you write efficient UI code, such as a [virtual list](https://github.com/lapce/floem/tree/main/examples/virtual_list).
- **Flexbox & Grid layout**: Using [Taffy](https://crates.io/crates/taffy), the library provides the Flexbox and Grid layout systems, which can be applied to any View node.
Expand Down
101 changes: 99 additions & 2 deletions benches/painting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

use std::hint::black_box;

use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use criterion::{BatchSize, BenchmarkId, Criterion, criterion_group, criterion_main};
use floem::headless::{HeadlessHarness, TestRoot};
use floem::prelude::*;
use floem::style::FlexWrap;
use floem::unit::Pct;
use floem::views::{Container, Decorators, Empty, Stack};
use floem::views::{Container, Decorators, Empty, Label, ScrollExt, Stack};

// =============================================================================
// View tree creation helpers
Expand Down Expand Up @@ -105,6 +105,29 @@ fn create_wide_tree_depth2(width: usize) -> impl IntoView {
Stack::from_iter(children).style(|s| s.size(100.0, 100.0))
}

/// Create a large vertically scrollable list of labels.
fn create_scroll_label_list(n: usize) -> impl IntoView {
let rows: Vec<_> = (0..n)
.map(|i| {
Label::new(format!(
"Row {i:05} | benchmark label content for retained scroll repaint"
))
.style(|s| {
s.width_full()
.height(24.0)
.padding_horiz(6.0)
.padding_vert(2.0)
})
.into_any()
})
.collect();

Stack::from_iter(rows)
.style(|s| s.flex_col())
.scroll()
.style(|s| s.size(1000.0, 240.0))
}

// =============================================================================
// Flat tree painting benchmarks
// =============================================================================
Expand Down Expand Up @@ -285,6 +308,79 @@ fn bench_repaint_after_change(c: &mut Criterion) {
group.finish();
}

// =============================================================================
// Scroll repaint benchmarks
// =============================================================================

fn bench_scroll_repaint(c: &mut Criterion) {
let mut group = c.benchmark_group("scroll_repaint");

for n in [100, 1_000].iter() {
group.bench_with_input(BenchmarkId::new("initial_paint", n), n, |b, &n| {
b.iter_batched(
|| {
let root = TestRoot::new();
let view = create_scroll_label_list(n);
HeadlessHarness::new_with_size(root, view, 1000.0, 240.0)
},
|mut harness| {
harness.paint();
black_box(());
},
BatchSize::SmallInput,
);
});

group.bench_with_input(
BenchmarkId::new("first_repaint_after_scroll", n),
n,
|b, &n| {
b.iter_batched(
|| {
let root = TestRoot::new();
let view = create_scroll_label_list(n);
let mut harness = HeadlessHarness::new_with_size(root, view, 1000.0, 240.0);
harness.paint();
harness
},
|mut harness| {
harness.scroll_down(160.0, 120.0, 48.0);
harness.paint();
black_box(());
},
BatchSize::SmallInput,
);
},
);

group.bench_with_input(
BenchmarkId::new("steady_repaint_after_scroll", n),
n,
|b, &n| {
let root = TestRoot::new();
let view = create_scroll_label_list(n);
let mut harness = HeadlessHarness::new_with_size(root, view, 320.0, 240.0);
harness.rebuild();
harness.paint();
let mut scroll_down = true;

b.iter(|| {
if scroll_down {
harness.scroll_down(160.0, 120.0, 48.0);
} else {
harness.scroll_up(160.0, 120.0, 48.0);
}
scroll_down = !scroll_down;
harness.paint();
black_box(());
});
},
);
}

group.finish();
}

criterion_group!(
benches,
bench_paint_flat_tree,
Expand All @@ -293,6 +389,7 @@ criterion_group!(
bench_paint_wide_tree,
bench_paint_order_tracking,
bench_repaint_after_change,
bench_scroll_repaint,
);

criterion_main!(benches);
Loading