forked from 1b-agent/opencadstudio-upstream
refactor: remove the legacy full view_proj (CPU cull now relative-to-eye)
The GPU draws exclusively via the rotation-only `view_rot` + eye double- single; the full view-projection survived only to project AABBs in the CPU frustum-cull / scissor / LOD passes. Migrate those to the same relative-to-eye math (project `point - eye` through `view_rot`) — pixel- equivalent but precise at UTM scale — then drop the dead uniform: - pipeline cull/scissor/LOD helpers take (view_rot, eye) instead of the full view_proj; render.rs reconstructs eye from the uniform's eye_high/eye_low and passes the cropped view_rot. - Remove the `view_proj` field from the shared Uniforms struct and all six shader structs that only declared it for layout (none read it), and delete Camera::view_proj(). The struct shrinks by one mat4 on both sides, keeping the Rust / WGSL layouts in lock-step. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
5a8ec35f4f
commit
04c93df2c4
10 files changed
+57
-80
No files matched your search
+42
-36
@@ -1041,11 +1041,11 @@ impl Pipeline {
|
||||
|
||||
/// Recompute pixel scissor rects for viewport-clipped wires from the current view_proj.
|
||||
/// Called every frame from prepare() because scissor pixels shift with pan/zoom.
|
||||
pub fn compute_wire_scissors(&mut self, view_proj: glam::Mat4, clip_w: u32, clip_h: u32) {
|
||||
pub fn compute_wire_scissors(&mut self, view_rot: glam::Mat4, eye: glam::DVec3, clip_w: u32, clip_h: u32) {
|
||||
self.wire_pixel_scissors = self
|
||||
.gpu_wires
|
||||
.iter()
|
||||
.map(|w| project_scissor(w.vp_scissor, view_proj, clip_w, clip_h))
|
||||
.map(|w| project_scissor(w.vp_scissor, view_rot, eye, clip_w, clip_h))
|
||||
.collect();
|
||||
}
|
||||
|
||||
@@ -1058,7 +1058,8 @@ impl Pipeline {
|
||||
pub fn compute_hatch_lod(
|
||||
&mut self,
|
||||
queue: &wgpu::Queue,
|
||||
view_proj: glam::Mat4,
|
||||
view_rot: glam::Mat4,
|
||||
eye: glam::DVec3,
|
||||
clip_w: u32,
|
||||
clip_h: u32,
|
||||
) {
|
||||
@@ -1066,19 +1067,19 @@ impl Pipeline {
|
||||
return;
|
||||
};
|
||||
for (i, aabb) in batch.instance_aabbs.iter().enumerate() {
|
||||
let skip = aabb_below_pixel(*aabb, view_proj, clip_w, clip_h, 2.0)
|
||||
|| aabb_offscreen(*aabb, view_proj, clip_w, clip_h);
|
||||
let skip = aabb_below_pixel(*aabb, view_rot, eye, clip_w, clip_h, 2.0)
|
||||
|| aabb_offscreen(*aabb, view_rot, eye, clip_w, clip_h);
|
||||
batch.visibility[i] = if skip { 0 } else { 1 };
|
||||
}
|
||||
batch.upload_visibility(queue);
|
||||
}
|
||||
|
||||
/// Recompute pixel scissor rects for viewport-clipped wipeouts.
|
||||
pub fn compute_wipeout_scissors(&mut self, view_proj: glam::Mat4, clip_w: u32, clip_h: u32) {
|
||||
pub fn compute_wipeout_scissors(&mut self, view_rot: glam::Mat4, eye: glam::DVec3, clip_w: u32, clip_h: u32) {
|
||||
self.wipeout_pixel_scissors = self
|
||||
.gpu_wipeouts
|
||||
.iter()
|
||||
.map(|h| project_scissor(h.vp_scissor, view_proj, clip_w, clip_h))
|
||||
.map(|h| project_scissor(h.vp_scissor, view_rot, eye, clip_w, clip_h))
|
||||
.collect();
|
||||
}
|
||||
|
||||
@@ -1086,20 +1087,20 @@ impl Pipeline {
|
||||
/// `compute_hatch_lod`'s frustum branch. No sub-pixel skip:
|
||||
/// wipeouts mask, so dropping a sub-pixel one wouldn't be wrong
|
||||
/// but also wouldn't pay off — they're usually few.
|
||||
pub fn compute_wipeout_lod(&mut self, view_proj: glam::Mat4, clip_w: u32, clip_h: u32) {
|
||||
pub fn compute_wipeout_lod(&mut self, view_rot: glam::Mat4, eye: glam::DVec3, clip_w: u32, clip_h: u32) {
|
||||
self.wipeout_skip_flags = self
|
||||
.gpu_wipeouts
|
||||
.iter()
|
||||
.map(|h| aabb_offscreen(h.world_aabb, view_proj, clip_w, clip_h))
|
||||
.map(|h| aabb_offscreen(h.world_aabb, view_rot, eye, clip_w, clip_h))
|
||||
.collect();
|
||||
}
|
||||
|
||||
/// Recompute pixel scissor rects for viewport-clipped raster images.
|
||||
pub fn compute_image_scissors(&mut self, view_proj: glam::Mat4, clip_w: u32, clip_h: u32) {
|
||||
pub fn compute_image_scissors(&mut self, view_rot: glam::Mat4, eye: glam::DVec3, clip_w: u32, clip_h: u32) {
|
||||
self.image_pixel_scissors = self
|
||||
.gpu_images
|
||||
.iter()
|
||||
.map(|i| project_scissor(i.vp_scissor, view_proj, clip_w, clip_h))
|
||||
.map(|i| project_scissor(i.vp_scissor, view_rot, eye, clip_w, clip_h))
|
||||
.collect();
|
||||
}
|
||||
|
||||
@@ -1175,11 +1176,11 @@ impl Pipeline {
|
||||
/// projected pixel diagonal of each mesh's `world_aabb` (Phase 3.4
|
||||
/// ladder: >200 px → 0, 50–200 → 1, <50 → 2). Falls back to the
|
||||
/// nearest available lower slot when a level wasn't generated.
|
||||
pub fn compute_mesh_lod(&mut self, view_proj: glam::Mat4, clip_w: u32, clip_h: u32) {
|
||||
pub fn compute_mesh_lod(&mut self, view_rot: glam::Mat4, eye: glam::DVec3, clip_w: u32, clip_h: u32) {
|
||||
self.mesh_lod_levels = self
|
||||
.gpu_meshes
|
||||
.iter()
|
||||
.map(|m| pick_mesh_lod(m, view_proj, clip_w, clip_h))
|
||||
.map(|m| pick_mesh_lod(m, view_rot, eye, clip_w, clip_h))
|
||||
.collect();
|
||||
// Phase 2.2 — frustum-visibility flag per mesh. Cheap: same
|
||||
// 4-corner projection used for LOD selection, just answering a
|
||||
@@ -1187,7 +1188,7 @@ impl Pipeline {
|
||||
self.mesh_visible = self
|
||||
.gpu_meshes
|
||||
.iter()
|
||||
.map(|m| !aabb_offscreen(m.world_aabb, view_proj, clip_w, clip_h))
|
||||
.map(|m| !aabb_offscreen(m.world_aabb, view_rot, eye, clip_w, clip_h))
|
||||
.collect();
|
||||
}
|
||||
|
||||
@@ -1847,11 +1848,12 @@ impl Pipeline {
|
||||
/// to the next available coarser/finer level so the mesh always renders.
|
||||
fn pick_mesh_lod(
|
||||
mesh: &MeshLodGpu,
|
||||
view_proj: glam::Mat4,
|
||||
view_rot: glam::Mat4,
|
||||
eye: glam::DVec3,
|
||||
clip_w: u32,
|
||||
clip_h: u32,
|
||||
) -> usize {
|
||||
let diag_px = aabb_diagonal_pixels(mesh.world_aabb, view_proj, clip_w, clip_h);
|
||||
let diag_px = aabb_diagonal_pixels(mesh.world_aabb, view_rot, eye, clip_w, clip_h);
|
||||
let target = if diag_px > 200.0 {
|
||||
0
|
||||
} else if diag_px > 50.0 {
|
||||
@@ -1870,7 +1872,8 @@ fn pick_mesh_lod(
|
||||
|
||||
fn aabb_diagonal_pixels(
|
||||
aabb: [f32; 4],
|
||||
view_proj: glam::Mat4,
|
||||
view_rot: glam::Mat4,
|
||||
eye: glam::DVec3,
|
||||
clip_w: u32,
|
||||
clip_h: u32,
|
||||
) -> f32 {
|
||||
@@ -1881,10 +1884,10 @@ fn aabb_diagonal_pixels(
|
||||
let w = clip_w as f32;
|
||||
let h = clip_h as f32;
|
||||
let corners = [
|
||||
view_proj.project_point3(glam::Vec3::new(x0, y0, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x1, y0, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x0, y1, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x1, y1, 0.0)),
|
||||
view_rot.project_point3((glam::DVec3::new(x0 as f64, y0 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x1 as f64, y0 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x0 as f64, y1 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x1 as f64, y1 as f64, 0.0) - eye).as_vec3()),
|
||||
];
|
||||
let mut min_px = f32::INFINITY;
|
||||
let mut max_px = f32::NEG_INFINITY;
|
||||
@@ -1916,7 +1919,8 @@ fn aabb_diagonal_pixels(
|
||||
/// extents` for this reason; meshes already store an absolute rect.
|
||||
fn aabb_offscreen(
|
||||
aabb: [f32; 4],
|
||||
view_proj: glam::Mat4,
|
||||
view_rot: glam::Mat4,
|
||||
eye: glam::DVec3,
|
||||
clip_w: u32,
|
||||
clip_h: u32,
|
||||
) -> bool {
|
||||
@@ -1927,10 +1931,10 @@ fn aabb_offscreen(
|
||||
let w = clip_w as f32;
|
||||
let h = clip_h as f32;
|
||||
let corners = [
|
||||
view_proj.project_point3(glam::Vec3::new(x0, y0, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x1, y0, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x0, y1, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x1, y1, 0.0)),
|
||||
view_rot.project_point3((glam::DVec3::new(x0 as f64, y0 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x1 as f64, y0 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x0 as f64, y1 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x1 as f64, y1 as f64, 0.0) - eye).as_vec3()),
|
||||
];
|
||||
let mut min_px = f32::INFINITY;
|
||||
let mut max_px = f32::NEG_INFINITY;
|
||||
@@ -1958,7 +1962,8 @@ fn aabb_offscreen(
|
||||
/// draw calls that wouldn't contribute a visible pixel.
|
||||
fn aabb_below_pixel(
|
||||
aabb: [f32; 4],
|
||||
view_proj: glam::Mat4,
|
||||
view_rot: glam::Mat4,
|
||||
eye: glam::DVec3,
|
||||
clip_w: u32,
|
||||
clip_h: u32,
|
||||
threshold_px: f32,
|
||||
@@ -1970,10 +1975,10 @@ fn aabb_below_pixel(
|
||||
let w = clip_w as f32;
|
||||
let h = clip_h as f32;
|
||||
let corners = [
|
||||
view_proj.project_point3(glam::Vec3::new(x0, y0, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x1, y0, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x0, y1, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x1, y1, 0.0)),
|
||||
view_rot.project_point3((glam::DVec3::new(x0 as f64, y0 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x1 as f64, y0 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x0 as f64, y1 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x1 as f64, y1 as f64, 0.0) - eye).as_vec3()),
|
||||
];
|
||||
let mut min_px = f32::INFINITY;
|
||||
let mut max_px = f32::NEG_INFINITY;
|
||||
@@ -1996,7 +2001,8 @@ fn aabb_below_pixel(
|
||||
/// the projection collapses (off-screen, behind camera).
|
||||
fn project_scissor(
|
||||
rect: Option<[f32; 4]>,
|
||||
view_proj: glam::Mat4,
|
||||
view_rot: glam::Mat4,
|
||||
eye: glam::DVec3,
|
||||
clip_w: u32,
|
||||
clip_h: u32,
|
||||
) -> Option<[u32; 4]> {
|
||||
@@ -2004,10 +2010,10 @@ fn project_scissor(
|
||||
let w = clip_w as f32;
|
||||
let h = clip_h as f32;
|
||||
let corners = [
|
||||
view_proj.project_point3(glam::Vec3::new(x0, y0, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x1, y0, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x0, y1, 0.0)),
|
||||
view_proj.project_point3(glam::Vec3::new(x1, y1, 0.0)),
|
||||
view_rot.project_point3((glam::DVec3::new(x0 as f64, y0 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x1 as f64, y0 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x0 as f64, y1 as f64, 0.0) - eye).as_vec3()),
|
||||
view_rot.project_point3((glam::DVec3::new(x1 as f64, y1 as f64, 0.0) - eye).as_vec3()),
|
||||
];
|
||||
let px: Vec<f32> = corners.iter().map(|c| (c.x + 1.0) * 0.5 * w).collect();
|
||||
let py: Vec<f32> = corners.iter().map(|c| (1.0 - c.y) * 0.5 * h).collect();
|
||||
|
||||
@@ -4,11 +4,6 @@ use iced::Rectangle;
|
||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Uniforms {
|
||||
/// Full view-projection (with translation) — GPU rendering no longer uses
|
||||
/// this; it remains for the CPU-side frustum-cull / scissor / LOD passes
|
||||
/// that project offset-relative AABBs to screen. The GPU draws via the
|
||||
/// relative-to-eye `view_rot` + `eye_high`/`eye_low` below.
|
||||
pub view_proj: glam::Mat4,
|
||||
pub viewport_size: [f32; 2],
|
||||
/// World units per screen pixel at the current zoom. Used by the
|
||||
/// hatch shader to substitute solid fill when pattern line spacing
|
||||
@@ -54,7 +49,6 @@ impl Uniforms {
|
||||
};
|
||||
let (eye_high, eye_low) = camera.eye_high_low();
|
||||
Self {
|
||||
view_proj: camera.view_proj(bounds),
|
||||
viewport_size: [bounds.width, bounds.height],
|
||||
world_per_pixel,
|
||||
lwdisplay_enable: if lwdisplay_enable { 1.0 } else { 0.0 },
|
||||
|
||||
@@ -91,28 +91,6 @@ impl Camera {
|
||||
|
||||
// ── Projection matrices ────────────────────────────────────────────────
|
||||
|
||||
pub fn view_proj(&self, bounds: Rectangle) -> Mat4 {
|
||||
let aspect = bounds.width / bounds.height;
|
||||
|
||||
// Up vector: use the rotation to find which world direction is "up"
|
||||
// in the current camera frame.
|
||||
let up_dir = self.rotation * Vec3::Y;
|
||||
|
||||
let view = Mat4::look_at_rh(self.eye(), self.target.as_vec3(), up_dir);
|
||||
let proj = match self.projection {
|
||||
Projection::Perspective => {
|
||||
Mat4::perspective_rh(self.fov_y, aspect, self.distance * 0.001, self.distance * 1000.0)
|
||||
}
|
||||
Projection::Orthographic => {
|
||||
let h = self.ortho_size();
|
||||
let w = h * aspect;
|
||||
let (near, far) = self.ortho_depth_range();
|
||||
Mat4::orthographic_rh(-w, w, -h, h, near, far)
|
||||
}
|
||||
};
|
||||
OPENGL_TO_WGPU * proj * view
|
||||
}
|
||||
|
||||
/// Orthographic near/far that CENTRE the target plane at ndc-z ≈ 0.5.
|
||||
///
|
||||
/// The draw-order depth bias shifts clip-z by ±`DRAW_ORDER_BIAS` (0.001).
|
||||
|
||||
+15
-10
@@ -305,13 +305,20 @@ impl shader::Primitive for Primitive {
|
||||
// refreshed every frame it's present, so a drag never re-uploads
|
||||
// the resident base wire buffer.
|
||||
inner.upload_preview_wires(device, &vp.preview_wires[..], &vp.draw_depths);
|
||||
let vproj = vp.uniforms.view_proj;
|
||||
inner.compute_wire_scissors(vproj, clip_size.width, clip_size.height);
|
||||
inner.compute_wipeout_scissors(vproj, clip_size.width, clip_size.height);
|
||||
inner.compute_image_scissors(vproj, clip_size.width, clip_size.height);
|
||||
inner.compute_hatch_lod(queue, vproj, clip_size.width, clip_size.height);
|
||||
inner.compute_wipeout_lod(vproj, clip_size.width, clip_size.height);
|
||||
inner.compute_mesh_lod(vproj, clip_size.width, clip_size.height);
|
||||
// Cull / scissor / LOD project AABBs relative-to-eye (matching the
|
||||
// GPU's RTE path) so the math stays precise at UTM-scale coords.
|
||||
let view_rot = vp.uniforms.view_rot;
|
||||
let eye = glam::DVec3::new(
|
||||
vp.uniforms.eye_high[0] as f64 + vp.uniforms.eye_low[0] as f64,
|
||||
vp.uniforms.eye_high[1] as f64 + vp.uniforms.eye_low[1] as f64,
|
||||
vp.uniforms.eye_high[2] as f64 + vp.uniforms.eye_low[2] as f64,
|
||||
);
|
||||
inner.compute_wire_scissors(view_rot, eye, clip_size.width, clip_size.height);
|
||||
inner.compute_wipeout_scissors(view_rot, eye, clip_size.width, clip_size.height);
|
||||
inner.compute_image_scissors(view_rot, eye, clip_size.width, clip_size.height);
|
||||
inner.compute_hatch_lod(queue, view_rot, eye, clip_size.width, clip_size.height);
|
||||
inner.compute_wipeout_lod(view_rot, eye, clip_size.width, clip_size.height);
|
||||
inner.compute_mesh_lod(view_rot, eye, clip_size.width, clip_size.height);
|
||||
if vp.show_viewcube {
|
||||
inner.viewcube.upload(
|
||||
queue,
|
||||
@@ -743,9 +750,7 @@ impl Scene {
|
||||
};
|
||||
let mut uniforms =
|
||||
Uniforms::new(&inst.camera, full_bounds, self.document.header.lineweight_display);
|
||||
uniforms.view_proj = crop_view_proj(uniforms.view_proj, uo, vo, us, vs);
|
||||
// The RTE matrix is a view-projection too, so the same clip-space crop
|
||||
// applies — keep it in lock-step with view_proj for tiled / sub-rect panes.
|
||||
// Crop the rotation-only RTE view-projection to the visible sub-rect.
|
||||
uniforms.view_rot = crop_view_proj(uniforms.view_rot, uo, vo, us, vs);
|
||||
uniforms.viewport_size = [visible_w, visible_h];
|
||||
uniforms.flat_shade = if flags.flat_shade { 1.0 } else { 0.0 };
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// color [f32; 4] offset 12 16 B
|
||||
|
||||
struct Uniforms {
|
||||
view_proj: mat4x4<f32>,
|
||||
viewport_size: vec2<f32>,
|
||||
world_per_pixel: f32,
|
||||
lwdisplay_enable: f32,
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// ── Group 0: frame uniforms (shared) ──────────────────────────────────────
|
||||
|
||||
struct Uniforms {
|
||||
view_proj: mat4x4<f32>,
|
||||
viewport_size: vec2<f32>,
|
||||
world_per_pixel: f32,
|
||||
lwdisplay_enable: f32,
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
// ── Group 0: shared frame uniforms (matches hatch.wgsl) ──────────────────
|
||||
|
||||
struct Uniforms {
|
||||
view_proj: mat4x4<f32>,
|
||||
viewport_size: vec2<f32>,
|
||||
world_per_pixel: f32,
|
||||
lwdisplay_enable: f32,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// ── Bind group 0: shared projection uniforms ─────────────────────────────────
|
||||
// Must match the shared `Uniforms` struct (scene::pipeline::uniforms, 112 B).
|
||||
struct Uniforms {
|
||||
view_proj: mat4x4<f32>,
|
||||
viewport_size: vec2<f32>,
|
||||
world_per_pixel: f32,
|
||||
lwdisplay_enable: f32,
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
// flat shade (FlatShaded).
|
||||
|
||||
struct Uniforms {
|
||||
view_proj: mat4x4<f32>,
|
||||
viewport_size: vec2<f32>,
|
||||
world_per_pixel: f32,
|
||||
lwdisplay_enable: f32,
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
// • pat0/pat1 encode up to 8 elements: positive=dash, negative=gap.
|
||||
|
||||
struct Uniforms {
|
||||
view_proj: mat4x4<f32>,
|
||||
viewport_size: vec2<f32>,
|
||||
world_per_pixel: f32,
|
||||
// LWDISPLAY toggle: 0.0 = force 1 px (half_width 0.5), 1.0 = use the
|
||||
|
||||
Reference in new issue
Block a user