mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-10 18:31:30 -06:00
Merge main
This commit is contained in:
@@ -26,7 +26,7 @@ pub(crate) fn apply(e: &mut Enc, src: &GpuImage, d: &PlaneDof) -> Option<GpuImag
|
||||
padded
|
||||
};
|
||||
let prefix = e.image(w + 1, h);
|
||||
e.dispatch("bokeh_prefix", &Params::default(), &boosted, None, &prefix, None, (h.div_ceil(64), 1));
|
||||
e.dispatch("bokeh_prefix", &Params::default(), crate::context::KernelImages { src: &boosted, aux: None, out: &prefix }, None, (h.div_ceil(64), 1));
|
||||
// Radius map, then every level's spans.
|
||||
let mut data: Vec<f32> = d.radius.as_ref().clone();
|
||||
let mut levels = vec![];
|
||||
|
||||
@@ -214,7 +214,14 @@ pub(crate) fn draw_run(e: &mut Enc, run: &Run3d, canvas: &GpuImage) -> Option<Gp
|
||||
let mut p = Params::default();
|
||||
p.u[0] = [run.planes.len() as u32, run.lights.len() as u32, run.casters.len() as u32, 0];
|
||||
let out = e.scratch(w, h);
|
||||
e.dispatch_ext("classic3d", &p, &atlas, Some(canvas), &out, Some(&mb), (w.div_ceil(16), h.div_ceil(16)), Some([&pb, &gb, &lb, &cb]));
|
||||
e.dispatch_ext(
|
||||
"classic3d",
|
||||
&p,
|
||||
crate::context::KernelImages { src: &atlas, aux: Some(canvas), out: &out },
|
||||
Some(&mb),
|
||||
(w.div_ceil(16), h.div_ceil(16)),
|
||||
Some([&pb, &gb, &lb, &cb]),
|
||||
);
|
||||
Some(out)
|
||||
}
|
||||
|
||||
|
||||
+12
-16
@@ -1089,6 +1089,13 @@ pub(crate) struct Enc<'g> {
|
||||
owner: u64,
|
||||
}
|
||||
|
||||
/// Images bound to a compute kernel's source, optional auxiliary and output slots.
|
||||
pub(crate) struct KernelImages<'a> {
|
||||
pub src: &'a GpuImage,
|
||||
pub aux: Option<&'a GpuImage>,
|
||||
pub out: &'a GpuImage,
|
||||
}
|
||||
|
||||
impl Drop for Enc<'_> {
|
||||
fn drop(&mut self) {
|
||||
// Unsubmitted work is discarded, so nothing recorded will read pooled textures.
|
||||
@@ -1162,32 +1169,21 @@ impl<'g> Enc<'g> {
|
||||
}
|
||||
|
||||
/// Run kernel `entry` writing `out` (dispatched over `groups` workgroups).
|
||||
pub fn dispatch(
|
||||
&mut self,
|
||||
entry: &str,
|
||||
p: &Params,
|
||||
src: &GpuImage,
|
||||
aux: Option<&GpuImage>,
|
||||
out: &GpuImage,
|
||||
data: Option<&wgpu::Buffer>,
|
||||
groups: (u32, u32),
|
||||
) {
|
||||
self.dispatch_ext(entry, p, src, aux, out, data, groups, None);
|
||||
pub fn dispatch(&mut self, entry: &str, p: &Params, images: KernelImages<'_>, data: Option<&wgpu::Buffer>, groups: (u32, u32)) {
|
||||
self.dispatch_ext(entry, p, images, data, groups, None);
|
||||
}
|
||||
|
||||
/// [`Enc::dispatch`] for the kernels in `EXT_ENTRIES`, with their group 1 storage buffers.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn dispatch_ext(
|
||||
&mut self,
|
||||
entry: &str,
|
||||
p: &Params,
|
||||
src: &GpuImage,
|
||||
aux: Option<&GpuImage>,
|
||||
out: &GpuImage,
|
||||
images: KernelImages<'_>,
|
||||
data: Option<&wgpu::Buffer>,
|
||||
groups: (u32, u32),
|
||||
ext: Option<[&wgpu::Buffer; 4]>,
|
||||
) {
|
||||
let KernelImages { src, aux, out } = images;
|
||||
let g = self.g;
|
||||
let Some(pipe) = g.pipelines.get(entry) else {
|
||||
if !g.unavailable.iter().any(|(e, _)| *e == entry) {
|
||||
@@ -1243,7 +1239,7 @@ impl<'g> Enc<'g> {
|
||||
/// Per-pixel kernel over `out`'s size (16×16 workgroups).
|
||||
pub fn pixels(&mut self, entry: &str, p: &Params, src: &GpuImage, aux: Option<&GpuImage>, out: &GpuImage, data: Option<&wgpu::Buffer>) {
|
||||
let groups = (out.width.div_ceil(16), out.height.div_ceil(16));
|
||||
self.dispatch(entry, p, src, aux, out, data, groups);
|
||||
self.dispatch(entry, p, KernelImages { src, aux, out }, data, groups);
|
||||
}
|
||||
|
||||
/// Copy `src` into a larger zeroed image at (`x`, `y`) (Buf::pad).
|
||||
|
||||
@@ -234,7 +234,13 @@ pub(crate) fn box_passes(e: &mut Enc, img: &GpuImage, rx: &[usize], ry: &[usize]
|
||||
let mut p = Params::default();
|
||||
p.u[0] = [r as u32, repeat as u32, block, 0];
|
||||
let out = e.scratch(cur.width, cur.height);
|
||||
e.dispatch(if vertical { "box_v" } else { "box_h" }, &p, &cur, None, &out, None, (lines.div_ceil(64), n.div_ceil(block)));
|
||||
e.dispatch(
|
||||
if vertical { "box_v" } else { "box_h" },
|
||||
&p,
|
||||
crate::context::KernelImages { src: &cur, aux: None, out: &out },
|
||||
None,
|
||||
(lines.div_ceil(64), n.div_ceil(block)),
|
||||
);
|
||||
cur = out;
|
||||
}
|
||||
cur
|
||||
@@ -390,7 +396,7 @@ fn transform(e: &mut Enc, ctx: &EffectCtx, mut b: GBuf) -> Option<GBuf> {
|
||||
* Mat3::translate(vec2(-anchor.0, -anchor.1));
|
||||
let sampling = if ctx.params.e("sampling") == 1 { Sampling::Bicubic } else { Sampling::Bilinear };
|
||||
let empty = e.image(b.img.width, b.img.height);
|
||||
b.img = ops::warp(e, &empty, &b.img, &m, sampling, effectcraft_color::BlendMode::Normal, opacity, 0, None);
|
||||
b.img = ops::warp(e, &empty, &b.img, &m, ops::WarpOptions { sampling, mode: effectcraft_color::BlendMode::Normal, opacity, seed: 0 }, None);
|
||||
Some(b)
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ fn camera_lens(e: &mut Enc, ctx: &EffectCtx, mut b: GBuf) -> Option<GBuf> {
|
||||
return None;
|
||||
}
|
||||
let pre = e.image(w + 1, h);
|
||||
e.dispatch("gen_row_prefix", &Params::default(), &src, None, &pre, None, (h.div_ceil(64), 1));
|
||||
e.dispatch("gen_row_prefix", &Params::default(), crate::context::KernelImages { src: &src, aux: None, out: &pre }, None, (h.div_ceil(64), 1));
|
||||
Some(pre)
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -214,7 +214,7 @@ pub(crate) fn median_image(e: &mut Enc, img: &GpuImage, r: usize, straight: bool
|
||||
let seg = (4 * r as u32).clamp(32, 512).min(q.width);
|
||||
let out = e.scratch(q.width, q.height);
|
||||
let groups = (q.height.div_ceil(e.g.median_kernel.rows_per_workgroup()), q.width.div_ceil(seg));
|
||||
e.dispatch("fxn_median_huang", &with_u0([r as u32, seg, 0, 0]), &q, None, &out, Some(&levels), groups);
|
||||
e.dispatch("fxn_median_huang", &with_u0([r as u32, seg, 0, 0]), crate::context::KernelImages { src: &q, aux: None, out: &out }, Some(&levels), groups);
|
||||
out
|
||||
}
|
||||
|
||||
|
||||
@@ -879,7 +879,7 @@ fn corner_pin(e: &mut Enc, ctx: &EffectCtx, mut b: GBuf) -> Option<GBuf> {
|
||||
let o = b.to_px([0.0, 0.0]);
|
||||
let m = quad * Mat3::scale(vec2(1.0 / (w * b.scale), 1.0 / (h * b.scale))) * Mat3::translate(vec2(-o.0, -o.1));
|
||||
let empty = e.image(b.img.width, b.img.height);
|
||||
b.img = ops::warp(e, &empty, &b.img, &m, Sampling::Bilinear, BlendMode::Normal, 1.0, 0, None);
|
||||
b.img = ops::warp(e, &empty, &b.img, &m, ops::WarpOptions { sampling: Sampling::Bilinear, mode: BlendMode::Normal, opacity: 1.0, seed: 0 }, None);
|
||||
Some(b)
|
||||
}
|
||||
|
||||
@@ -989,7 +989,7 @@ pub(crate) fn transform(e: &mut Enc, ctx: &EffectCtx, mut b: GBuf) -> Option<GBu
|
||||
let Some(pr) = host.params_at(t) else { continue };
|
||||
let op = pr.f("opacity") as f32 / 100.0;
|
||||
let empty = e.image(w, h);
|
||||
let one = ops::warp(e, &empty, &b.img, &transform_matrix(&pr, &b), sampling, BlendMode::Normal, op, 0, None);
|
||||
let one = ops::warp(e, &empty, &b.img, &transform_matrix(&pr, &b), ops::WarpOptions { sampling, mode: BlendMode::Normal, opacity: op, seed: 0 }, None);
|
||||
let sum = e.image(w, h);
|
||||
e.pixels("fxs_add", &Params::default(), &acc, Some(&one), &sum, None);
|
||||
acc = sum;
|
||||
|
||||
+14
-2
@@ -181,7 +181,13 @@ fn sphere_blur(e: &mut Enc, img: &GpuImage, sigma: f64) -> GpuImage {
|
||||
let mut p = Params::default();
|
||||
p.u[0][0] = 256;
|
||||
let out = e.scratch(cur.width, cur.height);
|
||||
e.dispatch("fxv_box_h", &p, &cur, None, &out, Some(&buf), (cur.height.div_ceil(64), cur.width.div_ceil(256)));
|
||||
e.dispatch(
|
||||
"fxv_box_h",
|
||||
&p,
|
||||
crate::context::KernelImages { src: &cur, aux: None, out: &out },
|
||||
Some(&buf),
|
||||
(cur.height.div_ceil(64), cur.width.div_ceil(256)),
|
||||
);
|
||||
cur = out;
|
||||
}
|
||||
for &r in &radii {
|
||||
@@ -192,7 +198,13 @@ fn sphere_blur(e: &mut Enc, img: &GpuImage, sigma: f64) -> GpuImage {
|
||||
let mut p = Params::default();
|
||||
p.u[0] = [block, r as u32, 0, 0];
|
||||
let out = e.scratch(cur.width, cur.height);
|
||||
e.dispatch("fxv_box_v", &p, &cur, None, &out, None, (cur.width.div_ceil(64), cur.height.div_ceil(block)));
|
||||
e.dispatch(
|
||||
"fxv_box_v",
|
||||
&p,
|
||||
crate::context::KernelImages { src: &cur, aux: None, out: &out },
|
||||
None,
|
||||
(cur.width.div_ceil(64), cur.height.div_ceil(block)),
|
||||
);
|
||||
cur = out;
|
||||
}
|
||||
cur
|
||||
|
||||
+13
-27
@@ -32,37 +32,23 @@ pub fn half(e: &mut Enc, src: &GpuImage) -> GpuImage {
|
||||
out
|
||||
}
|
||||
|
||||
/// `raster::composite_warp` (and, with `accumulate = Some(weight)`, `accumulate_warp`): draw
|
||||
/// `src` transformed by `m` (src pixels → dst pixels) onto `dst`.
|
||||
pub fn warp(
|
||||
e: &mut Enc,
|
||||
dst: &GpuImage,
|
||||
src: &GpuImage,
|
||||
m: &Mat3,
|
||||
sampling: Sampling,
|
||||
mode: BlendMode,
|
||||
opacity: f32,
|
||||
seed: u32,
|
||||
accumulate: Option<f32>,
|
||||
) -> GpuImage {
|
||||
warp_q(e, dst, src, m, sampling, mode, opacity, seed, accumulate, None)
|
||||
/// Sampling and blending policy for a transformed GPU image.
|
||||
pub struct WarpOptions {
|
||||
pub sampling: Sampling,
|
||||
pub mode: BlendMode,
|
||||
pub opacity: f32,
|
||||
pub seed: u32,
|
||||
}
|
||||
|
||||
/// Draw `src` transformed by `m` onto `dst`, or accumulate it with a sample weight.
|
||||
pub fn warp(e: &mut Enc, dst: &GpuImage, src: &GpuImage, m: &Mat3, opts: WarpOptions, accumulate: Option<f32>) -> GpuImage {
|
||||
warp_q(e, dst, src, m, opts, accumulate, None)
|
||||
}
|
||||
|
||||
/// [`warp`], clamping and quantising the result to `levels` (8/16 bpc: the comp after a layer,
|
||||
/// in the same pass).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn warp_q(
|
||||
e: &mut Enc,
|
||||
dst: &GpuImage,
|
||||
src: &GpuImage,
|
||||
m: &Mat3,
|
||||
sampling: Sampling,
|
||||
mode: BlendMode,
|
||||
opacity: f32,
|
||||
seed: u32,
|
||||
accumulate: Option<f32>,
|
||||
levels: Option<f32>,
|
||||
) -> GpuImage {
|
||||
pub fn warp_q(e: &mut Enc, dst: &GpuImage, src: &GpuImage, m: &Mat3, opts: WarpOptions, accumulate: Option<f32>, levels: Option<f32>) -> GpuImage {
|
||||
let WarpOptions { sampling, mode, opacity, seed } = opts;
|
||||
if src.width == 0 || src.height == 0 || opacity <= 0.0 {
|
||||
return dst.clone();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
//! Set EFFECTCRAFT_REQUIRE_GPU=1 for acceptance: unavailable adapters/devices then fail
|
||||
//! instead of taking the explicitly logged headless-CI skip path.
|
||||
#![cfg(not(target_arch = "wasm32"))]
|
||||
#![allow(clippy::expect_used)] // Test helpers require successful setup to exercise rejection.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
@@ -466,12 +466,12 @@ fn place_src(
|
||||
) -> Option<GpuImage> {
|
||||
let pl = r.placement(ctx, layer, buf);
|
||||
if let [m] = pl.matrices.as_slice() {
|
||||
return Some(ops::warp_q(e, target, &src, m, pl.sampling, mode, opacity, pl.seed, None, levels));
|
||||
return Some(ops::warp_q(e, target, &src, m, ops::WarpOptions { sampling: pl.sampling, mode, opacity, seed: pl.seed }, None, levels));
|
||||
}
|
||||
let mut acc = e.zeros(target.width, target.height);
|
||||
let k = 1.0 / pl.matrices.len() as f32;
|
||||
for m in &pl.matrices {
|
||||
acc = ops::warp(e, &acc, &src, m, pl.sampling, BlendMode::Normal, 1.0, 0, Some(k));
|
||||
acc = ops::warp(e, &acc, &src, m, ops::WarpOptions { sampling: pl.sampling, mode: BlendMode::Normal, opacity: 1.0, seed: 0 }, Some(k));
|
||||
}
|
||||
Some(ops::blend_full(e, target, &acc, mode, opacity, pl.seed, levels))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user