makepad/widgets/tests/apply_bench.rs
Admin 53e0e85b92 draw and widgets: Slug text everywhere, no blank glyphs, slimmer widgets
All outline text draws with Slug curves on every platform (SDF/MSDF stays
selectable per size); a frame never presents a blank glyph and retained draw
lists keep theirs. One coverage model for vector feathering, widget families
split out of the core crate, fewer steady-state allocations, video keeps its
aspect (Contain/Cover), render and SSAO pass unification.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

137 lines
5.8 KiB
Rust

//! Native timing of the derived `script_apply` on real widgets (rustc, no Relax): (a) the full
//! document apply of a fresh widget, (b) a small animator-sized block (`{height: 0}`) applied
//! to a big widget, (c) a hot-reload re-apply (`Apply::Reload`) of the whole document.
//! Prints the median of 15 batches per case, in microseconds per apply.
//! Run: cargo test --release -p makepad-widgets --test apply_bench -- --ignored --nocapture
use makepad_widgets::makepad_script::script;
use makepad_widgets::*;
use std::time::Instant;
fn median_us(cx: &mut Cx, iters: usize, mut f: impl FnMut(&mut Cx)) -> f64 {
for _ in 0..iters {
f(cx);
}
let mut v: Vec<f64> = (0..15)
.map(|_| {
let t = Instant::now();
for _ in 0..iters {
f(cx);
}
t.elapsed().as_secs_f64() * 1e6 / iters as f64
})
.collect();
v.sort_by(|a, b| a.partial_cmp(b).unwrap());
v[7]
}
#[test]
#[ignore]
fn apply_bench() {
let mut cx = Cx::new(Box::new(|_, _| {}));
cx.with_vm(|vm| makepad_widgets::script_mod(vm));
let docs: Vec<(&str, ScriptValue)> = vec![
(
"Button",
cx.with_vm(|vm| {
vm.eval(script! {
use mod.prelude.widgets.*
Button{ text: "Hello" }
})
}),
),
(
"View (big)",
cx.with_vm(|vm| {
vm.eval(script! {
use mod.prelude.widgets.*
View{
width: Fill height: Fit flow: Down spacing: 4 padding: 8
show_bg: true draw_bg.color: #x334
a := Label{ text: "a" } b := Button{ text: "b" } c := Slider{ text: "c" }
d := Label{ text: "d" } e := Button{ text: "e" } f := Slider{ text: "f" }
g := View{ flow: Right h := Label{ text: "h" } i := Button{ text: "i" } j := CheckBox{ text: "j" } }
k := View{ flow: Right l := Label{ text: "l" } m := Button{ text: "m" } n := CheckBox{ text: "n" } }
o := TextInput{ text: "o" } p := DropDown{ labels: ["x" "y" "z"] } q := Label{ text: "q" }
r := Button{ text: "r" } s := Slider{ text: "s" } t := Label{ text: "t" }
}
})
}),
),
(
"Slider",
cx.with_vm(|vm| {
vm.eval(script! {
use mod.prelude.widgets.*
Slider{ text: "Gain" min: 0.0 max: 10.0 }
})
}),
),
];
let small = cx.with_vm(|vm| {
vm.eval(script! {
{height: 0}
})
});
for (name, doc) in docs {
// (a) full document apply: a fresh widget from the document
let a = median_us(&mut cx, 200, |cx| {
let w = cx.with_vm(|vm| WidgetRef::script_from_value(vm, doc));
drop(w);
});
let mut widget = cx.with_vm(|vm| WidgetRef::script_from_value(vm, doc));
// (b) a small state block on the built widget
let b = median_us(&mut cx, 2000, |cx| {
cx.with_vm(|vm| widget.script_apply(vm, &Apply::Animate, &mut Scope::empty(), small));
});
// (c) hot reload: the whole document re-applied
let c = median_us(&mut cx, 200, |cx| {
cx.with_vm(|vm| widget.script_apply(vm, &Apply::Reload, &mut Scope::empty(), doc));
});
println!("apply_bench {name:<12} (a) new {a:9.2} us (b) small block {b:8.3} us (c) reload {c:9.2} us");
}
}
/// Native timing of one draw-shader compile as a platform does it: compile_shader for each
/// backend's options, the draw description, and a read of every io row.
/// Run: cargo test --release -p makepad-widgets --test apply_bench shader_bench -- --ignored --nocapture
#[test]
#[ignore]
fn shader_bench() {
use makepad_widgets::makepad_script::shader_backend::ShaderBackend;
use makepad_widgets::makepad_script::shader_compile::{compile_shader, ShaderCompileOptions};
use makepad_widgets::makepad_script::shader_reflection::DrawShaderDesc;
let mut cx = Cx::new(Box::new(|_, _| {}));
cx.with_vm(|vm| makepad_widgets::script_mod(vm));
let shaders: Vec<(&str, ScriptObject)> = ["Button", "Slider", "CheckBox"]
.iter()
.map(|name| {
let w = cx.with_vm(|vm| match *name {
"Button" => vm.eval(script! { use mod.prelude.widgets.* Button{} }),
"Slider" => vm.eval(script! { use mod.prelude.widgets.* Slider{} }),
_ => vm.eval(script! { use mod.prelude.widgets.* CheckBox{} }),
});
let o = w.as_object().unwrap();
let bg = cx.with_vm(|vm| vm.bx.heap.value(o, id!(draw_bg).into(), NoTrap)).as_object().unwrap();
(*name, bg)
})
.collect();
for (name, shader) in shaders {
for backend in [ShaderBackend::Glsl, ShaderBackend::Metal, ShaderBackend::Hlsl] {
let opts = ShaderCompileOptions::new(backend, false, false, false, false);
let mut sink = 0usize;
let t = median_us(&mut cx, 20, |cx| {
cx.with_vm(|vm| {
let out = compile_shader(vm, shader, &opts);
let desc = DrawShaderDesc::from_output(vm.heap(), &out);
sink += out.combined().len() + out.glsl().map_or(0, |g| g.vertex().len()) + out.table_consts().len();
for io in desc.io() {
sink += io.name().0 as usize & 1 + io.ty().slots() + io.block_name().len() + io.sampler();
}
sink += desc.scope_uniforms().len() + desc.samplers().len();
})
});
println!("shader_bench {name:<9} {backend:?}: {t:9.1} us per compile+describe ({sink})");
}
}
}