Merge main into M12.7 (bokeh DOF: the GPU 3D path takes the CPU's bokeh-blurred plane buffers)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-03 02:17:50 -04:00
co-authored by Claude Opus 5.5
68 changed files with 4037 additions and 182 deletions
+4 -15
View File
@@ -1,6 +1,6 @@
//! Classic 3D runs on the GPU (`classic3d.wgsl`): the CPU prepares the planes
//! ([`Renderer::prepare_3d_run`](effectcraft_render::Renderer::prepare_3d_run): layer buffers,
//! homographies, materials, lights, track mattes), the GPU blurs depth of field, packs the
//! homographies, materials, lights, track mattes, bokeh depth of field), the GPU packs the
//! buffers into one atlas texture and runs the per-pixel depth sort, lighting, ray-cast shadows
//! and blending of `render::three_d::compose` in one kernel.
@@ -136,22 +136,11 @@ pub(crate) fn draw_run(e: &mut Enc, run: &Run3d, canvas: &GpuImage) -> Option<Gp
{
return None;
}
// Plane textures: the (cached) layer upload, depth-of-field blurred when asked.
// Plane textures: the layer uploads (cached per buffer; bokeh depth of field is already in
// them).
let mut texs: Vec<(GpuImage, [f64; 2])> = Vec::with_capacity(run.planes.len());
for p in &run.planes {
let mut img = e.g.upload_buf(&p.buf)?;
let mut off = p.buf.offset;
if let Some((sigma, pad)) = p.dof {
let (pw, ph) = (img.width + 2 * pad, img.height + 2 * pad);
if !e.g.fits(pw, ph) {
return None;
}
let padded = e.image(pw, ph);
e.copy_into(&img, &padded, pad, pad);
img = crate::effects::gaussian_blur(e, &padded, sigma, sigma, false);
off = [off[0] + pad as f64, off[1] + pad as f64];
}
texs.push((img, off));
texs.push((e.g.upload_buf(&p.buf)?, p.buf.offset));
}
let sizes: Vec<(u32, u32)> = texs.iter().map(|(t, _)| (t.width, t.height)).collect();
let (aw, ah, pos) = pack(&sizes, e.g.max_dim)?;
+1 -1
View File
@@ -111,7 +111,7 @@ fn box_passes(e: &mut Enc, img: &GpuImage, rx: &[usize], ry: &[usize], repeat: b
}
/// raster::gaussian_blur (3 box passes per axis).
pub(crate) fn gaussian_blur(e: &mut Enc, img: &GpuImage, sx: f64, sy: f64, repeat: bool) -> GpuImage {
fn gaussian_blur(e: &mut Enc, img: &GpuImage, sx: f64, sy: f64, repeat: bool) -> GpuImage {
let rx = if sx > 0.05 { box_radii(sx, 3) } else { vec![] };
let ry = if sy > 0.05 { box_radii(sy, 3) } else { vec![] };
box_passes(e, img, &rx, &ry, repeat)