The camera-space tap: OutputMode::CameraLinear, composed with no operations
compose_camera_linear composes the fused pass with an empty operation list, the file's orientation as the baseline, a view rect for the tile, and a store of rgba32float. On the GPU, render_camera_linear is the only entry that accepts it: it fills the profile uniforms neutral — unit white balance, identity matrix, curve off — so what lands in the texture is the sensor's numbers after the lens warp and nothing else (FR-MRG-2). A third bind-group layout carries the format, as the linear one does, and the readback is generalised to any pixel width for the f32 copy. Thirty-two bits because the composite is written back at the sensor's scale: a 14-bit sensor has 16 384 steps to white and f16 keeps 2 048 of them in the top octave.
This commit is contained in:
@@ -424,6 +424,21 @@ pub enum OutputMode {
|
||||
/// clamping before a sharpener sees it would draw a hard edge at precisely
|
||||
/// the luminance a sharpener is most visible at.
|
||||
LinearWorking,
|
||||
/// TRACES: FR-MRG-2
|
||||
/// `rgba32float`, **camera space**: after the lens warp and nothing else.
|
||||
///
|
||||
/// What a merge stitches (FR-MRG-2). The shader is the linear tail with
|
||||
/// no operations, and the caller fills the reserved uniforms neutral —
|
||||
/// unit white balance, identity matrix, base curve off — so what is
|
||||
/// stored is the sensor's own numbers, demosaiced and undistorted. Only
|
||||
/// [`compose_camera_linear`] produces it, and only
|
||||
/// `AdjustPass::render_camera_linear` accepts it, so the neutral
|
||||
/// uniforms cannot be forgotten by a caller that composed it by mistake.
|
||||
///
|
||||
/// Thirty-two bits rather than sixteen because the composite is written
|
||||
/// back as a RAW at the sensor's scale (FR-MRG-3): a 14-bit sensor has
|
||||
/// 16 384 steps to white and `f16` keeps 2 048 of them in the top octave.
|
||||
CameraLinear,
|
||||
}
|
||||
|
||||
/// The result of composing a set of operations into one shader.
|
||||
@@ -573,6 +588,54 @@ pub fn compose_full_revealing(
|
||||
spots: &crate::spot::SpotSet,
|
||||
warps: &[Box<dyn crate::lens::Warp>],
|
||||
reveal: Option<&crate::mask::Reveal>,
|
||||
) -> ComposedShader {
|
||||
compose_inner(ops, framing, output, masks, spots, warps, reveal, None)
|
||||
}
|
||||
|
||||
/// TRACES: FR-MRG-2
|
||||
/// The camera-space tap: the fused pass with no operations, stopping after
|
||||
/// the lens warp and storing `rgba32float` ([`OutputMode::CameraLinear`]).
|
||||
///
|
||||
/// Takes the warps and a view, because that is all the tap uses of an edit:
|
||||
/// no crop (a merge wants the whole frame, and crops the composite), no
|
||||
/// masks, no spots, no operations. `view` is the tile — the fraction of the
|
||||
/// undistorted frame to render, in `Framing::set_view`'s terms — so that a
|
||||
/// merge pulls source tiles on demand (FR-MRG-11) rather than a frame that
|
||||
/// may not fit. The camera profile's uniforms are still declared — the
|
||||
/// prologue is the same — and the GPU side fills them neutral.
|
||||
pub fn compose_camera_linear(
|
||||
warps: &[Box<dyn crate::lens::Warp>],
|
||||
baseline: dr_types::Orientation,
|
||||
view: crate::framing::CropRect,
|
||||
) -> ComposedShader {
|
||||
let mut framing = Framing::default();
|
||||
// The file's orientation and nothing of the user's: a merge aligned
|
||||
// its frames upright (`dr_pano::Gray::oriented`), so its tiles must be
|
||||
// upright too, and `view` is a fraction of the upright frame.
|
||||
framing.set_baseline(baseline);
|
||||
framing.set_view(view);
|
||||
compose_inner(
|
||||
&[],
|
||||
&framing,
|
||||
ColourSpace::Srgb,
|
||||
&MaskStack::new(),
|
||||
&crate::spot::SpotSet::new(),
|
||||
warps,
|
||||
None,
|
||||
Some(OutputMode::CameraLinear),
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compose_inner(
|
||||
ops: &[Box<dyn Operation>],
|
||||
framing: &Framing,
|
||||
output: ColourSpace,
|
||||
masks: &MaskStack,
|
||||
spots: &crate::spot::SpotSet,
|
||||
warps: &[Box<dyn crate::lens::Warp>],
|
||||
reveal: Option<&crate::mask::Reveal>,
|
||||
forced: Option<OutputMode>,
|
||||
) -> ComposedShader {
|
||||
// The lens corrections, composed into one coordinate transform. Beside
|
||||
// `framing` because they are the other half of the same stage: framing
|
||||
@@ -603,12 +666,18 @@ pub fn compose_full_revealing(
|
||||
// all: a photograph with a spot on it and no sharpening still has a detail
|
||||
// stage, and a fused pass that encoded its own output there would quantise
|
||||
// twice and be bound to a texture of the wrong format.
|
||||
let output_mode =
|
||||
//
|
||||
// `forced` is the one exception, and it is not a caller flag in the
|
||||
// sense above: `compose_camera_linear` is the only function that passes
|
||||
// it, with an empty operation list, and the mode it forces has its own
|
||||
// storage format and its own render entry on the GPU side.
|
||||
let output_mode = forced.unwrap_or(
|
||||
if ops.iter().any(|o| o.is_active() && o.detail().is_some()) || !spots.is_neutral() {
|
||||
OutputMode::LinearWorking
|
||||
} else {
|
||||
OutputMode::Encoded
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// Whether an operation has taken over the rendering. Decided from the
|
||||
// operations for the same reason `output_mode` is: a caller that got it
|
||||
@@ -827,6 +896,16 @@ pub fn compose_full_revealing(
|
||||
// would put a hard edge into the very neighbourhood the next pass is
|
||||
// about to convolve, which is how sharpeners come to draw dark rings
|
||||
// around specular highlights.
|
||||
textureStore(output, vec2<i32>(gid.xy), vec4<f32>(c, 1.0));"
|
||||
.to_string(),
|
||||
),
|
||||
OutputMode::CameraLinear => (
|
||||
"rgba32float",
|
||||
String::new(),
|
||||
String::new(),
|
||||
" // Camera space, for a merge (FR-MRG-2): the sensor's numbers after the
|
||||
// lens warp, with the profile uniforms filled neutral by the caller so the
|
||||
// prologue above changed nothing. Not clamped, not encoded, full precision.
|
||||
textureStore(output, vec2<i32>(gid.xy), vec4<f32>(c, 1.0));"
|
||||
.to_string(),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user