mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-02 22:42:24 -06:00
G2: improve editing reliability and Windows rendering fidelity
This commit is contained in:
@@ -2,10 +2,17 @@
|
||||
// the CPU effect in effectcraft-effects operation for operation. Every name here is prefixed
|
||||
// `fxk_` (the family files share one module).
|
||||
|
||||
// Correct the approximate quotient before bitwise/threshold operations.
|
||||
fn fxk_div3(a: vec3<f32>, b: f32) -> vec3<f32> {
|
||||
let q = a / b;
|
||||
let r = fma(-q, vec3<f32>(b), a);
|
||||
return fma(r, vec3<f32>(1.0 / b), q);
|
||||
}
|
||||
|
||||
// util::unpremul: (straight colour, alpha); colour 0 when alpha ≤ 1e-6.
|
||||
fn fxk_straight(px: vec4<f32>) -> vec3<f32> {
|
||||
if (px.w > 1e-6) {
|
||||
return px.xyz / px.w;
|
||||
return fxk_div3(px.xyz, px.w);
|
||||
}
|
||||
return vec3<f32>(0.0);
|
||||
}
|
||||
@@ -457,7 +464,7 @@ fn fxk_point(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
textureStore(out, p, px);
|
||||
return;
|
||||
}
|
||||
let s = px.xyz / px.w;
|
||||
let s = fxk_div3(px.xyz, px.w);
|
||||
var r = vec3<f32>(0.0);
|
||||
for (var i = 0u; i < 3u; i++) {
|
||||
let v = s[i];
|
||||
|
||||
@@ -15,11 +15,14 @@ fn fxn_pixel(gid: vec3<u32>) -> vec2<i32> {
|
||||
}
|
||||
|
||||
// a / b rounded like the CPU's IEEE division (GPU division may be approximate): one
|
||||
// residual correction with fma. Used where a threshold follows the quotient.
|
||||
// residual correction with fma, followed by a second refinement for half-step cases.
|
||||
// Used where a strict threshold follows the quotient.
|
||||
fn fxn_div4(a: vec4<f32>, b: vec4<f32>) -> vec4<f32> {
|
||||
let q = a / b;
|
||||
let r = fma(-q, b, a);
|
||||
return fma(r, 1.0 / b, q);
|
||||
let corrected = fma(r, 1.0 / b, q);
|
||||
let residual = fma(-corrected, b, a);
|
||||
return fma(residual, 1.0 / b, corrected);
|
||||
}
|
||||
|
||||
// util::unpremul: (straight colour, alpha).
|
||||
|
||||
@@ -453,10 +453,15 @@ fn fxs_warp(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
u = bu + (u - bu) * persp;
|
||||
v = bv + (v - bv) * persp;
|
||||
}
|
||||
if (u < -1e-9 || u > 1.0 + 1e-9 || v < -1e-9 || v > 1.0 + 1e-9) {
|
||||
// The CPU's 1e-9 edge allowance disappears in f32 at 1.0. Allow eight
|
||||
// f32 ULPs for the inverse solve so exact quad-edge pixels remain covered.
|
||||
let edge = 0.00000095367431640625;
|
||||
if (u < -edge || u > 1.0 + edge || v < -edge || v > 1.0 + edge) {
|
||||
textureStore(out, p, vec4<f32>(0.0));
|
||||
return;
|
||||
}
|
||||
u = clamp(u, 0.0, 1.0);
|
||||
v = clamp(v, 0.0, 1.0);
|
||||
let rc = P.f[5];
|
||||
let lx = rc.x + u * rc.z;
|
||||
let ly = rc.y + v * rc.w;
|
||||
|
||||
@@ -33,7 +33,7 @@ fn warp_blend(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
if ((flags & 2u) != 0u) {
|
||||
d = vec4<f32>(0.0);
|
||||
}
|
||||
store_q(vec2<i32>(x, y), d, P.f[3].z);
|
||||
store_q(vec2<i32>(x, y), d, P.f[3].z, P.f[3].w);
|
||||
return;
|
||||
}
|
||||
let r0 = P.f[0];
|
||||
@@ -52,7 +52,7 @@ fn warp_blend(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
if (stencil && (flags & 1u) == 0u) {
|
||||
d = blend_pixel(mode, d, vec4<f32>(0.0), 0.5);
|
||||
}
|
||||
store_q(vec2<i32>(x, y), d, P.f[3].z);
|
||||
store_q(vec2<i32>(x, y), d, P.f[3].z, P.f[3].w);
|
||||
return;
|
||||
}
|
||||
var s = sample(src, sampling, px, py);
|
||||
@@ -60,11 +60,11 @@ fn warp_blend(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
if (s.w > 0.0) {
|
||||
d = d + s * P.f[3].y;
|
||||
}
|
||||
store_q(vec2<i32>(x, y), d, P.f[3].z);
|
||||
store_q(vec2<i32>(x, y), d, P.f[3].z, P.f[3].w);
|
||||
return;
|
||||
}
|
||||
if (s.w <= 0.0 && !stencil) {
|
||||
store_q(vec2<i32>(x, y), d, P.f[3].z);
|
||||
store_q(vec2<i32>(x, y), d, P.f[3].z, P.f[3].w);
|
||||
return;
|
||||
}
|
||||
let op = P.f[3].x;
|
||||
@@ -75,7 +75,7 @@ fn warp_blend(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
if (mode == 1u || mode == 2u) {
|
||||
n = hash_noise(u32(x), u32(y), seed);
|
||||
}
|
||||
store_q(vec2<i32>(x, y), blend_pixel(mode, d, s, n), P.f[3].z);
|
||||
store_q(vec2<i32>(x, y), blend_pixel(mode, d, s, n), P.f[3].z, P.f[3].w);
|
||||
}
|
||||
|
||||
// Blend `src` (same size) onto `aux` (Image::blend_from).
|
||||
@@ -91,7 +91,7 @@ fn blend_full(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let d = textureLoad(aux, p, 0);
|
||||
var s = textureLoad(src, p, 0);
|
||||
if (s.w <= 0.0 && !is_stencil(mode)) {
|
||||
store_q(p, d, P.f[0].y);
|
||||
store_q(p, d, P.f[0].y, P.f[0].z);
|
||||
return;
|
||||
}
|
||||
s = s * P.f[0].x;
|
||||
@@ -99,7 +99,7 @@ fn blend_full(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
if (mode == 1u || mode == 2u) {
|
||||
n = hash_noise(gid.x, gid.y, P.u[0].z);
|
||||
}
|
||||
store_q(p, blend_pixel(mode, d, s, n), P.f[0].y);
|
||||
store_q(p, blend_pixel(mode, d, s, n), P.f[0].y, P.f[0].z);
|
||||
}
|
||||
|
||||
// Track matte: `src` × matte factor of `aux`. u[0].x = 0 alpha, 1 alpha inverted, 2 luma,
|
||||
@@ -179,24 +179,24 @@ fn quantize(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
if (p.x >= dims.x || p.y >= dims.y) {
|
||||
return;
|
||||
}
|
||||
textureStore(out, p, quantize_px(textureLoad(src, p, 0), P.f[0].x));
|
||||
textureStore(out, p, quantize_px(textureLoad(src, p, 0), P.f[0].x, P.f[0].y));
|
||||
}
|
||||
|
||||
fn quantize_px(s: vec4<f32>, levels: f32) -> vec4<f32> {
|
||||
let inv = 1.0 / levels;
|
||||
let a = round_away(clamp(s.w, 0.0, 1.0) * levels) * inv;
|
||||
fn quantize_px(s: vec4<f32>, levels: f32, inv: f32) -> vec4<f32> {
|
||||
// Use the CPU-rounded reciprocal to avoid backend-dependent division.
|
||||
let a = floor(clamp(s.w, 0.0, 1.0) * levels + 0.5) * inv;
|
||||
var o = vec4<f32>(0.0, 0.0, 0.0, a);
|
||||
o.x = round_away(clamp(s.x, 0.0, a) * levels) * inv;
|
||||
o.y = round_away(clamp(s.y, 0.0, a) * levels) * inv;
|
||||
o.z = round_away(clamp(s.z, 0.0, a) * levels) * inv;
|
||||
o.x = floor(clamp(s.x, 0.0, a) * levels + 0.5) * inv;
|
||||
o.y = floor(clamp(s.y, 0.0, a) * levels + 0.5) * inv;
|
||||
o.z = floor(clamp(s.z, 0.0, a) * levels + 0.5) * inv;
|
||||
return o;
|
||||
}
|
||||
|
||||
// Store a compositing result, clamped and quantised to `levels` when > 0 (8/16 bpc: the comp
|
||||
// after a layer, fused into the layer's composite).
|
||||
fn store_q(p: vec2<i32>, v: vec4<f32>, levels: f32) {
|
||||
fn store_q(p: vec2<i32>, v: vec4<f32>, levels: f32, inv: f32) {
|
||||
if (levels > 0.0) {
|
||||
textureStore(out, p, quantize_px(v, levels));
|
||||
textureStore(out, p, quantize_px(v, levels, inv));
|
||||
} else {
|
||||
textureStore(out, p, v);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user