forked from 1b-agent/opencadstudio-upstream
Make tessellation memo and resident wire set zoom-independent
With GPU analytical rendering handling circles, arcs, and ellipses, tessellation geometry is now camera- and zoom-independent. - Remove wpp from the tessellation memo guard hash to prevent clearing all cached entities when zooming across quantization bands. - Remove quantized_wpp from resident_wire_key and resident_wires_for so all zoom levels reuse the cached resident set directly with zero CPU re-tessellation. - Fix clippy slice_from_raw_parts lint in dwg-thumbnailer without suppression. - Update zoom test to assert Arc pointer equality across camera zooms.
This commit is contained in:
1 parent
dd061a55ed
commit
690971c44e
3 files changed
+33
-49
No files matched your search
@@ -259,7 +259,7 @@ pub unsafe extern "C" fn dwg_thumbnail_png(
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn dwg_thumbnail_free(ptr: *mut u8, len: usize) {
|
pub unsafe extern "C" fn dwg_thumbnail_free(ptr: *mut u8, len: usize) {
|
||||||
if !ptr.is_null() && len != 0 {
|
if !ptr.is_null() && len != 0 {
|
||||||
drop(Box::from_raw(std::slice::from_raw_parts_mut(ptr, len)));
|
drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(ptr, len)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-38
@@ -2232,7 +2232,6 @@ impl Scene {
|
|||||||
self.annotation_all_visible(),
|
self.annotation_all_visible(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
let gen = WIRE_CONTENT_GEN.fetch_add(1, Ordering::Relaxed);
|
let gen = WIRE_CONTENT_GEN.fetch_add(1, Ordering::Relaxed);
|
||||||
self.last_model_wire_gen.set(gen);
|
self.last_model_wire_gen.set(gen);
|
||||||
@@ -5162,9 +5161,9 @@ impl Scene {
|
|||||||
pub(super) fn model_tile_wires_arc(
|
pub(super) fn model_tile_wires_arc(
|
||||||
&self,
|
&self,
|
||||||
_tile_idx: usize,
|
_tile_idx: usize,
|
||||||
cam: &Camera,
|
_cam: &Camera,
|
||||||
_cam_aspect: f32,
|
_cam_aspect: f32,
|
||||||
tile_pixel_height: f32,
|
_tile_pixel_height: f32,
|
||||||
) -> Arc<Vec<WireModel>> {
|
) -> Arc<Vec<WireModel>> {
|
||||||
// In a BEDIT block editor the resident set is the edited block's own
|
// In a BEDIT block editor the resident set is the edited block's own
|
||||||
// (block-local) entities; otherwise model space. (#261)
|
// (block-local) entities; otherwise model space. (#261)
|
||||||
@@ -5175,12 +5174,7 @@ impl Scene {
|
|||||||
&self.document,
|
&self.document,
|
||||||
&self.document.header.current_annotation_scale,
|
&self.document.header.current_annotation_scale,
|
||||||
);
|
);
|
||||||
let wpp = if tile_pixel_height > 0.0 {
|
self.resident_wires_for(block, None, scale, None, None)
|
||||||
Some((2.0 * cam.ortho_size()) / tile_pixel_height)
|
|
||||||
} else {
|
|
||||||
self.world_per_pixel()
|
|
||||||
};
|
|
||||||
self.resident_wires_for(block, None, scale, None, None, wpp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unified static-hold wire builder — the ONE tessellation path every
|
/// Unified static-hold wire builder — the ONE tessellation path every
|
||||||
@@ -5229,7 +5223,6 @@ impl Scene {
|
|||||||
annotation_scale_handle: Option<Handle>,
|
annotation_scale_handle: Option<Handle>,
|
||||||
frozen_layers: Option<&HashSet<Handle>>,
|
frozen_layers: Option<&HashSet<Handle>>,
|
||||||
style_viewport: Option<Handle>,
|
style_viewport: Option<Handle>,
|
||||||
wpp: Option<f32>,
|
|
||||||
) -> Arc<Vec<WireModel>> {
|
) -> Arc<Vec<WireModel>> {
|
||||||
// Normalize an inert anno override away so distinct viewport scales
|
// Normalize an inert anno override away so distinct viewport scales
|
||||||
// share one resident set when annotation can't change the wires.
|
// share one resident set when annotation can't change the wires.
|
||||||
@@ -5246,7 +5239,6 @@ impl Scene {
|
|||||||
self.paper_bg_color
|
self.paper_bg_color
|
||||||
};
|
};
|
||||||
let all_visible = self.annotation_all_visible();
|
let all_visible = self.annotation_all_visible();
|
||||||
let quantized_wpp = Self::quantize_wpp(wpp);
|
|
||||||
let key = self.resident_wire_key(
|
let key = self.resident_wire_key(
|
||||||
block,
|
block,
|
||||||
bg,
|
bg,
|
||||||
@@ -5255,7 +5247,6 @@ impl Scene {
|
|||||||
all_visible,
|
all_visible,
|
||||||
frozen_layers,
|
frozen_layers,
|
||||||
style_viewport,
|
style_viewport,
|
||||||
quantized_wpp,
|
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
let sets = self.resident_wire_sets.borrow();
|
let sets = self.resident_wire_sets.borrow();
|
||||||
@@ -5280,18 +5271,17 @@ impl Scene {
|
|||||||
all_visible,
|
all_visible,
|
||||||
frozen_layers,
|
frozen_layers,
|
||||||
style_viewport,
|
style_viewport,
|
||||||
quantized_wpp,
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return arc;
|
return arc;
|
||||||
}
|
}
|
||||||
// Build once: full tessellation, no cull (region = None), zoom LOD
|
// Build once: full tessellation, no cull, no zoom LOD — the resident
|
||||||
// bounded by quantized_wpp for every space.
|
// set is zoom-independent (GPU analytical circles/arcs/ellipses).
|
||||||
let t_tess = iced::time::Instant::now();
|
let t_tess = iced::time::Instant::now();
|
||||||
let mut wires = self.wires_for_block_culled(
|
let mut wires = self.wires_for_block_culled(
|
||||||
block,
|
block,
|
||||||
None,
|
None,
|
||||||
quantized_wpp,
|
None,
|
||||||
frozen_layers,
|
frozen_layers,
|
||||||
anno_scale_override,
|
anno_scale_override,
|
||||||
annotation_scale_handle,
|
annotation_scale_handle,
|
||||||
@@ -5361,7 +5351,6 @@ impl Scene {
|
|||||||
all_visible: bool,
|
all_visible: bool,
|
||||||
frozen_layers: Option<&HashSet<Handle>>,
|
frozen_layers: Option<&HashSet<Handle>>,
|
||||||
style_viewport: Option<Handle>,
|
style_viewport: Option<Handle>,
|
||||||
quantized_wpp: Option<f32>,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let mut key: u64 = 0xcbf2_9ce4_8422_2325;
|
let mut key: u64 = 0xcbf2_9ce4_8422_2325;
|
||||||
let mut mix =
|
let mut mix =
|
||||||
@@ -5376,9 +5365,9 @@ impl Scene {
|
|||||||
mix(annotation_scale_handle.map(|handle| handle.value()).unwrap_or(0));
|
mix(annotation_scale_handle.map(|handle| handle.value()).unwrap_or(0));
|
||||||
mix(all_visible as u64);
|
mix(all_visible as u64);
|
||||||
mix(self.viewport_style_key(style_viewport));
|
mix(self.viewport_style_key(style_viewport));
|
||||||
mix(quantized_wpp
|
// quantized_wpp removed: GPU analytical rendering for circles/arcs/
|
||||||
.map(|w| w.to_bits() as u64)
|
// ellipses makes tessellation zoom-independent, so the resident set is
|
||||||
.unwrap_or(u64::MAX));
|
// shared across all zoom levels.
|
||||||
match frozen_layers {
|
match frozen_layers {
|
||||||
Some(frozen) => {
|
Some(frozen) => {
|
||||||
let mut signature = 0u64;
|
let mut signature = 0u64;
|
||||||
@@ -5491,7 +5480,6 @@ impl Scene {
|
|||||||
all_visible: bool,
|
all_visible: bool,
|
||||||
frozen_layers: Option<&HashSet<Handle>>,
|
frozen_layers: Option<&HashSet<Handle>>,
|
||||||
style_viewport: Option<Handle>,
|
style_viewport: Option<Handle>,
|
||||||
quantized_wpp: Option<f32>,
|
|
||||||
) -> Option<Arc<Vec<WireModel>>> {
|
) -> Option<Arc<Vec<WireModel>>> {
|
||||||
if self.viewport_style_key(style_viewport) != 0 {
|
if self.viewport_style_key(style_viewport) != 0 {
|
||||||
return None;
|
return None;
|
||||||
@@ -5575,7 +5563,7 @@ impl Scene {
|
|||||||
e,
|
e,
|
||||||
Some(&blk),
|
Some(&blk),
|
||||||
None,
|
None,
|
||||||
quantized_wpp,
|
None,
|
||||||
anno_scale_override.is_some(),
|
anno_scale_override.is_some(),
|
||||||
);
|
);
|
||||||
memo_updates.push((*h, Arc::new(raw.clone())));
|
memo_updates.push((*h, Arc::new(raw.clone())));
|
||||||
@@ -5857,7 +5845,7 @@ impl Scene {
|
|||||||
}
|
}
|
||||||
let layout_block = self.current_layout_block_handle();
|
let layout_block = self.current_layout_block_handle();
|
||||||
let scale = self.paper_annotation_scale_handle();
|
let scale = self.paper_annotation_scale_handle();
|
||||||
let base = self.resident_wires_for(layout_block, None, scale, None, None, None);
|
let base = self.resident_wires_for(layout_block, None, scale, None, None);
|
||||||
let mut wires = (*base).clone();
|
let mut wires = (*base).clone();
|
||||||
// The overall "sheet" viewport now IS the paper view itself, so its own
|
// The overall "sheet" viewport now IS the paper view itself, so its own
|
||||||
// border rectangle must not be drawn as an entity on the sheet.
|
// border rectangle must not be drawn as an entity on the sheet.
|
||||||
@@ -9112,7 +9100,10 @@ impl Scene {
|
|||||||
let guard = {
|
let guard = {
|
||||||
let mut g: u64 = 0xcbf2_9ce4_8422_2325;
|
let mut g: u64 = 0xcbf2_9ce4_8422_2325;
|
||||||
let mut mix = |x: u64| g = g.rotate_left(13) ^ x;
|
let mut mix = |x: u64| g = g.rotate_left(13) ^ x;
|
||||||
mix(wpp.map(|w| w.to_bits() as u64).unwrap_or(u64::MAX));
|
// wpp removed from guard: GPU analytical rendering handles
|
||||||
|
// circles/arcs/ellipses, Point ignores wpp, and Light (the
|
||||||
|
// only remaining wpp consumer) is rare enough that its stale
|
||||||
|
// glyphs don't justify clearing every memoized entity on zoom.
|
||||||
if let Some(v) = view_aabb {
|
if let Some(v) = view_aabb {
|
||||||
for c in v {
|
for c in v {
|
||||||
mix(c.to_bits() as u64);
|
mix(c.to_bits() as u64);
|
||||||
@@ -10230,7 +10221,6 @@ guard={:016x} guard_stale={} avp={} anno={:.4} anno_h={} all_vis={} sdf_gen={}",
|
|||||||
scale,
|
scale,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
let wire_points = wires.iter().flat_map(|wire| wire.key_vertices.iter().copied());
|
let wire_points = wires.iter().flat_map(|wire| wire.key_vertices.iter().copied());
|
||||||
let mesh_points = self.meshes.iter().filter_map(|(&handle, set)| {
|
let mesh_points = self.meshes.iter().filter_map(|(&handle, set)| {
|
||||||
@@ -11244,7 +11234,6 @@ mod layout_cache_tests {
|
|||||||
scale,
|
scale,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
let generation = s.last_model_wire_gen.get();
|
let generation = s.last_model_wire_gen.get();
|
||||||
let bounds = s.model_space_extents().unwrap();
|
let bounds = s.model_space_extents().unwrap();
|
||||||
@@ -11260,17 +11249,17 @@ mod layout_cache_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn circle_wires_scale_with_zoom_level() {
|
fn resident_wires_are_zoom_independent_and_gpu_analytical() {
|
||||||
let mut s = Scene::new();
|
let mut s = Scene::new();
|
||||||
let mut circle = acadrust::entities::Circle::default();
|
let mut circle = acadrust::entities::Circle::default();
|
||||||
circle.radius = 100.0;
|
circle.radius = 100.0;
|
||||||
let handle = s.add_entity(EntityType::Circle(circle));
|
let handle = s.add_entity(EntityType::Circle(circle));
|
||||||
|
|
||||||
// Far camera: distance is large -> wpp is large
|
// Far camera: distance is large
|
||||||
let mut cam_far = Camera::default();
|
let mut cam_far = Camera::default();
|
||||||
cam_far.distance = 1000.0;
|
cam_far.distance = 1000.0;
|
||||||
|
|
||||||
// Close camera: distance is small -> wpp is small
|
// Close camera: distance is small
|
||||||
let mut cam_close = Camera::default();
|
let mut cam_close = Camera::default();
|
||||||
cam_close.distance = 1.0;
|
cam_close.distance = 1.0;
|
||||||
|
|
||||||
@@ -11279,19 +11268,20 @@ mod layout_cache_tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.find(|w| w.name == handle.value().to_string())
|
.find(|w| w.name == handle.value().to_string())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let far_pts = circle_wire_far.points.len();
|
|
||||||
|
|
||||||
let wires_close = s.model_tile_wires_arc(0, &cam_close, 1.0, 1000.0);
|
let wires_close = s.model_tile_wires_arc(0, &cam_close, 1.0, 1000.0);
|
||||||
let circle_wire_close = wires_close
|
|
||||||
.iter()
|
|
||||||
.find(|w| w.name == handle.value().to_string())
|
|
||||||
.unwrap();
|
|
||||||
let close_pts = circle_wire_close.points.len();
|
|
||||||
|
|
||||||
|
// Zooming must reuse the exact same resident wire set (zero re-tessellation)
|
||||||
assert!(
|
assert!(
|
||||||
close_pts > far_pts,
|
Arc::ptr_eq(&wires_far, &wires_close),
|
||||||
"Close camera points ({close_pts}) should exceed far camera points ({far_pts})"
|
"Resident wires must be identical Arc across zoom levels"
|
||||||
);
|
);
|
||||||
assert!(close_pts > 500, "Close points ({close_pts}) should be > 500");
|
// Circle must carry TangentGeom for GPU analytical rendering
|
||||||
|
assert_eq!(circle_wire_far.tangent_geoms.len(), 1);
|
||||||
|
assert!(matches!(
|
||||||
|
circle_wire_far.tangent_geoms[0],
|
||||||
|
crate::scene::model::wire_model::TangentGeom::PlanarCircle { .. }
|
||||||
|
| crate::scene::model::wire_model::TangentGeom::Circle { .. }
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-10
@@ -819,7 +819,7 @@ impl Scene {
|
|||||||
fn model_wires_for_viewport(
|
fn model_wires_for_viewport(
|
||||||
&self,
|
&self,
|
||||||
vp_handle: Handle,
|
vp_handle: Handle,
|
||||||
screen_height_px: f32,
|
_screen_height_px: f32,
|
||||||
) -> Arc<Vec<WireModel>> {
|
) -> Arc<Vec<WireModel>> {
|
||||||
use rustc_hash::FxHashSet as HSet;
|
use rustc_hash::FxHashSet as HSet;
|
||||||
|
|
||||||
@@ -829,17 +829,12 @@ impl Scene {
|
|||||||
// wheel tick whenever the drawing contained one annotative object.
|
// wheel tick whenever the drawing contained one annotative object.
|
||||||
// Explicit viewport annotation-scale changes still rebuild the resident
|
// Explicit viewport annotation-scale changes still rebuild the resident
|
||||||
// set; PSLTSCALE is a viewport GPU uniform.
|
// set; PSLTSCALE is a viewport GPU uniform.
|
||||||
let (frozen, wpp) = match self.document.get_entity(vp_handle) {
|
let frozen = match self.document.get_entity(vp_handle) {
|
||||||
Some(EntityType::Viewport(vp)) => {
|
Some(EntityType::Viewport(vp)) => {
|
||||||
let f: HSet<Handle> = vp.frozen_layers.iter().cloned().collect();
|
let f: HSet<Handle> = vp.frozen_layers.iter().cloned().collect();
|
||||||
let w = if screen_height_px > 0.0 && vp.view_height > 0.0 {
|
f
|
||||||
Some((vp.view_height as f32) / screen_height_px)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
(f, w)
|
|
||||||
}
|
}
|
||||||
_ => (HSet::default(), None),
|
_ => HSet::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let scale_handle = self.viewport_scale_handle(vp_handle);
|
let scale_handle = self.viewport_scale_handle(vp_handle);
|
||||||
@@ -849,7 +844,6 @@ impl Scene {
|
|||||||
scale_handle,
|
scale_handle,
|
||||||
Some(&frozen),
|
Some(&frozen),
|
||||||
Some(vp_handle),
|
Some(vp_handle),
|
||||||
wpp,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user