Put the coordinate-domain lens corrections into the graph
`lens.rs` has held a `Warp` trait, a composer and two implementations — distortion and lateral chromatic aberration — since they were written, and `compose_warps` was called by nothing outside its own tests. The corrections existed, were correct, and never touched a photograph. `EditGraph` now holds them, and `compose_full` emits them between the framing prologue and the fetch. Distortion first, then CA: each warp receives the position the previous one produced, and lateral CA is a magnification about the optical axis of the *undistorted* frame, so measured on a barrel-distorted one it would be fitted to a radius no profile describes. They reach the panel the way framing already does — through `capabilities`. That was the one open question and existing practice answered it: framing is also not an `Operation`, also has parameters a photographer sets, and also arrives through that list. Because `Preset::capture` walks the same list, the sidecar, the clipboard and the undo stack carry a warp's parameters with nothing registered anywhere, and no file under `ui/` names one (FR-DEV-3a). `state()` destructures `EditGraph` field by field precisely so that a new field cannot be forgotten, and it was not. Chromatic aberration is the only thing that samples per channel, and `splits_channels` is what keeps everything else from paying for it. Red and blue are fetched from positions green is not — green is the reference and never moves, so a wrong correction still leaves one channel sharp rather than softening all three. With no CA in the chain the single-fetch path is emitted instead. The interpolating sampler is now chosen by framing *or* an active warp. Asking framing alone would have nearest-neighboured a distortion correction on an unstraightened frame, and that aliasing reads as a bad profile rather than as a missing filter. The warps go in the geometry invalidation key rather than the colour one: they decide which source pixel a colour is read from, so a tile cached across a distortion change would keep drawing the previous correction. The pipeline cache needs nothing new — `hash_source` already covers the generated body, and uniform values never enter it, so arming a warp recompiles and dragging it does not. Both are asserted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1845,7 +1845,21 @@ mod tests {
|
||||
fused_blocks += usize::from(point);
|
||||
}
|
||||
|
||||
// The count the loop above accumulated, plus framing — which emits a
|
||||
// The lens corrections, which are the third kind of block. They are
|
||||
// not in `descriptors` — they rewrite coordinates rather than
|
||||
// transform a colour, so they are not operations — and they run ahead
|
||||
// of the fetch rather than in either stage the loop above sorts into.
|
||||
let mut warp_blocks = 0;
|
||||
for desc in g.warp_descriptors() {
|
||||
let id = desc.id.0;
|
||||
assert!(
|
||||
shader.source.contains(&format!("---- warp: {id} ----")),
|
||||
"{id} was armed above and did not reach the shader"
|
||||
);
|
||||
warp_blocks += 1;
|
||||
}
|
||||
|
||||
// The counts the loops above accumulated, plus framing — which emits a
|
||||
// stage of its own rather than an operation block and is not in
|
||||
// `descriptors`. Asserted as well as the per-operation exclusive-or
|
||||
// because the two catch different faults: the XOR catches an operation
|
||||
@@ -1853,7 +1867,7 @@ mod tests {
|
||||
// in the chain asked for.
|
||||
assert_eq!(
|
||||
shader.source.matches("---- ").count(),
|
||||
fused_blocks + 1,
|
||||
fused_blocks + warp_blocks + 1,
|
||||
"the fused shader carries a block nothing in the chain asked for"
|
||||
);
|
||||
assert!(
|
||||
|
||||
Reference in New Issue
Block a user