diff --git a/src/scene/pipeline/circle_gpu.rs b/src/scene/pipeline/circle_gpu.rs index a9cd421e2..8477ea9bf 100644 --- a/src/scene/pipeline/circle_gpu.rs +++ b/src/scene/pipeline/circle_gpu.rs @@ -296,24 +296,74 @@ pub fn extract_circle_instances( wire: &crate::scene::WireModel, draw_depth: f32, ) -> Option> { + let mut instances = Vec::new(); + if extract_circle_instances_into(wire, draw_depth, &mut instances) { + Some(instances) + } else { + None + } +} + +/// Allocation-free check whether this wire consists entirely of analytical circle/arc instances. +pub fn can_extract_circle_instances(wire: &crate::scene::WireModel) -> bool { if wire.tangent_geoms.is_empty() || !wire.fill_tris.is_empty() || wire.fill_is_3d || !wire.text_verts.is_empty() || wire.render_instance.is_some() { - return None; + return false; + } + wire.tangent_geoms.iter().all(|geom| match *geom { + crate::scene::model::wire_model::TangentGeom::Circle { radius, .. } => { + radius > 0.0 && radius.is_finite() && radius <= 1e6 + } + crate::scene::model::wire_model::TangentGeom::PlanarCircle { radius, .. } => { + radius > 0.0 && radius.is_finite() && radius <= 1e6 + } + crate::scene::model::wire_model::TangentGeom::Arc { + radius, + start_angle, + end_angle, + .. + } => { + radius > 0.0 + && radius.is_finite() + && (start_angle as f32).is_finite() + && (end_angle as f32).is_finite() + && radius <= 1e6 + } + _ => false, + }) +} + +/// Appends all analytical circle/arc instances from `wire` directly into `out`. +/// If extraction fails midway, `out` is restored to its original length and `false` is returned. +pub fn extract_circle_instances_into( + wire: &crate::scene::WireModel, + draw_depth: f32, + out: &mut Vec, +) -> bool { + if wire.tangent_geoms.is_empty() + || !wire.fill_tris.is_empty() + || wire.fill_is_3d + || !wire.text_verts.is_empty() + || wire.render_instance.is_some() + { + return false; } - let mut instances = Vec::with_capacity(wire.tangent_geoms.len()); + let initial_len = out.len(); + out.reserve(wire.tangent_geoms.len()); for (i, geom) in wire.tangent_geoms.iter().enumerate() { if let Some(inst) = extract_circle_instance_from_geom_indexed(geom, wire, draw_depth, i) { - instances.push(inst); + out.push(inst); } else { - return None; + out.truncate(initial_len); + return false; } } - Some(instances) + true } /// Helper to extract a single analytical circle or circular arc instance from a `WireModel`. diff --git a/src/scene/pipeline/ellipse_gpu.rs b/src/scene/pipeline/ellipse_gpu.rs index 966859365..c6e20154d 100644 --- a/src/scene/pipeline/ellipse_gpu.rs +++ b/src/scene/pipeline/ellipse_gpu.rs @@ -252,24 +252,71 @@ pub fn extract_ellipse_instances( wire: &crate::scene::WireModel, draw_depth: f32, ) -> Option> { + let mut instances = Vec::new(); + if extract_ellipse_instances_into(wire, draw_depth, &mut instances) { + Some(instances) + } else { + None + } +} + +/// Allocation-free check whether this wire consists entirely of analytical ellipse instances. +pub fn can_extract_ellipse_instances(wire: &crate::scene::WireModel) -> bool { if wire.tangent_geoms.is_empty() || !wire.fill_tris.is_empty() || wire.fill_is_3d || !wire.text_verts.is_empty() || wire.render_instance.is_some() { - return None; + return false; + } + wire.tangent_geoms.iter().all(|geom| match *geom { + crate::scene::model::wire_model::TangentGeom::PlanarEllipse { + major_axis, + normal, + minor_axis_ratio, + start_param, + end_param, + .. + } => { + let major_len_sq = major_axis[0] * major_axis[0] + major_axis[1] * major_axis[1] + major_axis[2] * major_axis[2]; + let norm_len_sq = normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]; + major_len_sq > 1e-12 && major_len_sq.is_finite() && major_len_sq <= 1e12 + && norm_len_sq > 1e-12 && norm_len_sq.is_finite() + && minor_axis_ratio > 1e-6 && minor_axis_ratio.is_finite() + && start_param.is_finite() && end_param.is_finite() + } + _ => false, + }) +} + +/// Appends all analytical ellipse instances from `wire` directly into `out`. +/// If extraction fails midway, `out` is restored to its original length and `false` is returned. +pub fn extract_ellipse_instances_into( + wire: &crate::scene::WireModel, + draw_depth: f32, + out: &mut Vec, +) -> bool { + if wire.tangent_geoms.is_empty() + || !wire.fill_tris.is_empty() + || wire.fill_is_3d + || !wire.text_verts.is_empty() + || wire.render_instance.is_some() + { + return false; } - let mut instances = Vec::with_capacity(wire.tangent_geoms.len()); + let initial_len = out.len(); + out.reserve(wire.tangent_geoms.len()); for geom in &wire.tangent_geoms { if let Some(inst) = extract_ellipse_instance_from_geom(geom, wire, draw_depth) { - instances.push(inst); + out.push(inst); } else { - return None; + out.truncate(initial_len); + return false; } } - Some(instances) + true } /// Helper to extract a single analytical ellipse instance from a `WireModel`. diff --git a/src/scene/pipeline/mod.rs b/src/scene/pipeline/mod.rs index 8989c8538..c6bf571a0 100644 --- a/src/scene/pipeline/mod.rs +++ b/src/scene/pipeline/mod.rs @@ -2675,7 +2675,11 @@ impl Pipeline { } let depth = wire_gpu::wire_draw_depth(wire, depth_map); let mut circles = circle_gpu::extract_circle_instances(wire, depth); - let mut ellipses = ellipse_gpu::extract_ellipse_instances(wire, depth); + let mut ellipses = if circles.is_none() { + ellipse_gpu::extract_ellipse_instances(wire, depth) + } else { + None + }; if let Some(color) = color { if let Some(instances) = circles.as_mut() { for instance in instances { diff --git a/src/scene/pipeline/wire_arena.rs b/src/scene/pipeline/wire_arena.rs index 752072b99..d0f352579 100644 --- a/src/scene/pipeline/wire_arena.rs +++ b/src/scene/pipeline/wire_arena.rs @@ -149,10 +149,10 @@ pub fn classify_wire(wire: &WireModel) -> WireKind { && !wire.fill_is_3d && wire.text_verts.is_empty() { - if super::circle_gpu::extract_circle_instances(wire, 0.0).is_some() { + if super::circle_gpu::can_extract_circle_instances(wire) { return WireKind::Circle; } - if super::ellipse_gpu::extract_ellipse_instances(wire, 0.0).is_some() { + if super::ellipse_gpu::can_extract_ellipse_instances(wire) { return WireKind::Ellipse; } } @@ -195,24 +195,23 @@ pub fn partition_wires<'a>( wires: &'a [WireModel], depth_map: &rustc_hash::FxHashMap, ) -> PartitionedWires<'a> { - let mut regular = Vec::new(); + let cap = wires.len(); + let mut regular = Vec::with_capacity(cap); let mut mesh = Vec::new(); let mut instanced = Vec::new(); - let mut circle_instances = Vec::new(); + let mut circle_instances = Vec::with_capacity(cap); let mut ellipse_instances = Vec::new(); let mut contributors: rustc_hash::FxHashSet = rustc_hash::FxHashSet::default(); - let note = |wire: &WireModel, set: &mut rustc_hash::FxHashSet| { - if let Some(handle) = handle_of(wire) { - set.insert(handle); - } - }; + contributors.reserve(cap.min(1024)); for wire in wires { if !wire.display_visible { continue; } if wire.render_instance.is_some() { - note(wire, &mut contributors); + if let Some(handle) = handle_of(wire) { + contributors.insert(handle); + } instanced.push(wire); continue; } @@ -223,14 +222,16 @@ pub fn partition_wires<'a>( && wire.text_verts.is_empty() { let depth = super::wire_gpu::wire_draw_depth(wire, depth_map); - if let Some(insts) = super::circle_gpu::extract_circle_instances(wire, depth) { - note(wire, &mut contributors); - circle_instances.extend(insts); + if super::circle_gpu::extract_circle_instances_into(wire, depth, &mut circle_instances) { + if let Some(handle) = handle_of(wire) { + contributors.insert(handle); + } continue; } - if let Some(insts) = super::ellipse_gpu::extract_ellipse_instances(wire, depth) { - note(wire, &mut contributors); - ellipse_instances.extend(insts); + if super::ellipse_gpu::extract_ellipse_instances_into(wire, depth, &mut ellipse_instances) { + if let Some(handle) = handle_of(wire) { + contributors.insert(handle); + } continue; } }