Render R2: persist named render setups without re-evaluating geometry (#667)

render.settings.set saves validated named render setups (camera, output size, exposure, environment) as one undo step, and render.settings.get reads them back without applying a camera. When validated render settings are the only change, the engine keeps the evaluated model instead of rebuilding it. Setups are saved under design format 21; format-20 designs read as having none. Fixes #658; refs #620.

Contributed by @0xlillith.

Co-authored-by: lillith <35337584+0xlillith@users.noreply.github.com>
This commit is contained in:
selune authored and GitHub committed 2026-10-14 17:11:39 -04:00
1 parent 771a11feab
commit 289b521a65
22 files changed
+641 -16

No files matched your search

+27 -1
View File
@@ -477,6 +477,23 @@ impl Session {
}
}
/// Only persistent render intent changed. Never evaluate or clear a stale model here.
fn render_only_change(&self, before: &Snapshot) -> bool {
self.active_sketch == before.active_sketch && self.pending_moves == before.pending_moves && self.doc.differs_only_in_render(&before.doc)
}
fn refresh_render(&mut self, previous: &Arc<Document>) {
self.revision += 1;
// Geometry/placements have been proved identical. Retarget the cache's document key
// instead of discarding prepared meshes merely because the document Arc changed.
if let Ok(mut cache) = self.world_cache.lock()
&& let Some(cache) = cache.as_mut()
&& Arc::ptr_eq(&cache.doc, previous)
{
cache.doc = self.doc.clone();
}
}
/// Show the sketch a live frame changed, leaving the features after it as they were last
/// evaluated (marked stale); a full `refresh` when the model cannot show it that way.
fn refresh_live(&mut self, edit: LiveEdit) {
@@ -583,6 +600,8 @@ impl Session {
Ok((v, live_edit)) => {
let v = if changed { self.with_created(spec.id, params, &before, v) } else { v };
if changed {
let render_only = self.render_only_change(&before);
let previous_doc = before.doc.clone();
if spec.undoable {
self.undo.push(before);
if self.undo.len() > MAX_UNDO {
@@ -592,6 +611,7 @@ impl Session {
}
match live_edit {
Some(edit) => self.refresh_live(edit),
None if render_only => self.refresh_render(&previous_doc),
None => self.refresh(),
}
}
@@ -653,13 +673,19 @@ impl Session {
pub fn restore_checkpoint(&mut self, id: u64) -> Result<()> {
let i = self.checkpoints.iter().position(|c| c.id == id).ok_or_else(|| EngineError::Other(format!("no checkpoint {id}")))?;
let Some(c) = self.checkpoints.drain(i..).next() else { return Err(EngineError::Other(format!("no checkpoint {id}"))) };
let before =
Snapshot { label: String::new(), doc: self.doc.clone(), active_sketch: self.active_sketch, pending_moves: self.pending_moves.clone() };
self.doc = c.doc;
self.active_sketch = c.active_sketch;
self.pending_moves = c.pending_moves;
self.undo = c.undo;
self.redo = c.redo;
self.selection.clear();
self.refresh();
if self.render_only_change(&before) {
self.refresh_render(&before.doc);
} else if !Arc::ptr_eq(&self.doc, &before.doc) || self.active_sketch != before.active_sketch || self.pending_moves != before.pending_moves {
self.refresh();
}
Ok(())
}