Files
hcie-rust-v3.05/hcie-engine-api/tests/performance_stroke_4k.rs
T
phantom b09795ccff Implement stable serialization and restoration for dock layouts, including auto-hidden and floating panels
- Add `persistence.rs` for stable serialization of dock layouts without runtime identifiers.
- Introduce `preview.rs` for geometry handling of dock drop previews.
- Create `sizing.rs` to manage content-aware sizing policies and constraint solving for dock panels.
- Implement `welcome.rs` to provide a welcome surface when no documents are open, featuring New and Open actions.
2026-07-16 22:10:22 +03:00

144 lines
5.5 KiB
Rust

//! 4K multi-layer brush-stroke performance benchmark.
//!
//! ## Purpose
//! Measures the per-segment cost of painting on a large (3840x2160), multi-layer
//! document using the public `Engine` API. This benchmark is intentionally
//! **non-gating** — it only prints timing information so engineers can compare
//! before/after optimizations manually. It must never break the deterministic
//! golden visual regression suite.
//!
//! ## Logic & Workflow
//! 1. Creates a 3840x2160 document.
//! 2. Fills 10 raster layers with semi-random opaque color so that every layer
//! contributes to the composite (worst-case workload for the painter).
//! 3. Selects the top layer and begins a brush stroke.
//! 4. Issues 100 `stroke_to` calls across the canvas, calling
//! `render_composite_region` after every segment to mimic the GUI render loop.
//! 5. Ends the stroke and prints the average time per segment, per composite,
//! and the total wall-clock time.
//!
//! ## Arguments & Returns
//! This is a standard Rust test: `cargo test -p hcie-engine-api --test performance_stroke_4k -- --nocapture`.
//! It returns nothing and asserts only that the engine remains in a valid state.
//!
//! ## Side Effects / Dependencies
//! - Allocates ~400MB of pixel/tile/mask buffers.
//! - Uses `std::time::Instant` for measurement; results depend on the host CPU.
use hcie_engine_api::{BrushStyle, BrushTip, Engine};
const W: u32 = 3840;
const H: u32 = 2160;
const LAYERS: usize = 10;
const SEGMENTS: usize = 100;
const COMPOSITE_EVERY_N_SEGMENTS: usize = 1;
fn make_solid_color(r: u8, g: u8, b: u8) -> [u8; 4] {
[r, g, b, 255]
}
#[test]
fn benchmark_4k_stroke_on_multilayer_document() {
let mut engine = Engine::new(W, H);
// Fill the default background layer.
let bg_id = engine.active_layer_id();
engine.set_active_layer(bg_id);
engine.draw_filled_rect_rgba(0, 0, W, H, make_solid_color(240, 240, 240));
let mut layer_ids = vec![bg_id];
// Create and fill additional raster layers so every layer contributes.
for i in 1..LAYERS {
let id = engine.add_layer(&format!("Layer {}", i));
engine.set_active_layer(id);
let color = match i % 4 {
0 => make_solid_color(180, 80, 80),
1 => make_solid_color(80, 180, 80),
2 => make_solid_color(80, 80, 180),
_ => make_solid_color(160, 140, 100),
};
engine.draw_filled_rect_rgba(0, 0, W, H, color);
layer_ids.push(id);
}
// Switch to the top layer for painting.
let top_id = *layer_ids.last().expect("at least one layer");
engine.set_active_layer(top_id);
let mut tip = BrushTip::default();
tip.style = BrushStyle::Round;
tip.size = 24.0;
tip.opacity = 1.0;
tip.hardness = 0.85;
tip.spacing = 0.1;
engine.set_brush_tip(tip);
engine.set_color([220, 60, 60, 255]);
engine.set_eraser(false);
// Start the stroke roughly in the center.
let start_x = W as f32 / 2.0;
let start_y = H as f32 / 2.0;
engine.begin_stroke(top_id, start_x, start_y);
let mut segment_times = Vec::with_capacity(SEGMENTS);
let mut composite_times = Vec::with_capacity(SEGMENTS);
let total_start = std::time::Instant::now();
for i in 0..SEGMENTS {
let t = (i as f32) / SEGMENTS as f32;
let angle = t * 6.28 * 2.0;
let radius = 400.0 * t;
let x = start_x + angle.cos() * radius;
let y = start_y + angle.sin() * radius;
let seg_start = std::time::Instant::now();
engine.stroke_to(top_id, x, y, 0.8);
segment_times.push(seg_start.elapsed().as_micros() as f64);
if (i + 1) % COMPOSITE_EVERY_N_SEGMENTS == 0 {
let comp_start = std::time::Instant::now();
let (region, ptr, _size) = engine.render_composite_region();
// Ensure the engine actually produced a buffer pointer; do not
// optimize away the call.
assert!(!ptr.is_null());
let comp_us = comp_start.elapsed().as_micros() as f64;
composite_times.push(comp_us);
// Keep the region alive so the compiler doesn't discard it.
let _ = region;
}
}
engine.end_stroke(top_id);
let total_us = total_start.elapsed().as_micros() as f64;
let avg_segment_us = segment_times.iter().sum::<f64>() / segment_times.len().max(1) as f64;
let avg_composite_us =
composite_times.iter().sum::<f64>() / composite_times.len().max(1) as f64;
let max_segment_us = segment_times.iter().copied().fold(0.0, f64::max);
let max_composite_us = composite_times.iter().copied().fold(0.0, f64::max);
println!("\n=== 4K Multi-Layer Stroke Benchmark ===");
println!(
"Canvas: {}x{}, Layers: {}, Segments: {}",
W, H, LAYERS, SEGMENTS
);
println!("Average stroke_to: {:>8.1} us", avg_segment_us);
println!("Max stroke_to: {:>8.1} us", max_segment_us);
println!("Average composite: {:>8.1} us", avg_composite_us);
println!("Max composite: {:>8.1} us", max_composite_us);
println!(
"Total wall time: {:>8.1} us ({:.2} s)",
total_us,
total_us / 1_000_000.0
);
println!(
"Segments per second: {:>8.1}",
SEGMENTS as f64 / (total_us / 1_000_000.0)
);
// Soft sanity check: the engine must still report a valid top layer and the
// document must be marked modified after painting.
assert_eq!(engine.active_layer_id(), top_id);
assert!(engine.is_modified());
}