Skip to content

Commit 16a02bd

Browse files
Apply transforms
1 parent 371aa37 commit 16a02bd

22 files changed

Lines changed: 1592 additions & 170 deletions
Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,83 @@
11
// Copyright 2023 the Vello Authors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
33

4-
// Helpers for working with transforms.
4+
// Helpers for working with projective 2D transforms.
5+
//
6+
// Each transform stores the 9 elements of a 3x3 projective matrix extracted
7+
// from a Matrix4x4 with z=0:
8+
// X = x*M11 + y*M21 + M41
9+
// Y = x*M12 + y*M22 + M42
10+
// W = x*M14 + y*M24 + M44
11+
//
12+
// For affine matrices M14=M24=0, M44=1 so W is always 1 and the perspective
13+
// divide is a no-op.
514

615
struct Transform {
7-
matrx: vec4<f32>,
8-
translate: vec2<f32>,
16+
matrx: vec4<f32>, // [M11, M12, M21, M22]
17+
translate: vec2<f32>, // [M41, M42]
18+
perspective: vec3<f32>, // [M14, M24, M44]
919
}
1020

21+
// Matches TransformUtilities.ProjectiveTransform2D from ImageSharp:
22+
// Vector4.Transform(new Vector4(x, y, 0, 1), matrix) then divide by W.
1123
fn transform_apply(transform: Transform, p: vec2<f32>) -> vec2<f32> {
12-
return transform.matrx.xy * p.x + transform.matrx.zw * p.y + transform.translate;
24+
let x = fma(transform.matrx.x, p.x, fma(transform.matrx.z, p.y, transform.translate.x));
25+
let y = fma(transform.matrx.y, p.x, fma(transform.matrx.w, p.y, transform.translate.y));
26+
let w = fma(transform.perspective.x, p.x, fma(transform.perspective.y, p.y, transform.perspective.z));
27+
return vec2(x, y) / max(w, 0.0000001);
1328
}
1429

30+
fn transform_identity() -> Transform {
31+
return Transform(vec4(1.0, 0.0, 0.0, 1.0), vec2(0.0), vec3(0.0, 0.0, 1.0));
32+
}
33+
34+
// 3x3 projective inverse via cofactor expansion.
1535
fn transform_inverse(transform: Transform) -> Transform {
16-
let inv_det = 1.0 / (transform.matrx.x * transform.matrx.w - transform.matrx.y * transform.matrx.z);
17-
let inv_mat = inv_det * vec4(transform.matrx.w, -transform.matrx.y, -transform.matrx.z, transform.matrx.x);
18-
let inv_tr = mat2x2(inv_mat.xy, inv_mat.zw) * -transform.translate;
19-
return Transform(inv_mat, inv_tr);
36+
let a = transform.matrx.x; // M11
37+
let b = transform.matrx.y; // M12
38+
let c = transform.perspective.x; // M14
39+
let d = transform.matrx.z; // M21
40+
let e = transform.matrx.w; // M22
41+
let f = transform.perspective.y; // M24
42+
let g = transform.translate.x; // M41
43+
let h = transform.translate.y; // M42
44+
let i = transform.perspective.z; // M44
45+
46+
let det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
47+
let inv_det = 1.0 / det;
48+
49+
return Transform(
50+
inv_det * vec4(e * i - f * h, f * g - d * i, c * h - b * i, a * i - c * g),
51+
inv_det * vec2(b * f - c * e, c * d - a * f),
52+
inv_det * vec3(d * h - e * g, b * g - a * h, a * e - b * d)
53+
);
2054
}
2155

56+
// 3x3 projective multiplication.
2257
fn transform_mul(a: Transform, b: Transform) -> Transform {
58+
let a11 = a.matrx.x; let a12 = a.matrx.y; let a14 = a.perspective.x;
59+
let a21 = a.matrx.z; let a22 = a.matrx.w; let a24 = a.perspective.y;
60+
let a41 = a.translate.x; let a42 = a.translate.y; let a44 = a.perspective.z;
61+
62+
let b11 = b.matrx.x; let b12 = b.matrx.y; let b14 = b.perspective.x;
63+
let b21 = b.matrx.z; let b22 = b.matrx.w; let b24 = b.perspective.y;
64+
let b41 = b.translate.x; let b42 = b.translate.y; let b44 = b.perspective.z;
65+
2366
return Transform(
24-
a.matrx.xyxy * b.matrx.xxzz + a.matrx.zwzw * b.matrx.yyww,
25-
a.matrx.xy * b.translate.x + a.matrx.zw * b.translate.y + a.translate
67+
vec4(
68+
a11 * b11 + a21 * b12 + a41 * b14,
69+
a12 * b11 + a22 * b12 + a42 * b14,
70+
a11 * b21 + a21 * b22 + a41 * b24,
71+
a12 * b21 + a22 * b22 + a42 * b24
72+
),
73+
vec2(
74+
a11 * b41 + a21 * b42 + a41 * b44,
75+
a12 * b41 + a22 * b42 + a42 * b44
76+
),
77+
vec3(
78+
a14 * b11 + a24 * b12 + a44 * b14,
79+
a14 * b21 + a24 * b22 + a44 * b24,
80+
a14 * b41 + a24 * b42 + a44 * b44
81+
)
2682
);
2783
}

