Files
effectcraft/crates/project/src/lib.rs
T
Eduardo EspinosaandClaude Opus 5.5 e1d66a3c5c perf: share unchanged layers between undo states
Every undoable edit keeps the previous project for undo, so the comp it touches
is shared and Arc::make_mut deep-copies it: every layer and every keyframe. An
edit therefore costs time in proportion to the comp's size, and a script that
sets many keyframes in a big comp goes quadratic (a 1,600-layer build with
~100k sampled keys took ~45 minutes).

Comp::layers is now a Layers collection backed by Vec<Arc<Layer>>: copying the
stack copies pointers, and only the layer an edit changes is copied. The API
mirrors Vec<Layer>; serialized form is unchanged (a list of layers).
Comp::layer_mut and the iter_mut().filter/find call sites use find_mut /
iter_mut_where so they copy only the layers they actually change.

- prop.addKey in a 400-layer comp: 1.36 ms -> 0.16 ms per key (flat in comp size)
- the 1,600-layer script build: ~45 min -> 47 s, frames pixel-identical
- regression tests: an edit leaves the other layers shared with undo; layers
  round-trip as a plain list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkpGcKfWyCudfSsQNiWAaG
2026-10-28 22:20:23 -04:00

68 lines
2.5 KiB
Rust

//! The EffectCraft document model.
//!
//! `Project` → items (folders, compositions, footage, solids) → `Comp` → `Layer` → property tree
//! ([`props`]). Compositions, and each layer within them ([`Layers`]), sit behind `Arc` so undo
//! snapshots share everything that did not change.
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
pub mod build;
pub mod essential;
mod file_check;
pub mod limits;
pub mod props;
pub mod render_queue;
pub mod render_templates;
pub mod sequence;
pub mod styles;
pub mod tracking;
use std::collections::BTreeMap;
use std::sync::Arc;
pub use effectcraft_color::ColorSpace;
use effectcraft_color::{BlendMode, Label};
use effectcraft_time::{FrameRate, Tick};
pub use props::{Expression, FeatherFalloff, GroupKind, MaskMode, MaskMotionBlur, Node, ParamUi, PropGroup, Property, Uid, parse_path};
use serde::{Deserialize, Serialize};
pub use effectcraft_keyframe as keyframe;
pub use effectcraft_keyframe::{Keyframe, Value};
mod comp;
mod footage;
mod item;
mod layer;
mod layers;
mod settings;
pub use comp::{Comp, Guide, Renderer, Resolution};
pub use footage::{AlphaMode, FieldOrder, Footage, FootageKind, Proxy, Solid, SourceLayer};
pub use item::{Item, ItemId, ItemKind, LayerId};
pub use layers::Layers;
pub use layer::{AutoOrient, FrameBlend, Layer, LayerSource, LightKind, MatteKind, PrimitiveKind, Quality, Sampling, Switches, TrackMatte};
pub use settings::{
APP_VERSION, BitDepth, ColorEngine, CuePoint, HdrMode, Marker, ProjectError, ProjectSettings, SCHEMA_EFFECT_SPACE, SCHEMA_VERSION, SavedView,
TimeDisplayStyle, is_newer_version, saved_by, saved_view,
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Project {
pub schema: u32,
pub settings: ProjectSettings,
pub items: BTreeMap<ItemId, Item>,
pub next_id: u64,
/// The Render Queue (Composition ▸ Add to Render Queue).
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub render_queue: Vec<render_queue::RenderQueueItem>,
/// Render Settings / Output Module templates and their defaults.
#[serde(default, skip_serializing_if = "render_templates::RenderTemplates::is_default")]
pub render_templates: render_templates::RenderTemplates,
/// Render Queue preferences (Notify, storage overflow folders).
#[serde(default, skip_serializing_if = "render_queue::RenderQueuePrefs::is_default")]
pub render_prefs: render_queue::RenderQueuePrefs,
}
#[cfg(test)]
mod tests;