fix(paper): give the sheet back its desk and its edge

A paper layout painted its whole canvas in the page colour, so the sheet
had no visible edge — zooming out never reached one, because the surround
was the same colour as the page. Floating viewports then painted
themselves black over it.

Both come from the same idea, that a viewport's canvas is a colour it can
be handed. What lies behind a viewport depends on what the viewport is:

- The full-canvas sheet shows the desk. `paper_bg_color` is the page, and
  the page is a bounded rectangle `paper_sheet_fill` already draws; using
  it for the canvas too erased the boundary between them. The desk is now
  its own colour, defined once in the scene and read by both the widget
  and the renderer, so the two cannot drift apart.
- A floating viewport sits on that sheet and must paint no canvas at all.
  Handing it a transparent one does not work: the background shader
  returns an opaque colour whatever alpha it is given, so alpha zero came
  out black. Skipping the pass is the only way to ask for see-through, so
  `skip_background` now suppresses both the clear and the pass. It is part
  of the frame signature too — without that, a cached frame would keep
  showing the canvas the flag was just turned off for.

Shading and fog still blend toward what is behind a floating viewport,
which is the page, so the uniform keeps that colour even though nothing
paints it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-08-08 00:11:47 +03:00
commit 62dfbfe2be
4 changed files with 45 additions and 9 deletions

View file

@ -340,6 +340,9 @@ pub struct Pipeline {
/// `render` skips the (per-pixel, GPU-dominating) hatch pass this frame. The
/// scene-render cache holds the full-quality frame once the view settles.
pub skip_hatch_frame: bool,
/// Skip the canvas entirely — no clear colour, no background pass — so
/// whatever was drawn underneath this viewport shows through it.
pub skip_background: bool,
/// Stable identity of the viewport that last used this (index-addressed)
/// pipeline slot. The renderer's viewport list drops off-canvas viewports,
/// so a slot can be reused by a *different* viewport across frames; when the
@ -2015,6 +2018,7 @@ impl Pipeline {
render_sig: u64::MAX,
skip_geometry: false,
skip_hatch_frame: false,
skip_background: false,
slot_id: u64::MAX,
}
}
@ -3282,7 +3286,7 @@ impl Pipeline {
};
let msaa = &self.msaa_view;
let [r, g, b, a] = bg_color;
let clear_color = if self.clip_boundary.is_some() {
let clear_color = if self.clip_boundary.is_some() || self.skip_background {
wgpu::Color::TRANSPARENT
} else {
wgpu::Color {
@ -3393,10 +3397,15 @@ impl Pipeline {
pass.set_vertex_buffer(0, vbuf.slice(..));
pass.draw(0..*vcount, 0..1);
}
pass.set_pipeline(&self.background_pipeline);
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
pass.set_stencil_reference(stencil_ref);
pass.draw(0..3, 0..1);
// The background shader returns an opaque colour whatever alpha it
// is handed, so a see-through viewport cannot be asked for as a
// transparent background — the pass has to not run.
if !self.skip_background {
pass.set_pipeline(&self.background_pipeline);
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
pass.set_stencil_reference(stencil_ref);
pass.draw(0..3, 0..1);
}
// The capability-selected façade dispatches storage or texture
// draws before wires so outlines remain on top in either backend.
// Skipped while navigating because per-pixel hatch work dominates