src/ImageSharp.Drawing.WebGPU/Shaders/WgslSource/draw_leaf.wgsl

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,20 @@ var<storage, read_write> clip_inp: array<ClipInp>;
3535
const WG_SIZE = 256u;
3636

3737
fn read_transform(transform_base: u32, ix: u32) -> Transform {
38-
let base = transform_base + ix * 6u;
39-
let c0 = bitcast<f32>(scene[base]);
40-
let c1 = bitcast<f32>(scene[base + 1u]);
41-
let c2 = bitcast<f32>(scene[base + 2u]);
42-
let c3 = bitcast<f32>(scene[base + 3u]);
43-
let c4 = bitcast<f32>(scene[base + 4u]);
44-
let c5 = bitcast<f32>(scene[base + 5u]);
45-
let matrx = vec4(c0, c1, c2, c3);
46-
let translate = vec2(c4, c5);
47-
return Transform(matrx, translate);
38+
let base = transform_base + ix * 9u;
39+
let mat = vec4(
40+
bitcast<f32>(scene[base]),
41+
bitcast<f32>(scene[base + 1u]),
42+
bitcast<f32>(scene[base + 2u]),
43+
bitcast<f32>(scene[base + 3u]));
44+
let translate = vec2(
45+
bitcast<f32>(scene[base + 4u]),
46+
bitcast<f32>(scene[base + 5u]));
47+
let perspective = vec3(
48+
bitcast<f32>(scene[base + 6u]),
49+
bitcast<f32>(scene[base + 7u]),
50+
bitcast<f32>(scene[base + 8u]));
51+
return Transform(mat, translate, perspective);
4852
}
4953

