perf: speed large drawing workflows

This commit is contained in:
Hakan Seven committed 2026-07-25 09:05:52 +03:00
1 parent 97be4fefaa
commit 39449ec684
10 files changed
+700 -195

No files matched your search

+4
View File
@@ -226,6 +226,9 @@ pub(super) struct DocumentTab {
pub(super) active_mleader_style: String,
/// Last camera_generation value written back to the document.
pub(super) last_synced_camera_gen: u64,
/// Render-state key of `scene.document.preview`. Matching saves reuse the
/// encoded DWG thumbnail instead of rescanning every resident wire.
pub(super) thumbnail_cache_key: Option<super::ThumbnailCacheKey>,
/// Sentinel "Welcome / Start" tab. Always at index 0 when present.
/// Cannot be closed; the viewport area renders a welcome page instead
/// of the model-space shader. The scene is still constructed so the
@@ -460,6 +463,7 @@ impl DocumentTab {
block_edit: None,
active_mleader_style: "Standard".to_string(),
last_synced_camera_gen: 0,
thumbnail_cache_key: None,
is_start: false,
pan_mode: false,
plugin_state: HashMap::new(),
+11
View File
@@ -865,6 +865,15 @@ pub(super) enum SaveContinuation {
Quit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct ThumbnailCacheKey {
epoch: u64,
camera_generation: u64,
bg_color: [u32; 4],
png: bool,
viewport: [u32; 2],
}
#[derive(Debug, Clone)]
pub struct SaveOutcome {
job_id: u64,
@@ -877,6 +886,8 @@ pub struct SaveOutcome {
set_current_path: bool,
purpose: SavePurpose,
continuation: SaveContinuation,
thumbnail_key: Option<ThumbnailCacheKey>,
refreshed_preview: Option<Option<acadrust::Preview>>,
result: Result<(), String>,
}
+36 -31
View File
@@ -750,24 +750,6 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
Task::none()
}
/// Rasterize the current drawing into the document's DWG preview image, so
/// a saved file shows a thumbnail in file browsers and other CAD apps.
/// A degenerate/empty drawing clears any stale preview.
/// `target` is the version the file is about to be written as — a PNG
/// preview (few KB vs a ~180 KB DIB) is only valid from R2013 (AC1027) on.
pub(super) fn stamp_thumbnail(&mut self, i: usize, target: acadrust::DxfVersion) {
let png = target >= acadrust::DxfVersion::AC1027;
// Frame the thumbnail exactly as the model pane shows it now (current
// pan/zoom/rotation, visible region only), so pass the pane pixel size.
self.tabs[i].scene.document.preview =
crate::io::thumbnail::from_scene(&self.tabs[i].scene, png, self.vp_size);
// The on-disk thumbnail will change — drop the cached Start-page handle
// so it re-reads the updated file on the next refresh.
if let Some(p) = self.tabs[i].current_path.clone() {
self.recent_thumbs.remove(&p);
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(in crate::app) fn prepare_native_save(&mut self, i: usize) {
self.sync_vport_display(i);
@@ -800,17 +782,29 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let epoch = self.tabs[i].scene.geometry_epoch;
let revision = self.tabs[i].edit_revision;
let camera_generation = self.tabs[i].scene.camera_generation;
let thumbnail = if purpose == crate::app::SavePurpose::Autosave {
let thumbnail_key = (purpose != crate::app::SavePurpose::Autosave).then(|| {
let scene = &self.tabs[i].scene;
crate::app::ThumbnailCacheKey {
epoch,
camera_generation,
bg_color: scene.bg_color.map(f32::to_bits),
png: version >= acadrust::DxfVersion::AC1027,
viewport: [self.vp_size.0.to_bits(), self.vp_size.1.to_bits()],
}
});
let thumbnail = if thumbnail_key == self.tabs[i].thumbnail_cache_key {
None
} else {
let scene = &self.tabs[i].scene;
Some((
scene.entity_wires(),
scene.camera.borrow().clone(),
scene.bg_color,
version >= acadrust::DxfVersion::AC1027,
self.vp_size,
))
thumbnail_key.map(|key| {
let scene = &self.tabs[i].scene;
(
scene.entity_wires(),
scene.camera.borrow().clone(),
scene.bg_color,
key.png,
self.vp_size,
)
})
};
let clone_started = std::time::Instant::now();
let mut snapshot = self.tabs[i].scene.document.clone();
@@ -833,7 +827,8 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
Task::perform(
async move {
let result = std::thread::spawn(move || {
let (result, refreshed_preview) = std::thread::spawn(move || {
let mut refreshed_preview = None;
if let Some((wires, camera, bg_color, png, viewport)) = thumbnail {
let started = std::time::Instant::now();
snapshot.preview = crate::io::thumbnail::from_snapshot(
@@ -850,16 +845,18 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
wires.len(),
);
}
refreshed_preview = Some(snapshot.preview.clone());
}
crate::io::save_owned_as_version_atomic(
let result = crate::io::save_owned_as_version_atomic(
snapshot,
&worker_path,
version,
backup,
)
);
(result, refreshed_preview)
})
.join()
.unwrap_or_else(|_| Err("save worker panicked".to_string()));
.unwrap_or_else(|_| (Err("save worker panicked".to_string()), None));
crate::app::SaveOutcome {
job_id,
tab_id,
@@ -871,6 +868,8 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
set_current_path,
purpose,
continuation,
thumbnail_key,
refreshed_preview,
result,
}
},
@@ -921,6 +920,12 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let snapshot_is_current = self.tabs[i].scene.geometry_epoch == outcome.epoch
&& self.tabs[i].edit_revision == outcome.revision
&& self.tabs[i].scene.camera_generation == outcome.camera_generation;
if snapshot_is_current && outcome.purpose != crate::app::SavePurpose::Autosave {
if let Some(preview) = outcome.refreshed_preview {
self.tabs[i].scene.document.preview = preview;
}
self.tabs[i].thumbnail_cache_key = outcome.thumbnail_key;
}
let mut tasks = Vec::new();
match outcome.purpose {
crate::app::SavePurpose::Autosave => {