mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-09 11:59:36 -06:00
render: look up a table's style once, keep the skip list as a set, count each dash
This commit is contained in:
@@ -131,9 +131,11 @@ fn place(out: &mut Placed, rctx: &Ctx, fields: &SlideFields, s: &Shape, parent:
|
||||
}
|
||||
ShapeKind::Table(t) => {
|
||||
// Cell text, laid out exactly as the table draws it.
|
||||
// The table's style is looked up once, not once per cell.
|
||||
let def = table::resolve_style(rctx, t);
|
||||
for (r, c, rect) in table::cell_rects(t, &table::row_heights(rctx, t, fields)) {
|
||||
let Some(cell) = t.cell(r, c).filter(|cell| !cell.text.is_empty()) else { continue };
|
||||
let body = table::cell_body(rctx, t, r, c, cell);
|
||||
let body = table::cell_body_with_def(rctx, def, t, r, c, cell);
|
||||
let tmp = Shape::table_cell(body.clone());
|
||||
place_text(out, rctx, fields, s.id, &tmp, &body, rect, m);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ pub struct Renderer {
|
||||
/// Visits spent on outer-shadow silhouette passes, kept apart so they cannot use up the budget
|
||||
/// of the normal drawing that follows.
|
||||
shadow_work: usize,
|
||||
/// `RenderOpts::skip` as a set, with the slice (address, length) it was built from: the
|
||||
/// slice can be as long as the slide, and is tested for every shape drawn. Forgotten when a
|
||||
/// frame starts, so another frame's slice at the same address is never mistaken for it.
|
||||
skip: Option<(usize, usize, std::collections::HashSet<ShapeId>)>,
|
||||
}
|
||||
|
||||
impl Default for Renderer {
|
||||
@@ -48,6 +52,7 @@ impl Default for Renderer {
|
||||
in_shadow_pass: false,
|
||||
render_work: 0,
|
||||
shadow_work: 0,
|
||||
skip: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,6 +168,7 @@ impl Renderer {
|
||||
pub(crate) fn slide(&mut self, pres: &Presentation, slide: &Slide, index: usize, opts: &RenderOpts) -> Image {
|
||||
self.render_work = 0;
|
||||
self.shadow_work = 0;
|
||||
self.skip = None;
|
||||
self.in_shadow_pass = false;
|
||||
let (mut ctx, w, h, view) = Self::canvas(pres, opts, needs_filters(pres, slide));
|
||||
let f = Frame { pres, opts, fields: Self::fields(pres, index, opts) };
|
||||
@@ -178,6 +184,7 @@ impl Renderer {
|
||||
pub(crate) fn slide_part(&mut self, pres: &Presentation, slide: &Slide, index: usize, part: SlidePart, opts: &RenderOpts) -> Image {
|
||||
self.render_work = 0;
|
||||
self.shadow_work = 0;
|
||||
self.skip = None;
|
||||
self.in_shadow_pass = false;
|
||||
let (mut ctx, w, h, view) = Self::canvas(pres, opts, needs_filters(pres, slide));
|
||||
let f = Frame { pres, opts, fields: Self::fields(pres, index, opts) };
|
||||
@@ -212,6 +219,7 @@ impl Renderer {
|
||||
let mix = if mix.is_finite() { mix.clamp(0.0, 1.0) } else { 1.0 };
|
||||
self.render_work = 0;
|
||||
self.shadow_work = 0;
|
||||
self.skip = None;
|
||||
self.in_shadow_pass = false;
|
||||
let (mut ctx, w, h, view) = Self::canvas(pres, opts, needs_filters(pres, old.0) || needs_filters(pres, new.0));
|
||||
let (Some(co), Some(cn)) = (Ctx::for_slide(pres, old.0), Ctx::for_slide(pres, new.0)) else { return self.finish(ctx, w, h) };
|
||||
@@ -260,6 +268,7 @@ impl Renderer {
|
||||
pub(crate) fn layout(&mut self, pres: &Presentation, mi: usize, li: Option<usize>, opts: &RenderOpts) -> Image {
|
||||
self.render_work = 0;
|
||||
self.shadow_work = 0;
|
||||
self.skip = None;
|
||||
self.in_shadow_pass = false;
|
||||
let (w, h) = output_size(pres, opts);
|
||||
let mut ctx = RenderContext::new_with(w, h, vello_cpu::RenderSettings { num_threads: 0, ..Default::default() });
|
||||
@@ -385,8 +394,25 @@ impl Renderer {
|
||||
geo
|
||||
}
|
||||
|
||||
/// Whether `id` is in `skip`, looked up in a set built once per frame and slice.
|
||||
fn skipped(&mut self, skip: &[ShapeId], id: ShapeId) -> bool {
|
||||
if skip.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let key = (skip.as_ptr() as usize, skip.len());
|
||||
match &self.skip {
|
||||
Some((p, n, set)) if (*p, *n) == key => set.contains(&id),
|
||||
_ => {
|
||||
let set: std::collections::HashSet<ShapeId> = skip.iter().copied().collect();
|
||||
let hit = set.contains(&id);
|
||||
self.skip = Some((key.0, key.1, set));
|
||||
hit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shape_at(&mut self, ctx: &mut RenderContext, f: &Frame, rctx: &Ctx, s: &Shape, parent: Affine, depth: usize) {
|
||||
if depth > 32 || (s.hidden && !f.opts.show_hidden) || f.opts.skip.contains(&s.id) {
|
||||
if depth > 32 || (s.hidden && !f.opts.show_hidden) || self.skipped(f.opts.skip, s.id) {
|
||||
return;
|
||||
}
|
||||
let work = if self.in_shadow_pass { &mut self.shadow_work } else { &mut self.render_work };
|
||||
|
||||
@@ -191,7 +191,8 @@ pub(crate) fn limit_dash_count(path: &BezPath, pat: Vec<f64>) -> Vec<f64> {
|
||||
return pat;
|
||||
}
|
||||
let len: f64 = path.segments().map(|s| s.arclen(1e-2)).sum();
|
||||
let dashes = len / period;
|
||||
// Every dash of the pattern counts, not each repeat of it: a custom pattern can hold many.
|
||||
let dashes = len / period * pat.len().div_ceil(2) as f64;
|
||||
if !len.is_finite() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
@@ -798,4 +798,8 @@ fn a_tiny_dash_on_a_huge_shape_draws_without_running_out_of_memory() {
|
||||
p.line_to((4.0e6, 0.0));
|
||||
let pat = limit_dash_count(&p, vec![1.0, 1.0]);
|
||||
assert!((4.0e6 / (pat[0] + pat[1]) - MAX_DASHES).abs() < 1.0, "{pat:?}");
|
||||
// A custom pattern of 1,000 dashes counts each of them: the limit is on dashes drawn.
|
||||
let pat = limit_dash_count(&p, vec![1.0; 2000]);
|
||||
let period: f64 = pat.iter().sum();
|
||||
assert!((4.0e6 / period * 1000.0 - MAX_DASHES).abs() < 1.0, "{}", period);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user