5054
var<workgroup> sh_scratch: array<DrawMonoid, WG_SIZE>;
@@ -210,7 +214,7 @@ fn main(
210214
kind = RAD_GRAD_KIND_FOCAL_ON_CIRCLE;
211215
let scale = 0.5 * abs(1.0 - focal_x);
212216
user_to_scaled = transform_mul(
213-
Transform(vec4(scale, 0.0, 0.0, scale), vec2(0.0)),
217+
Transform(vec4(scale, 0.0, 0.0, scale), vec2(0.0), vec3(0.0, 0.0, 1.0)),
214218
user_to_unit_line
215219
);
216220
} else {
@@ -219,7 +223,7 @@ fn main(
219223
let scale_x = radius * scale_ratio;
220224
let scale_y = sqrt(abs(a)) * scale_ratio;
221225
user_to_scaled = transform_mul(
222-
Transform(vec4(scale_x, 0.0, 0.0, scale_y), vec2(0.0)),
226+
Transform(vec4(scale_x, 0.0, 0.0, scale_y), vec2(0.0), vec3(0.0, 0.0, 1.0)),
223227
user_to_unit_line
224228
);
225229
}
@@ -265,7 +269,7 @@ fn main(
265269
case DRAWTAG_FILL_SWEEP_GRADIENT: {
266270
info[di] = draw_flags;
267271
let p0 = bitcast<vec2<f32>>(vec2(scene[dd + 1u], scene[dd + 2u]));
268-
let xform = transform_mul(transform, Transform(vec4(1.0, 0.0, 0.0, 1.0), p0));
272+
let xform = transform_mul(transform, Transform(vec4(1.0, 0.0, 0.0, 1.0), p0, vec3(0.0, 0.0, 1.0)));
269273
let inv = transform_inverse(xform);
270274
info[di + 1u] = bitcast<u32>(inv.matrx.x);
271275
info[di + 2u] = bitcast<u32>(inv.matrx.y);
@@ -328,6 +332,7 @@ fn two_point_to_unit_line(p0: vec2<f32>, p1: vec2<f32>) -> Transform {
328332
fn from_poly2(p0: vec2<f32>, p1: vec2<f32>) -> Transform {
329333
return Transform(
330334
vec4(p1.y - p0.y, p0.x - p1.x, p1.x - p0.x, p1.y - p0.y),
331-
vec2(p0.x, p0.y)
335+
vec2(p0.x, p0.y),
336+
vec3(0.0, 0.0, 1.0)
332337
);
333338
}

src/ImageSharp.Drawing.WebGPU/Shaders/WgslSource/flatten.wgsl

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -665,29 +665,35 @@ fn read_i16_point(ix: u32) -> vec2f {
665665
struct Transform {
666666
mat: vec4f,
667667
translate: vec2f,
668+
perspective: vec3f,
668669
}
669670

670671
fn transform_identity() -> Transform {
671-
return Transform(vec4(1., 0., 0., 1.), vec2(0.));
672+
return Transform(vec4(1., 0., 0., 1.), vec2(0.), vec3(0., 0., 1.));
672673
}
673674

674675
fn read_transform(transform_base: u32, ix: u32) -> Transform {
675-
let base = transform_base + ix * 6u;
676-
let c0 = bitcast<f32>(scene[base]);
677-
let c1 = bitcast<f32>(scene[base + 1u]);
678-
let c2 = bitcast<f32>(scene[base + 2u]);
679-
let c3 = bitcast<f32>(scene[base + 3u]);
680-
let c4 = bitcast<f32>(scene[base + 4u]);
681-
let c5 = bitcast<f32>(scene[base + 5u]);
682-
let mat = vec4(c0, c1, c2, c3);
683-
let translate = vec2(c4, c5);
684-
return Transform(mat, translate);
676+
let base = transform_base + ix * 9u;
677+
let mat = vec4(
678+
bitcast<f32>(scene[base]),
679+
bitcast<f32>(scene[base + 1u]),
680+
bitcast<f32>(scene[base + 2u]),
681+
bitcast<f32>(scene[base + 3u]));
682+
let translate = vec2(
683+
bitcast<f32>(scene[base + 4u]),
684+
bitcast<f32>(scene[base + 5u]));
685+
let perspective = vec3(
686+
bitcast<f32>(scene[base + 6u]),
687+
bitcast<f32>(scene[base + 7u]),
688+
bitcast<f32>(scene[base + 8u]));
689+
return Transform(mat, translate, perspective);
685690
}
686691

687692
fn transform_apply(transform: Transform, p: vec2f) -> vec2f {
688693
let px = fma(transform.mat.x, p.x, fma(transform.mat.z, p.y, transform.translate.x));
689694
let py = fma(transform.mat.y, p.x, fma(transform.mat.w, p.y, transform.translate.y));
690-
return vec2(px, py);
695+
let w = fma(transform.perspective.x, p.x, fma(transform.perspective.y, p.y, transform.perspective.z));
696+
return vec2(px, py) / max(w, 0.0000001);
691697
}
692698

693699
fn round_down(x: f32) -> i32 {

src/ImageSharp.Drawing.WebGPU/WebGPUSceneDispatch.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,8 @@ public static unsafe bool TryDispatchSchedulingStages(
530530

531531
if (encodedScene.ClipCount > 0)
532532
{
533-
if (!TryDispatchClipReduce(
533+
if (workgroupCounts.ClipReduceX > 0 &&
534+
!TryDispatchClipReduce(
534535
recording,
535536
flushContext,
536537
stagedScene.Resources,
@@ -2181,6 +2182,12 @@ internal static unsafe bool TryDispatchComputePass(
21812182
uint groupCountZ,
21822183
out string? error)
21832184
{
2185+
if (groupCountX == 0 || groupCountY == 0 || groupCountZ == 0)
2186+
{
2187+
error = null;
2188+
return true;
2189+
}
2190+
21842191
BindGroupDescriptor descriptor = new()
21852192
{
21862193
Layout = bindGroupLayout,
@@ -2305,6 +2312,12 @@ private static unsafe bool TryExecuteComputeRecording(
23052312
return false;
23062313
}
23072314

2315+
if (!command.IsIndirect &&
2316+
(command.GroupCountX == 0 || command.GroupCountY == 0 || command.GroupCountZ == 0))
2317+
{
2318+
continue;
2319+
}
2320+
23082321
if (!flushContext.BeginComputePass())
23092322
{
23102323
error = $"Failed to begin the staged-scene compute pass for '{shaderName}'.";

0 commit comments

Comments
 (0)