Squashed from work: - score: a headless music engraving, playback and notation engine - score: the notation app — pianist mode, editing, playback - piano_model: it was a plucked string by construction, and 20 voicings - score: one document you can pan, zoom and navigate - piano_model: the body tap was a click, and the objective was rewarding noise - score: add the sound panel and library modules - piano_model: a second engine, and the attack that finally sounded right - score: two instruments, reverb and brightness — and the rest of the panel gone - score model: a note remembers how it was struck, and the score remembers the pedal - score import: keep the velocities and the pedal the file was carrying - score playback: play the performance, not a flattened copy of it - score ui: the music list moves to the sidebar, and the view stops fighting itself - score: the application ships its font and eight performances - piano_model: a limiter that rides the music, so the knee stops shaping chords - piano_model: the forte bell was the treble's dynamic slope, and the bass was dying at its own prompt rate - piano_model: the bridge decides each partial's decay, and a fixed multiplier cannot say that - piano_model: each partial gets its own two coupled modes, from the eigen algebra - piano_model: a median that fell between the peaks made every bass partial a drain - score-ai: LocalBroker — the seam's in-process implementation over the session engine (aicore P8) - client + chat dispatcher: the dead wire comes out (aicore P7/P8) - score_pdf: the score model grew a pedal map — the pdf importer initialises it - libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly - score app: the shipped-piece test speaks the PERFORMANCES table - zero-warning sweep, round three — the model lanes and the deep examples
156 lines
5.2 KiB
Rust
156 lines
5.2 KiB
Rust
use makepad_score_render::*;
|
|
use std::{sync::Arc, time::Instant};
|
|
|
|
const ITEMS_PER_PAGE: usize = 2_480;
|
|
|
|
fn synthetic_page(page_number: u32) -> Arc<PaintList> {
|
|
let base = page_number as u64 * 1_000_000;
|
|
let notehead = SmuflGlyph::new("noteheadBlack");
|
|
let mut items = Vec::with_capacity(ITEMS_PER_PAGE);
|
|
for i in 0..2_048u64 {
|
|
let x = 10.0 + (i % 64) as f64 * 2.5;
|
|
let y = 10.0 + (i / 64) as f64 * 6.8;
|
|
items.push(PaintItem {
|
|
id: SemanticId(base + i + 1),
|
|
bounds: Rect::from_xywh(x - 0.05, y - 0.45, 1.25, 0.9),
|
|
z: 3,
|
|
ink: Ink::role(InkRole::Primary),
|
|
kind: PaintKind::Glyph(GlyphItem {
|
|
font: MusicFontRef(0),
|
|
glyph: notehead.clone(),
|
|
origin: Point::new(x, y),
|
|
em_size: 4.0,
|
|
}),
|
|
});
|
|
}
|
|
for i in 0..320u64 {
|
|
let y = 8.0 + i as f64 * 0.72;
|
|
items.push(PaintItem::primitive(
|
|
SemanticId(base + 10_000 + i),
|
|
0,
|
|
Ink::role(InkRole::Staff),
|
|
Primitive::Rule {
|
|
rect: Rect::from_xywh(5.0, y, 170.0, 0.13),
|
|
kind: RuleKind::Staff,
|
|
staff_group: Some((i / 5) as u32),
|
|
},
|
|
));
|
|
}
|
|
for i in 0..64u64 {
|
|
let x = 8.0 + (i % 8) as f64 * 20.0;
|
|
let y = 18.0 + (i / 8) as f64 * 28.0;
|
|
items.push(PaintItem::primitive(
|
|
SemanticId(base + 20_000 + i),
|
|
1,
|
|
Ink::role(InkRole::Primary),
|
|
Primitive::Beam(Beam {
|
|
start: Point::new(x, y),
|
|
end: Point::new(x + 12.0, y + (i % 3) as f64 * 0.25),
|
|
thickness: 0.50,
|
|
}),
|
|
));
|
|
}
|
|
for i in 0..32u64 {
|
|
let x = 8.0 + (i % 4) as f64 * 40.0;
|
|
let y = 22.0 + (i / 4) as f64 * 28.0;
|
|
items.push(PaintItem::primitive(
|
|
SemanticId(base + 30_000 + i),
|
|
2,
|
|
Ink::role(InkRole::Primary),
|
|
Primitive::Ribbon(Ribbon {
|
|
curve: Cubic {
|
|
p0: Point::new(x, y),
|
|
p1: Point::new(x + 4.0, y - 2.0),
|
|
p2: Point::new(x + 12.0, y - 2.0),
|
|
p3: Point::new(x + 16.0, y),
|
|
},
|
|
endpoint_thickness: 0.10,
|
|
midpoint_thickness: 0.22,
|
|
}),
|
|
));
|
|
}
|
|
for i in 0..16u64 {
|
|
let x = 8.0 + (i % 4) as f64 * 40.0;
|
|
let y = 30.0 + (i / 4) as f64 * 52.0;
|
|
items.push(PaintItem {
|
|
id: SemanticId(base + 40_000 + i),
|
|
bounds: Rect::from_xywh(x, y - 2.0, 18.0, 3.0),
|
|
z: 4,
|
|
ink: Ink::role(InkRole::Secondary),
|
|
kind: PaintKind::Text(TextRun {
|
|
font: TextFontRef(0),
|
|
text: Arc::from("dolce e cantabile"),
|
|
origin: Point::new(x, y),
|
|
size: 2.4,
|
|
letter_spacing: 0.0,
|
|
direction: TextDirection::LeftToRight,
|
|
language: Some(Arc::from("it")),
|
|
}),
|
|
});
|
|
}
|
|
assert_eq!(items.len(), ITEMS_PER_PAGE);
|
|
Arc::new(
|
|
PaintList::new(
|
|
PageId(page_number),
|
|
1,
|
|
Point::new(180.0, 260.0),
|
|
items,
|
|
)
|
|
.unwrap(),
|
|
)
|
|
}
|
|
|
|
fn measure(page_count: usize) -> (usize, usize, u128, u128, usize) {
|
|
let build_started = Instant::now();
|
|
let pages: Vec<_> = (0..page_count as u32).map(synthetic_page).collect();
|
|
let build_us = build_started.elapsed().as_micros();
|
|
|
|
let scale = 0.12;
|
|
let views: Vec<_> = pages
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(index, page)| PageView {
|
|
page: page.clone(),
|
|
transform: Transform {
|
|
translation: Point::new(
|
|
(index % 10) as f64 * (180.0 * scale + 4.0),
|
|
(index / 10) as f64 * (260.0 * scale + 4.0),
|
|
),
|
|
scale,
|
|
},
|
|
})
|
|
.collect();
|
|
let cull_started = Instant::now();
|
|
let plan = RenderPlanner.plan(
|
|
&views,
|
|
Rect::from_xywh(0.0, 0.0, 400.0, 260.0),
|
|
&OverlayState::default(),
|
|
OverlayMetrics::default(),
|
|
);
|
|
let cull_us = cull_started.elapsed().as_micros();
|
|
let items = pages.iter().map(|page| page.items().len()).sum();
|
|
let memory = pages.iter().map(|page| page.memory_bytes()).sum::<usize>()
|
|
+ plan.memory_bytes();
|
|
eprintln!(
|
|
"score_render_perf pages={page_count} items={items} draw_calls={} cull_us={cull_us} build_us={build_us} memory_bytes={memory}",
|
|
plan.draw_calls(),
|
|
);
|
|
assert_eq!(items, page_count * ITEMS_PER_PAGE);
|
|
assert_eq!(plan.visible_items(), items);
|
|
assert_eq!(plan.culled_items, 0);
|
|
assert!(plan.draw_calls() <= 6);
|
|
assert!(build_us < 10_000_000, "synthetic page build exceeded 10 seconds");
|
|
assert!(cull_us < 2_000_000, "viewport planning exceeded 2 seconds");
|
|
assert!(memory < 128 * 1024 * 1024, "retained data exceeded 128 MiB");
|
|
(items, plan.draw_calls(), build_us, cull_us, memory)
|
|
}
|
|
|
|
#[test]
|
|
fn performance_1_10_50_pages() {
|
|
let one = measure(1);
|
|
let ten = measure(10);
|
|
let fifty = measure(50);
|
|
assert!(fifty.0 >= 100_000);
|
|
assert_eq!(one.1, ten.1);
|
|
assert_eq!(ten.1, fifty.1);
|
|
}
|