fix(viewport): blit UV crop for off-canvas viewports
Refactor the per-viewport blit so a content viewport whose screen rect extends off the canvas composites the correct slice of its MSAA target to the visible portion of the surface, instead of being slammed against the canvas corner by an `(f32 as u32)` saturation on the negative offset. - blit.wgsl + Pipeline gain a small uniform buffer (`uv_offset` / `uv_scale`). `Primitive::prepare` computes the per-viewport crop from `screen_rect`'s on-canvas fraction and writes it via the new `upload_blit_uv` helper while it still has the `Queue`. - `Pipeline::render` now takes `vp_size` (full vp — MSAA / camera size) and `surface_dest` (visible intersection — blit destination) and reads the crop from the uniform; passes that don't have a non-empty surface intersection are skipped. - `Primitive::render` clips the full vp rect against the surface clip in `i32` and skips empty intersections. The earlier "skip GPU for inactive off-canvas viewports" workaround in `active_viewports` is no longer needed. - ViewCube draws only when its hosting vp is fully on-canvas (its own pipeline writes directly to the surface and would distort under a clamped `set_viewport`). With the GPU path correct for every vp position, drop `viewport_content_wires` from `paper_canvas_wires` — the inactive viewport CPU projection was only there to mask the blit bug and was duplicating the unified shader's output. The hit-test / snap path (`entity_wires_arc`, `wires_for_block`) still calls `viewport_content_wires` directly, so picking through inactive viewports keeps working. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
600717c1ec
commit
3d24dd5168
4 changed files with 160 additions and 47 deletions
|
|
@ -4644,30 +4644,12 @@ impl Scene {
|
|||
) else {
|
||||
continue;
|
||||
};
|
||||
let is_active = self.active_viewport == Some(h);
|
||||
// Skip the GPU pass for inactive viewports whose screen rect
|
||||
// extends beyond the canvas — the per-viewport pipeline blits
|
||||
// the full MSAA at a u32-saturated (≥0) surface offset, so a
|
||||
// negative offset would paint the content at the canvas
|
||||
// corner instead of where the viewport actually sits. The CPU
|
||||
// projection in PaperCanvas already handles those correctly
|
||||
// clipped to the canvas, so the user simply sees that copy.
|
||||
// The active viewport always renders through the GPU so its
|
||||
// own render mode (Hidden Line, Gouraud, …) still applies.
|
||||
if !is_active
|
||||
&& (screen_rect.x < 0.0
|
||||
|| screen_rect.y < 0.0
|
||||
|| screen_rect.x + screen_rect.width > canvas_w
|
||||
|| screen_rect.y + screen_rect.height > canvas_h)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
out.push(ViewportInstance {
|
||||
handle: h,
|
||||
screen_rect,
|
||||
camera,
|
||||
render_mode: vp.render_mode,
|
||||
active: is_active,
|
||||
active: self.active_viewport == Some(h),
|
||||
});
|
||||
}
|
||||
out
|
||||
|
|
@ -4745,9 +4727,12 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
}
|
||||
let layout_block = self.current_layout_block_handle();
|
||||
// The unified GPU shader draws every content viewport (active and
|
||||
// inactive) directly through its own camera + scissor, so the
|
||||
// paper canvas no longer needs to CPU-project model content onto
|
||||
// the sheet. `paper_sheet_wires()` keeps the title-block /
|
||||
// annotation / viewport-border 2-D pass that GPU does not handle.
|
||||
let mut wires = self.paper_sheet_wires();
|
||||
wires.extend(self.viewport_content_wires(layout_block, None, self.active_viewport));
|
||||
if let Some(iw) = &self.interim_wire {
|
||||
wires.push(iw.clone());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue