From d12caead6cada141e2dbce7e0ff94ce847e7c266 Mon Sep 17 00:00:00 2001 From: Orual Date: Sun, 9 Aug 2026 19:26:38 -0400 Subject: [PATCH] PM-84: fix server test regression and stage compound parse --- .../polymodel-renderer-worker/src/worker.rs | 74 ++++++++++++++----- src/appview/ldraw.rs | 23 +++++- 2 files changed, 76 insertions(+), 21 deletions(-) diff --git a/crates/polymodel-renderer-worker/src/worker.rs b/crates/polymodel-renderer-worker/src/worker.rs index 77a93c2..d3148ca 100644 --- a/crates/polymodel-renderer-worker/src/worker.rs +++ b/crates/polymodel-renderer-worker/src/worker.rs @@ -79,6 +79,26 @@ struct RendererState { struct PreviewAbortSlot { identity: LoadIdentity, controller: AbortController, + #[allow(dead_code)] + listener: Option>, +} + +fn compound_abort_slot( + identity: LoadIdentity, + controller: AbortController, + cancellation: CancellationPolicy, +) -> PreviewAbortSlot { + let callback_cancellation = cancellation.clone(); + let listener = + Closure::wrap(Box::new(move || callback_cancellation.cancel()) as Box); + let _ = controller + .signal() + .add_event_listener_with_callback("abort", listener.as_ref().unchecked_ref()); + PreviewAbortSlot { + identity, + controller, + listener: Some(listener), + } } struct PreviewTarget { @@ -155,6 +175,7 @@ fn compound_materializations( fn parse_compound_source( source: VerifiedCompoundSource, + cancellation: CancellationPolicy, ) -> Result< ( polymodel_renderer_protocol::MeshStats, @@ -188,7 +209,7 @@ fn parse_compound_source( root: primary_root, materializations: &materializations, target_selections: &target_selections, - cancellation: CancellationPolicy::default(), + cancellation, }) .map_err(|error| error.to_string())?; let scene = adapt_ldraw_parse_with_resources(&result, Some(&source.resources)) @@ -691,7 +712,8 @@ mod tests { }], }; let (_stats, scene, _resources) = - parse_compound_source(source).expect("uploaded-root MPD selections should materialize"); + parse_compound_source(source, CancellationPolicy::default()) + .expect("uploaded-root MPD selections should materialize"); assert_eq!(scene.stats.triangles, 1); assert!(scene.bounds.is_some()); } @@ -1293,10 +1315,11 @@ fn handle_load_mesh( state .borrow_mut() .interactive_compound_abort - .replace(PreviewAbortSlot { + .replace(compound_abort_slot( identity, controller, - }) + CancellationPolicy::new(), + )) { previous.controller.abort(); } @@ -1589,16 +1612,18 @@ fn handle_compound_load( bundle: polymodel_renderer_protocol::ModelLoadBundle, ) { let abort = AbortController::new().ok(); + let cancellation = CancellationPolicy::new(); if let Some(abort) = &abort { if let Ok(mut st) = state.try_borrow_mut() { let slot = match namespace { RendererNamespace::Interactive => &mut st.interactive_compound_abort, RendererNamespace::Preview => &mut st.preview_abort, }; - if let Some(previous) = slot.replace(PreviewAbortSlot { + if let Some(previous) = slot.replace(compound_abort_slot( identity, - controller: abort.clone(), - }) { + abort.clone(), + cancellation.clone(), + )) { previous.controller.abort(); } } @@ -1609,7 +1634,7 @@ fn handle_compound_load( if let Err(detail) = &result { tracing::error!(?namespace, ?identity, %detail, "renderer worker: compound bundle load failed"); } - finish_compound_source(&state_clone, namespace, identity, result); + finish_compound_source(&state_clone, namespace, identity, result, cancellation); }); } @@ -1632,6 +1657,7 @@ fn handle_compound_preview( return; } let abort = AbortController::new().ok(); + let cancellation = CancellationPolicy::new(); { let mut st = state.borrow_mut(); let previous = st.preview_reducer.current_identity(); @@ -1659,10 +1685,11 @@ fn handle_compound_preview( }); } if let Some(controller) = abort.as_ref() - && let Some(previous) = st.preview_abort.replace(PreviewAbortSlot { + && let Some(previous) = st.preview_abort.replace(compound_abort_slot( identity, - controller: controller.clone(), - }) + controller.clone(), + cancellation.clone(), + )) { previous.controller.abort(); } @@ -1676,6 +1703,7 @@ fn handle_compound_preview( width.max(1), height.max(1), abort.clone(), + cancellation, ) .await; finish_compound_preview(&state_clone, request_id, identity, result); @@ -1781,6 +1809,7 @@ async fn render_compound_preview_request( width: u32, height: u32, abort: Option, + cancellation: CancellationPolicy, ) -> Result, CompoundLoadError> { let source = load_compound_source( state, @@ -1791,7 +1820,7 @@ async fn render_compound_preview_request( ) .await?; compound_is_current(state, RendererNamespace::Preview, identity, abort.as_ref())?; - let (_stats, scene, source_resources) = parse_compound_source(source)?; + let (_stats, scene, source_resources) = parse_compound_source(source, cancellation)?; compound_is_current(state, RendererNamespace::Preview, identity, abort.as_ref())?; let bounds = scene .bounds @@ -1969,6 +1998,7 @@ fn finish_compound_source( namespace: RendererNamespace, identity: LoadIdentity, result: Result, + cancellation: CancellationPolicy, ) { { let mut st = state.borrow_mut(); @@ -1976,9 +2006,6 @@ fn finish_compound_source( RendererNamespace::Interactive => &mut st.interactive_compound_abort, RendererNamespace::Preview => &mut st.preview_abort, }; - if slot.as_ref().is_some_and(|slot| slot.identity == identity) { - slot.take(); - } match result { Ok(source) => { let reducer = match namespace { @@ -1993,7 +2020,17 @@ fn finish_compound_source( ?identity, "renderer worker: parsing compound bundle" ); - let (stats, scene, source_resources) = match parse_compound_source(source) { + drop(st); + let parsed = parse_compound_source(source, cancellation); + let mut st = state.borrow_mut(); + let slot = match namespace { + RendererNamespace::Interactive => &mut st.interactive_compound_abort, + RendererNamespace::Preview => &mut st.preview_abort, + }; + if slot.as_ref().is_some_and(|slot| slot.identity == identity) { + slot.take(); + } + let (stats, scene, source_resources) = match parsed { Ok((stats, scene, source_resources)) => { tracing::info!( ?namespace, @@ -2240,10 +2277,11 @@ fn handle_render_preview_image( } let abort = AbortController::new().ok(); if let Some(controller) = abort.clone() { - if let Some(previous_slot) = st.preview_abort.replace(PreviewAbortSlot { + if let Some(previous_slot) = st.preview_abort.replace(compound_abort_slot( identity, controller, - }) { + CancellationPolicy::new(), + )) { previous_slot.controller.abort(); } } else if let Some(previous_slot) = st.preview_abort.take() { diff --git a/src/appview/ldraw.rs b/src/appview/ldraw.rs index 422513c..fdda9e9 100644 --- a/src/appview/ldraw.rs +++ b/src/appview/ldraw.rs @@ -109,6 +109,23 @@ fn child_root_local(node: &BundleNode) -> RootId { } } +fn cached_target_key( + cache: &HashMap<(RootId, CanonicalPath), ProtocolCacheKey>, + preferred_root: RootId, + path: &CanonicalPath, +) -> Option { + cache + .get(&(preferred_root, path.clone())) + .or_else(|| { + path.as_str() + .split('/') + .next() + .and_then(parse_root) + .and_then(|root| cache.get(&(root, path.clone()))) + }) + .cloned() +} + async fn load_bundle_root(state: &AppState, uri: &AtUri) -> AppResult<(AtUri, CanonicalPath)> { let Some(row) = db(sqlx::query!( "SELECT project_uri, canonical_path FROM ldraw_resources WHERE resource_uri = ?", @@ -515,9 +532,9 @@ async fn build_model_load_bundle(state: AppState, uri: AtUri) -> AppResult AppResult