fix: stabilize web loading and paper rendering
This commit is contained in:
parent
fe49cc8eb5
commit
bca65600cd
25 changed files with 109 additions and 62 deletions
|
|
@ -39,6 +39,7 @@ use crate::scene::model::mesh_model::{MeshLodSet, MeshModel};
|
|||
pub(crate) const EDGE_CHORD_FRAC: f64 = 0.002;
|
||||
/// Truck's own triangulation chord tolerance for the cone faces still routed
|
||||
/// through its kernel, as a fraction of the surface radius.
|
||||
#[cfg(feature = "solid3d")]
|
||||
pub(crate) const TRUCK_CHORD_FRAC: f64 = 0.1;
|
||||
/// Boundary-loop sampling for parameter-range classification (which arc of a
|
||||
/// sphere/torus a face covers): a fine fraction so the classification is
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ struct SceneDependencyIndex {
|
|||
text_styles: HashMap<String, DependencyTargets>,
|
||||
dim_styles: HashMap<String, DependencyTargets>,
|
||||
object_styles: HashMap<Handle, DependencyTargets>,
|
||||
blocks: HashMap<String, HashSet<Handle>>,
|
||||
}
|
||||
|
||||
fn hatch_interaction_aabb(hatch: &model::hatch_model::HatchModel) -> Option<[f64; 4]> {
|
||||
|
|
@ -270,6 +269,7 @@ impl std::fmt::Debug for PreparedOpenGeometry {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
|
||||
pub(crate) struct WireGpuPatch {
|
||||
pub(crate) changes: Arc<Vec<(Handle, ChangeKind)>>,
|
||||
pub(crate) runs: Arc<HashMap<Handle, Arc<Vec<WireModel>>>>,
|
||||
|
|
@ -280,6 +280,7 @@ pub(crate) struct WireGpuPatch {
|
|||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
|
||||
pub(crate) struct WireIndexEdit {
|
||||
pub(crate) handle: Handle,
|
||||
pub(crate) start: usize,
|
||||
|
|
@ -414,6 +415,7 @@ pub struct OpenTimings {
|
|||
|
||||
/// Build hatch / image / mesh caches from a document without needing `&mut Scene`.
|
||||
/// Intended to run on a background thread during file load.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
|
||||
build_derived_caches_impl(doc, None)
|
||||
}
|
||||
|
|
@ -6631,10 +6633,7 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
|
||||
let mut index = SceneDependencyIndex {
|
||||
blocks: roots.clone(),
|
||||
..SceneDependencyIndex::default()
|
||||
};
|
||||
let mut index = SceneDependencyIndex::default();
|
||||
for entity in self.document.entities() {
|
||||
let common = entity.common();
|
||||
let owner = if common.owner_handle.is_null() {
|
||||
|
|
@ -6792,18 +6791,6 @@ impl Scene {
|
|||
self.invalidate_dependency_targets(combined);
|
||||
}
|
||||
|
||||
pub fn block_dependency_handles(&self, name: &str) -> Vec<Handle> {
|
||||
if self.dependency_index_cache.borrow().is_none() {
|
||||
*self.dependency_index_cache.borrow_mut() = Some(self.rebuild_dependency_index());
|
||||
}
|
||||
self.dependency_index_cache
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.and_then(|index| index.blocks.get(&name.to_ascii_uppercase()))
|
||||
.map(|handles| handles.iter().copied().collect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_dependency_index(&self) {
|
||||
self.dependency_index_cache.borrow_mut().take();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -750,7 +750,7 @@ impl InteractionIndex {
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let perf = crate::perf::enabled();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let build_started = std::time::Instant::now();
|
||||
let build_started = iced::time::Instant::now();
|
||||
let wire_handles: Vec<Option<u64>> = wires
|
||||
.iter()
|
||||
.map(|wire| wire.name.parse::<u64>().ok())
|
||||
|
|
@ -770,7 +770,7 @@ impl InteractionIndex {
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let handles_elapsed = build_started.elapsed();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let collect_started = std::time::Instant::now();
|
||||
let collect_started = iced::time::Instant::now();
|
||||
let mut wire_entries = Vec::with_capacity(wires.len());
|
||||
let mut unbounded_wires = Vec::new();
|
||||
let mut max_line_half_width_px = 0.0f32;
|
||||
|
|
@ -793,7 +793,7 @@ impl InteractionIndex {
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let collect_elapsed = collect_started.elapsed();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let flatten_started = std::time::Instant::now();
|
||||
let flatten_started = iced::time::Instant::now();
|
||||
let mut segment_parts = Vec::with_capacity(per_wire.len());
|
||||
let mut snap_point_parts = Vec::with_capacity(per_wire.len());
|
||||
let mut key_vertex_parts = Vec::with_capacity(per_wire.len());
|
||||
|
|
@ -868,7 +868,7 @@ impl InteractionIndex {
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let flatten_elapsed = flatten_started.elapsed();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let spatial_started = std::time::Instant::now();
|
||||
let spatial_started = iced::time::Instant::now();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let (
|
||||
|
|
|
|||
|
|
@ -3219,22 +3219,40 @@ impl MultiPipeline {
|
|||
self.slot_clock = self.slot_clock.wrapping_add(1).max(1);
|
||||
let now = self.slot_clock;
|
||||
let reserved: rustc_hash::FxHashSet<u64> = instance_ids.iter().copied().collect();
|
||||
// `inner.slot_id` is updated later in `prepare`, after this whole
|
||||
// method returns. Without a per-call claim set, two new viewports in
|
||||
// the same primitive both see the freshly-grown slot as `MAX` and get
|
||||
// assigned to it. Paper then blits the final occupant's resolve texture
|
||||
// once as the full sheet and again as the floating viewport.
|
||||
let mut claimed: rustc_hash::FxHashSet<usize> =
|
||||
rustc_hash::FxHashSet::default();
|
||||
let mut slots = Vec::with_capacity(instance_ids.len());
|
||||
|
||||
for &instance_id in instance_ids {
|
||||
let slot = if let Some(&slot) = self.slot_by_instance.get(&instance_id) {
|
||||
let existing = self
|
||||
.slot_by_instance
|
||||
.get(&instance_id)
|
||||
.copied()
|
||||
.filter(|slot| !claimed.contains(slot));
|
||||
let slot = if let Some(slot) = existing {
|
||||
slot
|
||||
} else {
|
||||
self.slot_by_instance.remove(&instance_id);
|
||||
let vacant = self
|
||||
.inners
|
||||
.iter()
|
||||
.position(|inner| inner.slot_id == u64::MAX);
|
||||
.enumerate()
|
||||
.find(|(slot, inner)| {
|
||||
!claimed.contains(slot) && inner.slot_id == u64::MAX
|
||||
})
|
||||
.map(|(slot, _)| slot);
|
||||
let recyclable = vacant.or_else(|| {
|
||||
(self.inners.len() >= SOFT_LIMIT)
|
||||
.then(|| {
|
||||
self.inners
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(slot, _)| !claimed.contains(slot))
|
||||
.filter(|(_, inner)| !reserved.contains(&inner.slot_id))
|
||||
.filter(|(slot, _)| {
|
||||
now.saturating_sub(self.slot_last_used[*slot]) > HOT_WINDOW
|
||||
|
|
@ -3256,6 +3274,7 @@ impl MultiPipeline {
|
|||
self.slot_by_instance.insert(instance_id, slot);
|
||||
slot
|
||||
};
|
||||
claimed.insert(slot);
|
||||
self.slot_last_used[slot] = now;
|
||||
slots.push(slot);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ impl WireArena {
|
|||
) -> Option<Self> {
|
||||
let ranges = handle_ranges(wires)?;
|
||||
let perf = crate::perf::enabled();
|
||||
let total_started = std::time::Instant::now();
|
||||
let total_started = iced::time::Instant::now();
|
||||
|
||||
// Reject an oversized batch before parallel emission allocates hundreds
|
||||
// of megabytes. `points.len() - 1` is an upper bound because NaN-break
|
||||
|
|
@ -363,7 +363,7 @@ impl WireArena {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let pack_started = std::time::Instant::now();
|
||||
let pack_started = iced::time::Instant::now();
|
||||
use crate::par::prelude::*;
|
||||
let packed: Vec<PackedSlab> = plans
|
||||
.par_iter()
|
||||
|
|
@ -441,7 +441,7 @@ impl WireArena {
|
|||
let const_cap = ((const_tail as u64 * HEADROOM_NUM / HEADROOM_DEN)
|
||||
.max(MIN_CONST_CAP)
|
||||
.min(MAX_CONSTS)) as u32;
|
||||
let upload_started = std::time::Instant::now();
|
||||
let upload_started = iced::time::Instant::now();
|
||||
let inst_buf = alloc_inst_initialized(device, inst_cap as u64, &instances);
|
||||
let const_buf = alloc_const_initialized(device, const_cap as u64, &consts_cpu);
|
||||
let upload_ms = upload_started.elapsed().as_secs_f64() * 1000.0;
|
||||
|
|
|
|||
|
|
@ -224,9 +224,6 @@ impl PackedWireInstance {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub type WireInstance = PackedWireInstance;
|
||||
|
||||
/// Wire and hatch pipelines switch together: the fast path uses storage
|
||||
/// buffers; the compatibility path carries wire constants in packed vertex
|
||||
/// attributes and hatch data in a texture.
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ impl shader::Primitive for Primitive {
|
|||
bounds: &Rectangle,
|
||||
viewport: &Viewport,
|
||||
) {
|
||||
let nav_prepare_started = std::time::Instant::now();
|
||||
let nav_prepare_started = iced::time::Instant::now();
|
||||
let phys = viewport.physical_size();
|
||||
let full_size = Size::new(phys.width, phys.height);
|
||||
let scale = viewport.scale_factor() as f32;
|
||||
|
|
@ -436,11 +436,14 @@ impl shader::Primitive for Primitive {
|
|||
// the whole wire buffer. Only for the scissor-free, mesh-free
|
||||
// (single-batch) Model set; scissored paper viewports and mixed
|
||||
// 2D/3D sets fall through to the shared batched path below.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let mut arena_served = false;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let arena_served = false;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let _perf = crate::perf::enabled();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let _t0 = std::time::Instant::now();
|
||||
let _t0 = iced::time::Instant::now();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let mut _patched = false;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
|
@ -884,7 +887,7 @@ impl shader::Primitive for Primitive {
|
|||
target: &iced::wgpu::TextureView,
|
||||
clip: &Rectangle<u32>,
|
||||
) {
|
||||
let nav_render_started = std::time::Instant::now();
|
||||
let nav_render_started = iced::time::Instant::now();
|
||||
let cw = clip.width as f32;
|
||||
let ch = clip.height as f32;
|
||||
let clip_right = clip.x + clip.width;
|
||||
|
|
@ -1403,7 +1406,7 @@ impl Scene {
|
|||
_hover_region: Option<usize>,
|
||||
show_viewcube: bool,
|
||||
) -> Primitive {
|
||||
let nav_build_started = std::time::Instant::now();
|
||||
let nav_build_started = iced::time::Instant::now();
|
||||
let perf_nav = self.take_nav_perf();
|
||||
// Hover comes from the scene cell driven by the app-level
|
||||
// `CursorMoved` handler — the cube overlay sits above the shader
|
||||
|
|
@ -1463,7 +1466,7 @@ impl Scene {
|
|||
};
|
||||
let active = self.active_model_tile.get();
|
||||
let is_active = tile_idx == active;
|
||||
let nav_build_started = std::time::Instant::now();
|
||||
let nav_build_started = iced::time::Instant::now();
|
||||
let perf_nav = if is_active {
|
||||
self.take_nav_perf()
|
||||
} else {
|
||||
|
|
@ -1749,14 +1752,17 @@ impl Scene {
|
|||
// id so an unchanged wire set is not re-walked every frame.
|
||||
// The reuse fallback inside the gather is keyed per wire SOURCE — the
|
||||
// sheet, a Model tile, a content viewport and the implicit view carry
|
||||
// different glyph sets even at the same geometry epoch (#403). Tiles
|
||||
// different glyph sets even at the same geometry epoch (#403). Paper
|
||||
// sheets must also include their layout block: switching layouts does
|
||||
// not change the geometry epoch, and a role-only key reused the prior
|
||||
// sheet's glyph coordinates while its wires moved correctly. Tiles
|
||||
// share the resident Model set, so they share one key; the implicit
|
||||
// view mixes in the current layout block (BEDIT swaps sets without a
|
||||
// geometry delta).
|
||||
let text_source_key: u64 = if inst.tile_idx.is_some() {
|
||||
0x1000_0000_0000_0000
|
||||
} else if inst.paper_sheet {
|
||||
0x2000_0000_0000_0000
|
||||
0x2000_0000_0000_0000 | self.current_layout_block_handle().value()
|
||||
} else if inst.handle == acadrust::Handle::NULL {
|
||||
0x4000_0000_0000_0000 | self.current_layout_block_handle().value()
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue