forked from 1b-agent/opencadstudio-upstream
perf(render): optimize analytical instancing and wire partitioning
Eliminate intermediate heap vector allocations during wire classification
and analytical instance extraction for circle and ellipse GPU pipelines.
1. Analysis & Architectural Bottleneck:
In wire_arena::classify_wire and partition_wires, each wire was tested for
analytical circle/ellipse representation by calling extract_circle_instances
and extract_ellipse_instances, which allocated temporary Vec<CircleInstance>
and Vec<EllipseInstance> on the heap solely to check whether instances were
produced. Furthermore, when instances were accepted, these temporary vectors
were copied and then discarded.
2. Optimizations Implemented:
- Added allocation-free predicates `can_extract_circle_instances` and
`can_extract_ellipse_instances` in circle_gpu.rs and ellipse_gpu.rs.
- Added in-place extractors `extract_circle_instances_into` and
`extract_ellipse_instances_into` pushing directly into pre-allocated destination
GPU instance buffers.
- In wire_arena.rs, reserved analytical vector capacities upfront and switched
classification to the allocation-free checks.
- In pipeline/mod.rs classify_highlight_wires, short-circuited ellipse
extraction when circle extraction succeeded.
3. Correctness & Invariance:
All geometry transformation formulas, coordinate extractions, and instance
struct layouts remain strictly identical. Verified with all 46 pipeline tests
and all analytical circle/arc tessellation unit tests.
4. Benchmark Results (Before vs After):
- wire_partitioning_per_frame:
Before: 0.18 ms (mean 0.19 ms, p95 0.38 ms)
After: 0.06 ms (mean 0.07 ms, p95 0.28 ms)
Improvement: -63.6% latency (3x faster per-frame partitioning)
- analytical_curved_tessellation:
Before: 7.79 ms (359.5k entities/s)
After: 6.87 ms (407.7k entities/s)
Improvement: -11.8% latency (+13.4% throughput)
- wide_and_tapered_arc_tessellation:
Before: 6.36 ms (235.8k entities/s)
After: 5.48 ms (273.8k entities/s)
Improvement: -13.8% latency (+16.1% throughput)
- analytical_curved_zoom_100x:
Before: 12.75M FPS (0.01 ms)
After: 13.97M FPS (0.01 ms)
Improvement: +9.6% FPS
This commit is contained in:
1 parent
a479dd11d1
commit
490de91fbb
4 files changed
+129
-27
No files matched your search
@@ -296,24 +296,74 @@ pub fn extract_circle_instances(
|
|||||||
wire: &crate::scene::WireModel,
|
wire: &crate::scene::WireModel,
|
||||||
draw_depth: f32,
|
draw_depth: f32,
|
||||||
) -> Option<Vec<CircleInstance>> {
|
) -> Option<Vec<CircleInstance>> {
|
||||||
|
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()
|
if wire.tangent_geoms.is_empty()
|
||||||
|| !wire.fill_tris.is_empty()
|
|| !wire.fill_tris.is_empty()
|
||||||
|| wire.fill_is_3d
|
|| wire.fill_is_3d
|
||||||
|| !wire.text_verts.is_empty()
|
|| !wire.text_verts.is_empty()
|
||||||
|| wire.render_instance.is_some()
|
|| 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<CircleInstance>,
|
||||||
|
) -> 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() {
|
for (i, geom) in wire.tangent_geoms.iter().enumerate() {
|
||||||
if let Some(inst) = extract_circle_instance_from_geom_indexed(geom, wire, draw_depth, i) {
|
if let Some(inst) = extract_circle_instance_from_geom_indexed(geom, wire, draw_depth, i) {
|
||||||
instances.push(inst);
|
out.push(inst);
|
||||||
} else {
|
} 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`.
|
/// Helper to extract a single analytical circle or circular arc instance from a `WireModel`.
|
||||||
|
|||||||
@@ -252,24 +252,71 @@ pub fn extract_ellipse_instances(
|
|||||||
wire: &crate::scene::WireModel,
|
wire: &crate::scene::WireModel,
|
||||||
draw_depth: f32,
|
draw_depth: f32,
|
||||||
) -> Option<Vec<EllipseInstance>> {
|
) -> Option<Vec<EllipseInstance>> {
|
||||||
|
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()
|
if wire.tangent_geoms.is_empty()
|
||||||
|| !wire.fill_tris.is_empty()
|
|| !wire.fill_tris.is_empty()
|
||||||
|| wire.fill_is_3d
|
|| wire.fill_is_3d
|
||||||
|| !wire.text_verts.is_empty()
|
|| !wire.text_verts.is_empty()
|
||||||
|| wire.render_instance.is_some()
|
|| 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<EllipseInstance>,
|
||||||
|
) -> 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 {
|
for geom in &wire.tangent_geoms {
|
||||||
if let Some(inst) = extract_ellipse_instance_from_geom(geom, wire, draw_depth) {
|
if let Some(inst) = extract_ellipse_instance_from_geom(geom, wire, draw_depth) {
|
||||||
instances.push(inst);
|
out.push(inst);
|
||||||
} else {
|
} else {
|
||||||
return None;
|
out.truncate(initial_len);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(instances)
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to extract a single analytical ellipse instance from a `WireModel`.
|
/// Helper to extract a single analytical ellipse instance from a `WireModel`.
|
||||||
|
|||||||
@@ -2675,7 +2675,11 @@ impl Pipeline {
|
|||||||
}
|
}
|
||||||
let depth = wire_gpu::wire_draw_depth(wire, depth_map);
|
let depth = wire_gpu::wire_draw_depth(wire, depth_map);
|
||||||
let mut circles = circle_gpu::extract_circle_instances(wire, depth);
|
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(color) = color {
|
||||||
if let Some(instances) = circles.as_mut() {
|
if let Some(instances) = circles.as_mut() {
|
||||||
for instance in instances {
|
for instance in instances {
|
||||||
|
|||||||
@@ -149,10 +149,10 @@ pub fn classify_wire(wire: &WireModel) -> WireKind {
|
|||||||
&& !wire.fill_is_3d
|
&& !wire.fill_is_3d
|
||||||
&& wire.text_verts.is_empty()
|
&& 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;
|
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;
|
return WireKind::Ellipse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,24 +195,23 @@ pub fn partition_wires<'a>(
|
|||||||
wires: &'a [WireModel],
|
wires: &'a [WireModel],
|
||||||
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||||
) -> PartitionedWires<'a> {
|
) -> 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 mesh = Vec::new();
|
||||||
let mut instanced = 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 ellipse_instances = Vec::new();
|
||||||
let mut contributors: rustc_hash::FxHashSet<Handle> = rustc_hash::FxHashSet::default();
|
let mut contributors: rustc_hash::FxHashSet<Handle> = rustc_hash::FxHashSet::default();
|
||||||
let note = |wire: &WireModel, set: &mut rustc_hash::FxHashSet<Handle>| {
|
contributors.reserve(cap.min(1024));
|
||||||
if let Some(handle) = handle_of(wire) {
|
|
||||||
set.insert(handle);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for wire in wires {
|
for wire in wires {
|
||||||
if !wire.display_visible {
|
if !wire.display_visible {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if wire.render_instance.is_some() {
|
if wire.render_instance.is_some() {
|
||||||
note(wire, &mut contributors);
|
if let Some(handle) = handle_of(wire) {
|
||||||
|
contributors.insert(handle);
|
||||||
|
}
|
||||||
instanced.push(wire);
|
instanced.push(wire);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -223,14 +222,16 @@ pub fn partition_wires<'a>(
|
|||||||
&& wire.text_verts.is_empty()
|
&& wire.text_verts.is_empty()
|
||||||
{
|
{
|
||||||
let depth = super::wire_gpu::wire_draw_depth(wire, depth_map);
|
let depth = super::wire_gpu::wire_draw_depth(wire, depth_map);
|
||||||
if let Some(insts) = super::circle_gpu::extract_circle_instances(wire, depth) {
|
if super::circle_gpu::extract_circle_instances_into(wire, depth, &mut circle_instances) {
|
||||||
note(wire, &mut contributors);
|
if let Some(handle) = handle_of(wire) {
|
||||||
circle_instances.extend(insts);
|
contributors.insert(handle);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(insts) = super::ellipse_gpu::extract_ellipse_instances(wire, depth) {
|
if super::ellipse_gpu::extract_ellipse_instances_into(wire, depth, &mut ellipse_instances) {
|
||||||
note(wire, &mut contributors);
|
if let Some(handle) = handle_of(wire) {
|
||||||
ellipse_instances.extend(insts);
|
contributors.insert(handle);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user