mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 18:18:17 -06:00
Cryptomatte: the GPU path skips the ID colours unless the Colors display reads them, and the CPU path keys the layer in place instead of copying it (#473)
Output is bit-identical (all displays, CPU and GPU). At 2560x1440 with 3 ranks and 2000 IDs on 8 cores the CPU effect drops from ~87-135 ms to ~44-54 ms and the GPU path's aux reduction from 38 to 8 ms for Matte, Preview and Matte Only.
This commit is contained in:
@@ -413,10 +413,16 @@ fn crypto_colors(planes: &RankPlanes, i: usize) -> [f32; 3] {
|
||||
/// selection's coverage (A), computed in parallel with the rank planes resolved once. The GPU
|
||||
/// effect uploads this image and samples it per layer pixel.
|
||||
pub fn crypto_aux_image(a: &AuxChannels, layer: &str, sel: &[u32]) -> Image {
|
||||
crypto_aux_image_with(a, layer, sel, true)
|
||||
}
|
||||
|
||||
/// [`crypto_aux_image`]; with `colors` false the RGB stays 0 and only the matte (A) is computed,
|
||||
/// for the displays that never read the ID colours.
|
||||
pub fn crypto_aux_image_with(a: &AuxChannels, layer: &str, sel: &[u32], colors: bool) -> Image {
|
||||
let planes = a.crypto_rank_planes(layer);
|
||||
let mut img = Image::new(a.width, a.height);
|
||||
img.data.par_iter_mut().enumerate().for_each(|(i, px)| {
|
||||
let c = crypto_colors(&planes, i);
|
||||
let c = if colors { crypto_colors(&planes, i) } else { [0.0; 3] };
|
||||
*px = [c[0], c[1], c[2], crypto_matte(&planes, sel, i)];
|
||||
});
|
||||
img
|
||||
@@ -436,7 +442,6 @@ fn cryptomatte(ctx: &EffectCtx, mut b: Buf) -> Buf {
|
||||
let display = ctx.params.e("display");
|
||||
let matte_only = ctx.params.b("matteOnly");
|
||||
let planes = a.crypto_rank_planes(&layer);
|
||||
let src = b.img.clone();
|
||||
b.img.data.par_iter_mut().enumerate().for_each(|(i, px)| {
|
||||
let m = idx[i].map_or(0.0, |k| crypto_matte(&planes, &sel, k));
|
||||
if matte_only {
|
||||
@@ -450,11 +455,11 @@ fn cryptomatte(ctx: &EffectCtx, mut b: Buf) -> Buf {
|
||||
let c = c.map(|v| v * (1.0 - 0.5 * m) + 0.5 * m);
|
||||
[c[0], c[1], c[2], 1.0]
|
||||
}
|
||||
// Matte: the layer keyed by the selection.
|
||||
1 => src.data[i].map(|v| v * m),
|
||||
// Matte: the layer keyed by the selection (each pixel only reads itself, so no copy).
|
||||
1 => px.map(|v| v * m),
|
||||
// Preview: the layer with the selection tinted.
|
||||
_ => {
|
||||
let s = src.data[i];
|
||||
let s = *px;
|
||||
[s[0] * (1.0 - 0.5 * m) + 0.5 * m * s[3], s[1] * (1.0 - 0.5 * m) + 0.5 * m * s[3], s[2] * (1.0 - 0.5 * m), s[3]]
|
||||
}
|
||||
};
|
||||
@@ -835,6 +840,19 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Without the ID colours the matte (A) is bit-identical and the RGB is 0, on the edge values.
|
||||
#[test]
|
||||
fn crypto_aux_image_without_colors_keeps_the_matte() {
|
||||
let a = edge_aux(true);
|
||||
let sel = edge_selection();
|
||||
let full = crypto_aux_image(&a, "CryptoObject", &sel);
|
||||
let matte = crypto_aux_image_with(&a, "CryptoObject", &sel, false);
|
||||
for (f, m) in full.data.iter().zip(&matte.data) {
|
||||
assert_eq!(f[3].to_bits(), m[3].to_bits());
|
||||
assert_eq!(m[..3], [0.0; 3]);
|
||||
}
|
||||
}
|
||||
|
||||
/// The CPU effect on a 20×10 layer over the 6×2 edge aux: pixels inside the aux area match
|
||||
/// the lookup by name bit for bit in every display mode, pixels outside it (no aux index)
|
||||
/// get an empty matte and no ID colour.
|
||||
|
||||
@@ -110,7 +110,7 @@ pub use time_fx::{PixelMotion, TimewarpPlan, TwStep, pmb_plan, posterized_time,
|
||||
// effectcraft-gpu fx_tone.
|
||||
pub use ocio::color_stabilizer_maps;
|
||||
// GPU effects, part B (3D Channel, Immersive Video, colour management, simulation render passes).
|
||||
pub use channel3d::{CRYPTO_LAYERS, crypto_aux_image, id_color, selection_hashes};
|
||||
pub use channel3d::{CRYPTO_LAYERS, crypto_aux_image, crypto_aux_image_with, id_color, selection_hashes};
|
||||
pub use ocio::{ColorOp, Straight, Tf, color_program};
|
||||
pub use sim::{Acc, Blob, Bubble, Post, Shape, Sprite, SpritePlan, Tint, bubble_list, drizzle_drops, mercury_blobs, sprite_plan};
|
||||
pub use sim2::{
|
||||
|
||||
@@ -278,7 +278,10 @@ fn cryptomatte(e: &mut Enc, ctx: &EffectCtx, b: GBuf, a: &AuxChannels) -> Option
|
||||
let sel = effectcraft_effects::selection_hashes(pr.s("selection"), manifest);
|
||||
// Per aux pixel: coverage-weighted ID colours (RGB) and the selection's coverage (A), on all
|
||||
// cores with the rank planes resolved once.
|
||||
let img = effectcraft_effects::crypto_aux_image(a, &layer, &sel);
|
||||
// The ID colours are only read by the Colors display; Matte, Preview and Matte Only need the
|
||||
// matte alone.
|
||||
let colors = pr.e("display") == 0 && !pr.b("matteOnly");
|
||||
let img = effectcraft_effects::crypto_aux_image_with(a, &layer, &sel, colors);
|
||||
let tex = e.g.upload_image(&img)?;
|
||||
let plane = lookup(e, &b, a, &tex, [0.5, 0.5], [0.0; 4]);
|
||||
let mut p = op(7);
|
||||
|
||||
Reference in New Issue
Block a user