Xenova HF Staff commited on
Commit
01fb71c
·
verified ·
1 Parent(s): c0391df

sync 91d990483a17

Browse files
README.md CHANGED
@@ -18,19 +18,19 @@ See the [ONNX `DeformConv` spec](https://onnx.ai/onnx/operators/onnx__DeformConv
18
 
19
  ## Inputs
20
 
21
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
- | `X` | `x` | `T` | `4` | — | Input data tensor of shape `(N, C, H, W)` for 2D or `(N, C, D1, ..., Dn)` in general. | required |
24
- | `W` | `w` | `T` | `4` | — | Convolution weight tensor of shape `(oC, C/group, kH, kW)`. | required |
25
- | `offset` | `offset` | `T` | `4` | — | Per-output-position sampling offsets of shape `(N, offset_group * kH * kW * 2, oH, oW)` for 2D data. | required |
26
- | `B` | `bias` | `T` | `1` | — | Optional 1D bias of length oC added to the convolution output. | optional |
27
- | `mask` | `mask` | `T` | `4` | — | Optional modulation mask of shape `(N, offset_group * kH * kW, oH, oW)` scaling each sampled kernel point; defaults to ones. | optional |
28
 
29
  ## Outputs
30
 
31
- | Name | Bind key | Logical dtype | Rank | Shape | Description | Presence |
32
  | --- | --- | --- | --- | --- | --- | --- |
33
- | `Y` | `y` | `T` | `4` | derived; see description | Output tensor of shape `(N, oC, oH, oW)` containing the deformable convolution result. | required |
34
 
35
  ## Attributes
36
 
@@ -38,10 +38,10 @@ Attributes and default values (overridable per request):
38
 
39
  | Attribute | Default | Description |
40
  | --- | --- | --- |
41
- | `group` | `1` | Number of groups the input channels C and output channels oC are divided into; both must be divisible by group. |
42
- | `offset_group` | `1` | Number of offset groups; input channels `C` must be divisible by `offset_group`. |
43
  | `dilations` | — | Dilation factors for the spatial axes in height-width order; defaults to `[1, 1]`. |
 
44
  | `kernel_shape` | — | Kernel dimensions in height-width order. When omitted, they are inferred from the spatial dimensions of `W`. |
 
45
  | `pads` | — | Padding at the beginning and end of each spatial axis in `[top, left, bottom, right]` order; defaults to zeros. |
46
  | `strides` | — | Stride factors for the spatial axes in height-width order; defaults to `[1, 1]`. |
47
 
@@ -53,7 +53,7 @@ Attributes and default values (overridable per request):
53
 
54
  ## Files
55
 
56
- - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, provenance)
57
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
58
  - [`test.json`](build/webgpu/test.json) — correctness cases
59
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
@@ -62,10 +62,14 @@ Attributes and default values (overridable per request):
62
 
63
  ## Use with `@huggingface/kernels`
64
 
65
- The loader derives every required output's shape and logical dtype from the manifest contract and this call.
66
- It then allocates the result tensors automatically.
 
 
 
67
 
68
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
 
69
 
70
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
71
 
 
18
 
19
  ## Inputs
20
 
21
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
22
  | --- | --- | --- | --- | --- | --- | --- |
23
+ | `x` | `X` | `T` | `4` | — | Input data tensor of shape `(N, C, H, W)` for 2D or `(N, C, D1, ..., Dn)` in general. | required |
24
+ | `w` | `W` | `T` | `4` | — | Convolution weight tensor of shape `(oC, C/group, kH, kW)`. | required |
25
+ | `offset` | | `T` | `4` | — | Per-output-position sampling offsets of shape `(N, offset_group * kH * kW * 2, oH, oW)` for 2D data. | required |
26
+ | `bias` | `B` | `T` | `1` | — | Optional 1D bias of length oC added to the convolution output. | optional |
27
+ | `mask` | | `T` | `4` | — | Optional modulation mask of shape `(N, offset_group * kH * kW, oH, oW)` scaling each sampled kernel point; defaults to ones. | optional |
28
 
29
  ## Outputs
30
 
31
+ | Name | Upstream name | Logical dtype | Rank | Shape | Description | Presence |
32
  | --- | --- | --- | --- | --- | --- | --- |
33
+ | `y` | `Y` | `T` | `4` | derived | Output tensor of shape `(N, oC, oH, oW)` containing the deformable convolution result. | required |
34
 
35
  ## Attributes
36
 
 
38
 
39
  | Attribute | Default | Description |
40
  | --- | --- | --- |
 
 
41
  | `dilations` | — | Dilation factors for the spatial axes in height-width order; defaults to `[1, 1]`. |
42
+ | `group` | `1` | Number of groups the input channels C and output channels oC are divided into; both must be divisible by group. |
43
  | `kernel_shape` | — | Kernel dimensions in height-width order. When omitted, they are inferred from the spatial dimensions of `W`. |
44
+ | `offset_group` | `1` | Number of offset groups; input channels `C` must be divisible by `offset_group`. |
45
  | `pads` | — | Padding at the beginning and end of each spatial axis in `[top, left, bottom, right]` order; defaults to zeros. |
46
  | `strides` | — | Stride factors for the spatial axes in height-width order; defaults to `[1, 1]`. |
47
 
 
53
 
54
  ## Files
55
 
56
+ - [`metadata.json`](build/webgpu/metadata.json) — kernel metadata (id, digests, per-variant templates, provenance)
57
  - [`manifest.json`](build/webgpu/manifest.json) — the op contract (source of truth)
58
  - [`test.json`](build/webgpu/test.json) — correctness cases
59
  - [`bench.json`](build/webgpu/bench.json) — benchmark + tuning cases
 
62
 
63
  ## Use with `@huggingface/kernels`
64
 
65
+ ```sh
66
+ npm install --save-exact @huggingface/kernels@0.0.1-preview.2
67
+ ```
68
+
69
+ Required output shapes and logical data types are inferred from the supplied inputs and attributes; result tensors are allocated automatically.
70
 
71
  The `version: 1` option selects the published kernel contract; it is independent of any operator opset, contrib `since_version`, or model version.
72
+ It follows the `v1` branch as fixes land. To pin exact artifact bytes, pass a 40-character commit `revision` instead of `version`.
73
 
74
  Replace each `*Data` placeholder with a typed array containing the corresponding input data.
75
 
build/webgpu/bench.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.DeformConv",
3
  "cases": [
4
  {
5
  "name": "nchw_1x8x32x32_k3",
@@ -75,7 +74,7 @@
75
  "name": "deform-conv-f32-unmodulated-b1c64m64-128x128-k3-g4-pathology",
76
  "preset": "stress",
77
  "provenance": {
78
- "source": "authored for variant coverage",
79
  "notes": "Unmodulated grouped DeformConv exercises the nchw2d route; the paired bias-and-mask case selects the blocked kernel and isolates the optional-input route boundary."
80
  },
81
  "attrs": { "group": 4, "offset_group": 4, "pads": [1, 1, 1, 1] },
 
1
  {
 
2
  "cases": [
3
  {
4
  "name": "nchw_1x8x32x32_k3",
 
74
  "name": "deform-conv-f32-unmodulated-b1c64m64-128x128-k3-g4-pathology",
75
  "preset": "stress",
76
  "provenance": {
77
+ "source": "synthetic benchmark",
78
  "notes": "Unmodulated grouped DeformConv exercises the nchw2d route; the paired bias-and-mask case selects the blocked kernel and isolates the optional-input route boundary."
79
  },
80
  "attrs": { "group": 4, "offset_group": 4, "pads": [1, 1, 1, 1] },
build/webgpu/deform-conv2d-aligned-groups.wgsl.jinja CHANGED
@@ -4,26 +4,26 @@
4
  // output group shares the same tap coordinates and modulation mask. Hoist the
5
  // offset fetch, floor, bounds and interpolation coefficients out of the channel
6
  // loop instead of repeating them C/group times for every output channel.
7
- const COUNT: u32 = {{ source.count }}u;
8
- const IN_C: u32 = {{ source.inC }}u;
9
- const IN_CPG: u32 = {{ source.inCPerGroup }}u;
10
- const IN_H: i32 = {{ source.inH }};
11
- const IN_W: i32 = {{ source.inW }};
12
- const IN_H_U: u32 = {{ source.inH }}u;
13
- const IN_W_U: u32 = {{ source.inW }}u;
14
- const OUT_C: u32 = {{ source.outC }}u;
15
- const OUT_CPG: u32 = {{ source.outCPerGroup }}u;
16
- {% if source.outChannelBlock != 1 %}const OC_BLOCK: u32 = {{ source.outChannelBlock }}u;
17
  {% endif %}
18
- const OUT_H: u32 = {{ source.outH }}u;
19
- const OUT_W: u32 = {{ source.outW }}u;
20
- const KAREA: u32 = {{ source.kernelH * source.kernelW }}u;
21
- {% if source.outChannelBlock != 1 %}const W_OC_STRIDE: u32 = IN_CPG * KAREA;
22
  {% endif %}
23
- const STRIDE_H: u32 = {{ source.strideH }}u;
24
- const STRIDE_W: u32 = {{ source.strideW }}u;
25
- const PAD_H: i32 = {{ source.padH }};
26
- const PAD_W: i32 = {{ source.padW }};
27
  const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
28
 
29
  fn pixel(n: u32, c: u32, h: i32, wc: i32) -> f32 {
@@ -32,10 +32,10 @@ fn pixel(n: u32, c: u32, h: i32, wc: i32) -> f32 {
32
  }
33
 
34
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
35
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
36
- let index = gid.x + gid.y * nwg.x * WG;
37
  if (index >= COUNT) { return; }
38
- {% if source.outChannelBlock != 1 %}
39
  // Output channels in the same convolution group use identical deformable
40
  // coordinates, masks, and sampled input values. Accumulate a compile-time
41
  // output-channel block together so those values are shared across the block.
@@ -45,11 +45,11 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
45
  t /= OUT_H;
46
  let ocBlock = t % (OUT_CPG / OC_BLOCK);
47
  t /= OUT_CPG / OC_BLOCK;
48
- let group = t % {{ source.groups }}u;
49
- let n = t / {{ source.groups }}u;
50
  let oc = group * OUT_CPG + ocBlock * OC_BLOCK;
51
- {% for v in range(source.outChannelVecs) %}
52
- {% if source.hasBias %}
53
  var acc{{ v }} = vec4<f32>(
54
  bias[oc + {{ v * 4 }}u], bias[oc + {{ v * 4 + 1 }}u],
55
  bias[oc + {{ v * 4 + 2 }}u], bias[oc + {{ v * 4 + 3 }}u]);
@@ -65,7 +65,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
65
  let oc = t % OUT_C;
66
  let n = t / OUT_C;
67
  let group = oc / OUT_CPG;
68
- {% if source.hasBias %}
69
  var acc = bias[oc];
70
  {% else %}
71
  var acc = 0.0;
@@ -75,24 +75,24 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
75
  let spatial = oh * OUT_W + ow;
76
  let offsetPlane = OUT_H * OUT_W;
77
 
78
- {% for kh in range(source.kernelH) %}
79
- {% for kw in range(source.kernelW) %}
80
  {
81
- const TAP: u32 = {{ kh * source.kernelW + kw }}u;
82
  let offsetChannel = group * KAREA * 2u + TAP * 2u;
83
- let offsetBase = n * ({{ source.groups }}u * KAREA * 2u) * offsetPlane;
84
  let dy = offset[offsetBase + offsetChannel * offsetPlane + spatial];
85
  let dx = offset[offsetBase + (offsetChannel + 1u) * offsetPlane + spatial];
86
- let sy = f32(i32(oh * STRIDE_H) + {{ kh * source.dilationH }} - PAD_H) + dy;
87
- let sx = f32(i32(ow * STRIDE_W) + {{ kw * source.dilationW }} - PAD_W) + dx;
88
  if (sy > -1.0 && sy < f32(IN_H) && sx > -1.0 && sx < f32(IN_W)) {
89
  let h0 = i32(floor(sy)); let h1 = h0 + 1;
90
  let w0 = i32(floor(sx)); let w1 = w0 + 1;
91
  let lh = sy - f32(h0); let lw = sx - f32(w0);
92
  let hh = 1.0 - lh; let hw = 1.0 - lw;
93
- {% if source.hasMask %}
94
  let maskChannel = group * KAREA + TAP;
95
- let maskBase = n * ({{ source.groups }}u * KAREA) * offsetPlane;
96
  let mv = mask[maskBase + maskChannel * offsetPlane + spatial];
97
  {% else %}
98
  let mv = 1.0;
@@ -104,8 +104,8 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
104
  + lh * hw * pixel(n, ic, h1, w0)
105
  + lh * lw * pixel(n, ic, h1, w1);
106
  let wi = ((oc * IN_CPG + localIc) * KAREA) + TAP;
107
- {% if source.outChannelBlock != 1 %}
108
- {% for v in range(source.outChannelVecs) %}
109
  let weights{{ v }} = vec4<f32>(
110
  w[wi + {{ v * 4 }}u * W_OC_STRIDE], w[wi + {{ v * 4 + 1 }}u * W_OC_STRIDE],
111
  w[wi + {{ v * 4 + 2 }}u * W_OC_STRIDE], w[wi + {{ v * 4 + 3 }}u * W_OC_STRIDE]);
@@ -119,10 +119,10 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups)
119
  }
120
  {% endfor %}
121
  {% endfor %}
122
- {% if source.outChannelBlock != 1 %}
123
  let y0 = ((n * OUT_C + oc) * OUT_H + oh) * OUT_W + ow;
124
  let yPlane = OUT_H * OUT_W;
125
- {% for v in range(source.outChannelVecs) %}
126
  y[y0 + {{ v * 4 }}u * yPlane] = acc{{ v }}.x;
127
  y[y0 + {{ v * 4 + 1 }}u * yPlane] = acc{{ v }}.y;
128
  y[y0 + {{ v * 4 + 2 }}u * yPlane] = acc{{ v }}.z;
 
4
  // output group shares the same tap coordinates and modulation mask. Hoist the
5
  // offset fetch, floor, bounds and interpolation coefficients out of the channel
6
  // loop instead of repeating them C/group times for every output channel.
7
+ const COUNT: u32 = {{ count }}u;
8
+ const IN_C: u32 = {{ inC }}u;
9
+ const IN_CPG: u32 = {{ inCPerGroup }}u;
10
+ const IN_H: i32 = {{ inH }};
11
+ const IN_W: i32 = {{ inW }};
12
+ const IN_H_U: u32 = {{ inH }}u;
13
+ const IN_W_U: u32 = {{ inW }}u;
14
+ const OUT_C: u32 = {{ outC }}u;
15
+ const OUT_CPG: u32 = {{ outCPerGroup }}u;
16
+ {% if outChannelBlock != 1 %}const OC_BLOCK: u32 = {{ outChannelBlock }}u;
17
  {% endif %}
18
+ const OUT_H: u32 = {{ outH }}u;
19
+ const OUT_W: u32 = {{ outW }}u;
20
+ const KAREA: u32 = {{ kernelHSpec * kernelWSpec }}u;
21
+ {% if outChannelBlock != 1 %}const W_OC_STRIDE: u32 = IN_CPG * KAREA;
22
  {% endif %}
23
+ const STRIDE_H: u32 = {{ strideHSpec }}u;
24
+ const STRIDE_W: u32 = {{ strideWSpec }}u;
25
+ const PAD_H: i32 = {{ padH }};
26
+ const PAD_W: i32 = {{ padW }};
27
  const WG: u32 = {{ tunables.WORKGROUP_SIZE }}u;
28
 
29
  fn pixel(n: u32, c: u32, h: i32, wc: i32) -> f32 {
 
32
  }
33
 
34
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
35
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
36
+ let index = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * WG;
37
  if (index >= COUNT) { return; }
38
+ {% if outChannelBlock != 1 %}
39
  // Output channels in the same convolution group use identical deformable
40
  // coordinates, masks, and sampled input values. Accumulate a compile-time
41
  // output-channel block together so those values are shared across the block.
 
45
  t /= OUT_H;
46
  let ocBlock = t % (OUT_CPG / OC_BLOCK);
47
  t /= OUT_CPG / OC_BLOCK;
48
+ let group = t % {{ groups }}u;
49
+ let n = t / {{ groups }}u;
50
  let oc = group * OUT_CPG + ocBlock * OC_BLOCK;
51
+ {% for v in range(outChannelVecs) %}
52
+ {% if hasBias %}
53
  var acc{{ v }} = vec4<f32>(
54
  bias[oc + {{ v * 4 }}u], bias[oc + {{ v * 4 + 1 }}u],
55
  bias[oc + {{ v * 4 + 2 }}u], bias[oc + {{ v * 4 + 3 }}u]);
 
65
  let oc = t % OUT_C;
66
  let n = t / OUT_C;
67
  let group = oc / OUT_CPG;
68
+ {% if hasBias %}
69
  var acc = bias[oc];
70
  {% else %}
71
  var acc = 0.0;
 
75
  let spatial = oh * OUT_W + ow;
76
  let offsetPlane = OUT_H * OUT_W;
77
 
78
+ {% for kh in range(kernelHSpec) %}
79
+ {% for kw in range(kernelWSpec) %}
80
  {
81
+ const TAP: u32 = {{ kh * kernelWSpec + kw }}u;
82
  let offsetChannel = group * KAREA * 2u + TAP * 2u;
83
+ let offsetBase = n * ({{ groups }}u * KAREA * 2u) * offsetPlane;
84
  let dy = offset[offsetBase + offsetChannel * offsetPlane + spatial];
85
  let dx = offset[offsetBase + (offsetChannel + 1u) * offsetPlane + spatial];
86
+ let sy = f32(i32(oh * STRIDE_H) + {{ kh * dilationHSpec }} - PAD_H) + dy;
87
+ let sx = f32(i32(ow * STRIDE_W) + {{ kw * dilationWSpec }} - PAD_W) + dx;
88
  if (sy > -1.0 && sy < f32(IN_H) && sx > -1.0 && sx < f32(IN_W)) {
89
  let h0 = i32(floor(sy)); let h1 = h0 + 1;
90
  let w0 = i32(floor(sx)); let w1 = w0 + 1;
91
  let lh = sy - f32(h0); let lw = sx - f32(w0);
92
  let hh = 1.0 - lh; let hw = 1.0 - lw;
93
+ {% if hasMask %}
94
  let maskChannel = group * KAREA + TAP;
95
+ let maskBase = n * ({{ groups }}u * KAREA) * offsetPlane;
96
  let mv = mask[maskBase + maskChannel * offsetPlane + spatial];
97
  {% else %}
98
  let mv = 1.0;
 
104
  + lh * hw * pixel(n, ic, h1, w0)
105
  + lh * lw * pixel(n, ic, h1, w1);
106
  let wi = ((oc * IN_CPG + localIc) * KAREA) + TAP;
107
+ {% if outChannelBlock != 1 %}
108
+ {% for v in range(outChannelVecs) %}
109
  let weights{{ v }} = vec4<f32>(
110
  w[wi + {{ v * 4 }}u * W_OC_STRIDE], w[wi + {{ v * 4 + 1 }}u * W_OC_STRIDE],
111
  w[wi + {{ v * 4 + 2 }}u * W_OC_STRIDE], w[wi + {{ v * 4 + 3 }}u * W_OC_STRIDE]);
 
119
  }
120
  {% endfor %}
121
  {% endfor %}
122
+ {% if outChannelBlock != 1 %}
123
  let y0 = ((n * OUT_C + oc) * OUT_H + oh) * OUT_W + ow;
124
  let yPlane = OUT_H * OUT_W;
125
+ {% for v in range(outChannelVecs) %}
126
  y[y0 + {{ v * 4 }}u * yPlane] = acc{{ v }}.x;
127
  y[y0 + {{ v * 4 + 1 }}u * yPlane] = acc{{ v }}.y;
128
  y[y0 + {{ v * 4 + 2 }}u * yPlane] = acc{{ v }}.z;
build/webgpu/deform-conv2d-nchw.wgsl.jinja CHANGED
@@ -1,30 +1,30 @@
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
- // 2D-folded flat index: gid.y carries the high bits past the
4
- // maxComputeWorkgroupsPerDimension dispatch limit (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
- // 2D-folded flat index: gid.y carries the high bits past the
7
- // maxComputeWorkgroupsPerDimension limit.
8
  {% elif note == "device-axis" %}
9
- // The flat dispatch is folded across x/y at the device's per-axis workgroup
10
- // limit; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
- // 2D-folded flat vec4 index: gid.y carries the high bits past the
13
- // maxComputeWorkgroupsPerDimension limit (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
- // maxComputeWorkgroupsPerDimension limit.
17
  {% elif note == "dispatch" %}
18
- // 2D-folded flat index: gid.y carries the high bits past the
19
- // maxComputeWorkgroupsPerDimension dispatch limit.
20
  {% endif %}
21
  {% if bound == "" %}
22
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
- let {{ name }} = gid.x + gid.y * nwg.x * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
@@ -69,7 +69,7 @@ fn mask_at(n: u32, channel: u32, oh: u32, ow: u32) -> f32 {
69
 
70
  {% endif %}
71
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
72
- fn main(@builtin(global_invocation_id) gid: vec3<u32>, @builtin(num_workgroups) nwg: vec3<u32>) {
73
  {{ flat_index_2d("index", guardInline=true) }}
74
 
75
  let ow = index % params.outW;
 
1
  {% macro flat_index_2d(name="i", bound="params.count", guardInline=false, note="dispatch-limit") %}
2
  {% if note == "dispatch-limit" %}
3
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
4
+ // per-axis workgroup fold width (outputs > 16.7M elements).
5
  {% elif note == "limit" %}
6
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
7
+ // per-axis workgroup fold width.
8
  {% elif note == "device-axis" %}
9
+ // The flat dispatch is folded across x/y at a fixed per-axis workgroup
10
+ // width; gid.y carries the high portion of the output index.
11
  {% elif note == "vec4-limit" %}
12
+ // 2D-folded flat vec4 index: gid.y carries the high bits past the dispatch's
13
+ // per-axis workgroup fold width (the dispatch caps x and spills into y).
14
  {% elif note == "element-limit" %}
15
  // 2D-folded flat element index: gid.y carries the high bits past the
16
+ // dispatch's per-axis workgroup fold width.
17
  {% elif note == "dispatch" %}
18
+ // 2D-folded flat index: gid.y carries the high bits past the dispatch's
19
+ // per-axis workgroup fold width.
20
  {% endif %}
21
  {% if bound == "" %}
22
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
23
  {%- elif guardInline %}
24
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
25
  if ({{ name }} >= {{ bound }}) { return; }
26
  {%- else %}
27
+ let {{ name }} = gid.x + gid.y * {{ DISPATCH_FOLD_WIDTH }}u * {{ tunables.WORKGROUP_SIZE }}u;
28
  if ({{ name }} >= {{ bound }}) {
29
  return;
30
  }
 
69
 
70
  {% endif %}
71
  @compute @workgroup_size({{ tunables.WORKGROUP_SIZE }})
72
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
73
  {{ flat_index_2d("index", guardInline=true) }}
74
 
75
  let ow = index % params.outW;
build/webgpu/manifest.json CHANGED
@@ -2,69 +2,31 @@
2
  "domain": "ai.onnx",
3
  "name": "DeformConv",
4
  "sinceVersion": 22,
5
- "description": "Performs deformable convolution by sampling input at spatially offset locations specified per output position, enabling the kernel to adapt its receptive field shape. The `offset` tensor provides `(y, x)` offsets for each kernel point and output location; fractional offsets are resolved via bilinear interpolation and out-of-bounds locations contribute zero. An optional `mask` tensor modulates each sampling point's contribution.",
6
- "inputs": [
7
- {
8
- "role": "X",
9
- "dtype": "T",
10
- "description": "Input data tensor of shape `(N, C, H, W)` for 2D or `(N, C, D1, ..., Dn)` in general.",
11
- "rank": 4
12
- },
13
- {
14
- "role": "W",
15
- "dtype": "T",
16
- "description": "Convolution weight tensor of shape `(oC, C/group, kH, kW)`.",
17
- "rank": 4
18
- },
19
- {
20
- "role": "offset",
21
- "dtype": "T",
22
- "description": "Per-output-position sampling offsets of shape `(N, offset_group * kH * kW * 2, oH, oW)` for 2D data.",
23
- "rank": 4
24
- },
25
- {
26
- "role": "B",
27
- "dtype": "T",
28
- "optional": true,
29
- "description": "Optional 1D bias of length oC added to the convolution output.",
30
- "rank": 1
31
- },
32
- {
33
- "role": "mask",
34
- "dtype": "T",
35
- "optional": true,
36
- "description": "Optional modulation mask of shape `(N, offset_group * kH * kW, oH, oW)` scaling each sampled kernel point; defaults to ones.",
37
- "rank": 4
38
- }
39
- ],
40
- "outputs": [
41
- {
42
- "role": "Y",
43
  "dtype": "T",
44
- "description": "Output tensor of shape `(N, oC, oH, oW)` containing the deformable convolution result.",
45
  "rank": 4,
46
- "shape": ["dim(shapes.X, 0)", "dim(shapes.W, 0)", "floor((dim(shapes.X, 2) + padTop + padBottom - ((dim(shapes.W, 2) - 1) * dilationH + 1)) / strideH) + 1", "floor((dim(shapes.X, 3) + padLeft + padRight - ((dim(shapes.W, 3) - 1) * dilationW + 1)) / strideW) + 1"]
47
  }
48
- ],
49
- "attributes": { "group": 1, "offset_group": 1 },
50
- "attributeDescriptions": {
51
- "dilations": "Dilation factors for the spatial axes in height-width order; defaults to `[1, 1]`.",
52
- "group": "Number of groups the input channels C and output channels oC are divided into; both must be divisible by group.",
53
- "kernel_shape": "Kernel dimensions in height-width order. When omitted, they are inferred from the spatial dimensions of `W`.",
54
- "offset_group": "Number of offset groups; input channels `C` must be divisible by `offset_group`.",
55
- "pads": "Padding at the beginning and end of each spatial axis in `[top, left, bottom, right]` order; defaults to zeros.",
56
- "strides": "Stride factors for the spatial axes in height-width order; defaults to `[1, 1]`."
57
  },
58
- "typeConstraints": { "T": ["float32"] },
59
- "args": {
60
- "x": { "kind": "tensor", "semantic": "X", "role": "input" },
61
- "w": { "kind": "tensor", "semantic": "W", "role": "input" },
62
- "offset": { "kind": "tensor", "semantic": "offset", "role": "input" },
63
- "bias": { "kind": "tensor", "semantic": "B", "role": "input", "required": false },
64
- "mask": { "kind": "tensor", "semantic": "mask", "role": "input", "required": false },
65
- "y": { "kind": "tensor", "semantic": "Y", "role": "output" }
66
  },
67
- "tunables": { "WORKGROUP_SIZE": 128 },
 
68
  "derive": {
69
  "spatialRank": 2,
70
  "kernelShapeLengthOk": "not has(attrs, \"kernel_shape\") or (attrs.kernel_shape | length) == spatialRank",
@@ -81,157 +43,52 @@
81
  "padLeft": "attrs.pads[spatialRank - 1] if padsLengthOk and has(attrs, \"pads\") and spatialRank >= 1 else 0",
82
  "padBottom": "attrs.pads[2 * spatialRank - 2] if padsLengthOk and has(attrs, \"pads\") and spatialRank >= 2 else 0",
83
  "padRight": "attrs.pads[2 * spatialRank - 1] if padsLengthOk and has(attrs, \"pads\") and spatialRank >= 1 else 0",
84
- "spatialAttributesOk": "kernelShapeLengthOk and stridesLengthOk and dilationsLengthOk and padsLengthOk and strideH >= 1 and strideW >= 1 and dilationH >= 1 and dilationW >= 1 and padTop >= 0 and padLeft >= 0 and padBottom >= 0 and padRight >= 0 and (not has(attrs, \"kernel_shape\") or (kernelH == dim(shapes.W, 2) and kernelW == dim(shapes.W, 3)))",
85
- "alignedGroupsCoreOk": "spatialAttributesOk and ranks.X == 4 and ranks.W == 4 and ranks.offset == 4 and ranks.Y == 4 and attrs.group >= 1 and attrs.offset_group == attrs.group and dim(shapes.W, 2) >= 1 and dim(shapes.W, 2) <= 7 and dim(shapes.W, 3) >= 1 and dim(shapes.W, 3) <= 7 and dim(shapes.W, 1) * attrs.group == dim(shapes.X, 1) and dim(shapes.W, 0) % attrs.group == 0 and dim(shapes.Y, 0) == dim(shapes.X, 0) and dim(shapes.Y, 1) == dim(shapes.W, 0) and dim(shapes.Y, 2) == floor((dim(shapes.X, 2) + padTop + padBottom - ((dim(shapes.W, 2) - 1) * dilationH + 1)) / strideH) + 1 and dim(shapes.Y, 3) == floor((dim(shapes.X, 3) + padLeft + padRight - ((dim(shapes.W, 3) - 1) * dilationW + 1)) / strideW) + 1 and dim(shapes.offset, 0) == dim(shapes.X, 0) and dim(shapes.offset, 1) == attrs.offset_group * dim(shapes.W, 2) * dim(shapes.W, 3) * 2 and dim(shapes.offset, 2) == dim(shapes.Y, 2) and dim(shapes.offset, 3) == dim(shapes.Y, 3)",
86
- "alignedBiasMaskOk": "alignedGroupsCoreOk and present.bias and present.mask and ranks.B == 1 and ranks.mask == 4 and dim(shapes.B, 0) == dim(shapes.W, 0) and dim(shapes.mask, 0) == dim(shapes.X, 0) and dim(shapes.mask, 1) == attrs.offset_group * dim(shapes.W, 2) * dim(shapes.W, 3) and dim(shapes.mask, 2) == dim(shapes.Y, 2) and dim(shapes.mask, 3) == dim(shapes.Y, 3)",
87
  "alignedPlainOk": "alignedGroupsCoreOk and not present.bias and not present.mask",
88
- "genericCoreOk": "spatialAttributesOk and ranks.X == 4 and ranks.W == 4 and ranks.offset == 4 and ranks.Y == 4 and attrs.group >= 1 and attrs.offset_group >= 1 and dim(shapes.W, 1) * attrs.group == dim(shapes.X, 1) and dim(shapes.X, 1) % attrs.offset_group == 0 and dim(shapes.W, 0) % attrs.group == 0 and dim(shapes.Y, 0) == dim(shapes.X, 0) and dim(shapes.Y, 1) == dim(shapes.W, 0) and dim(shapes.Y, 2) == floor((dim(shapes.X, 2) + padTop + padBottom - ((dim(shapes.W, 2) - 1) * dilationH + 1)) / strideH) + 1 and dim(shapes.Y, 3) == floor((dim(shapes.X, 3) + padLeft + padRight - ((dim(shapes.W, 3) - 1) * dilationW + 1)) / strideW) + 1 and dim(shapes.offset, 0) == dim(shapes.X, 0) and dim(shapes.offset, 1) == attrs.offset_group * dim(shapes.W, 2) * dim(shapes.W, 3) * 2 and dim(shapes.offset, 2) == dim(shapes.Y, 2) and dim(shapes.offset, 3) == dim(shapes.Y, 3)",
89
- "genericBiasShapeOk": "present.bias and ranks.B == 1 and dim(shapes.B, 0) == dim(shapes.W, 0)",
90
- "genericMaskShapeOk": "present.mask and ranks.mask == 4 and dim(shapes.mask, 0) == dim(shapes.X, 0) and dim(shapes.mask, 1) == attrs.offset_group * dim(shapes.W, 2) * dim(shapes.W, 3) and dim(shapes.mask, 2) == dim(shapes.Y, 2) and dim(shapes.mask, 3) == dim(shapes.Y, 3)",
91
  "genericPlainOk": "genericCoreOk and not present.bias and not present.mask",
92
  "genericBiasOk": "genericCoreOk and genericBiasShapeOk and not present.mask",
93
  "genericMaskOk": "genericCoreOk and not present.bias and genericMaskShapeOk",
94
  "genericBiasMaskOk": "genericCoreOk and genericBiasShapeOk and genericMaskShapeOk",
95
- "alignedOcBlock": "16 if (dim(shapes.W, 0) / attrs.group) % 16 == 0 else (8 if (dim(shapes.W, 0) / attrs.group) % 8 == 0 else (4 if (dim(shapes.W, 0) / attrs.group) % 4 == 0 else 1))",
96
  "alignedOcVecs": "4 if alignedOcBlock == 16 else (2 if alignedOcBlock == 8 else (1 if alignedOcBlock == 4 else 0))"
97
  },
98
- "bindingSets": {
99
- "alignedBiasMask": [
100
- { "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
101
- { "name": "w", "arg": "w", "semantic": "W", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
102
- {
103
- "name": "offset",
104
- "arg": "offset",
105
- "semantic": "offset",
106
- "buffer": { "type": "read-only-storage" },
107
- "elementType": "f32"
108
- },
109
- {
110
- "name": "bias",
111
- "arg": "bias",
112
- "semantic": "B",
113
- "buffer": { "type": "read-only-storage" },
114
- "elementType": "f32"
115
- },
116
- {
117
- "name": "mask",
118
- "arg": "mask",
119
- "semantic": "mask",
120
- "buffer": { "type": "read-only-storage" },
121
- "elementType": "f32"
122
- },
123
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "f32" }
124
- ],
125
- "alignedPlain": [
126
- { "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
127
- { "name": "w", "arg": "w", "semantic": "W", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
128
- {
129
- "name": "offset",
130
- "arg": "offset",
131
- "semantic": "offset",
132
- "buffer": { "type": "read-only-storage" },
133
- "elementType": "f32"
134
- },
135
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "f32" }
136
- ],
137
- "nchw2dPlain": [
138
- { "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
139
- { "name": "w", "arg": "w", "semantic": "W", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
140
- {
141
- "name": "offset",
142
- "arg": "offset",
143
- "semantic": "offset",
144
- "buffer": { "type": "read-only-storage" },
145
- "elementType": "f32"
146
- },
147
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "f32" },
148
- {
149
- "name": "params",
150
- "semantic": "kernel.params",
151
- "buffer": { "type": "uniform" },
152
- "struct": {
153
- "name": "Params",
154
- "fields": [
155
- { "name": "count", "type": "u32", "value": "numel(shapes.Y)" },
156
- { "name": "inChannels", "type": "u32", "value": "dim(shapes.X, 1)" },
157
- { "name": "inH", "type": "u32", "value": "dim(shapes.X, 2)" },
158
- { "name": "inW", "type": "u32", "value": "dim(shapes.X, 3)" },
159
- { "name": "outChannels", "type": "u32", "value": "dim(shapes.W, 0)" },
160
- { "name": "weightInChannels", "type": "u32", "value": "dim(shapes.W, 1)" },
161
- { "name": "inChannelsPerGroup", "type": "u32", "value": "dim(shapes.X, 1) / attrs.group" },
162
- { "name": "outChannelsPerGroup", "type": "u32", "value": "dim(shapes.W, 0) / attrs.group" },
163
- { "name": "inChannelsPerOffsetGroup", "type": "u32", "value": "dim(shapes.X, 1) / attrs.offset_group" },
164
- { "name": "kernelH", "type": "u32", "value": "dim(shapes.W, 2)" },
165
- { "name": "kernelW", "type": "u32", "value": "dim(shapes.W, 3)" },
166
- { "name": "outH", "type": "u32", "value": "dim(shapes.Y, 2)" },
167
- { "name": "outW", "type": "u32", "value": "dim(shapes.Y, 3)" },
168
- { "name": "strideH", "type": "u32", "value": "strideH" },
169
- { "name": "strideW", "type": "u32", "value": "strideW" },
170
- { "name": "dilationH", "type": "u32", "value": "dilationH" },
171
- { "name": "dilationW", "type": "u32", "value": "dilationW" },
172
- { "name": "padH", "type": "i32", "value": "padTop" },
173
- { "name": "padW", "type": "i32", "value": "padLeft" },
174
- { "name": "offsetGroup", "type": "u32", "value": "attrs.offset_group" }
175
- ]
176
- }
177
- }
178
- ],
179
- "nchw2dBiasMask": [
180
- { "name": "x", "arg": "x", "semantic": "X", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
181
- { "name": "w", "arg": "w", "semantic": "W", "buffer": { "type": "read-only-storage" }, "elementType": "f32" },
182
- {
183
- "name": "offset",
184
- "arg": "offset",
185
- "semantic": "offset",
186
- "buffer": { "type": "read-only-storage" },
187
- "elementType": "f32"
188
- },
189
- {
190
- "name": "bias",
191
- "arg": "bias",
192
- "semantic": "B",
193
- "buffer": { "type": "read-only-storage" },
194
- "elementType": "f32"
195
- },
196
- {
197
- "name": "mask",
198
- "arg": "mask",
199
- "semantic": "mask",
200
- "buffer": { "type": "read-only-storage" },
201
- "elementType": "f32"
202
- },
203
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "f32" },
204
- {
205
- "name": "params",
206
- "semantic": "kernel.params",
207
- "buffer": { "type": "uniform" },
208
- "struct": {
209
- "name": "Params",
210
- "fields": [
211
- { "name": "count", "type": "u32", "value": "numel(shapes.Y)" },
212
- { "name": "inChannels", "type": "u32", "value": "dim(shapes.X, 1)" },
213
- { "name": "inH", "type": "u32", "value": "dim(shapes.X, 2)" },
214
- { "name": "inW", "type": "u32", "value": "dim(shapes.X, 3)" },
215
- { "name": "outChannels", "type": "u32", "value": "dim(shapes.W, 0)" },
216
- { "name": "weightInChannels", "type": "u32", "value": "dim(shapes.W, 1)" },
217
- { "name": "inChannelsPerGroup", "type": "u32", "value": "dim(shapes.X, 1) / attrs.group" },
218
- { "name": "outChannelsPerGroup", "type": "u32", "value": "dim(shapes.W, 0) / attrs.group" },
219
- { "name": "inChannelsPerOffsetGroup", "type": "u32", "value": "dim(shapes.X, 1) / attrs.offset_group" },
220
- { "name": "kernelH", "type": "u32", "value": "dim(shapes.W, 2)" },
221
- { "name": "kernelW", "type": "u32", "value": "dim(shapes.W, 3)" },
222
- { "name": "outH", "type": "u32", "value": "dim(shapes.Y, 2)" },
223
- { "name": "outW", "type": "u32", "value": "dim(shapes.Y, 3)" },
224
- { "name": "strideH", "type": "u32", "value": "strideH" },
225
- { "name": "strideW", "type": "u32", "value": "strideW" },
226
- { "name": "dilationH", "type": "u32", "value": "dilationH" },
227
- { "name": "dilationW", "type": "u32", "value": "dilationW" },
228
- { "name": "padH", "type": "i32", "value": "padTop" },
229
- { "name": "padW", "type": "i32", "value": "padLeft" },
230
- { "name": "offsetGroup", "type": "u32", "value": "attrs.offset_group" }
231
- ]
232
- }
233
- }
234
- ]
235
  },
236
  "variants": [
237
  {
@@ -242,35 +99,37 @@
242
  {
243
  "id": "main",
244
  "name": "DeformConv.AlignedGroupsBlocked",
245
- "source": {
246
- "shader": "deform-conv2d-aligned-groups.wgsl.jinja",
247
- "inputs": {
248
- "count": "numel(shapes.Y) / alignedOcBlock",
249
- "inC": "dim(shapes.X, 1)",
250
- "inCPerGroup": "dim(shapes.X, 1) / attrs.group",
251
- "inH": "dim(shapes.X, 2)",
252
- "inW": "dim(shapes.X, 3)",
253
- "outC": "dim(shapes.Y, 1)",
254
- "outCPerGroup": "dim(shapes.Y, 1) / attrs.group",
255
- "outChannelBlock": "alignedOcBlock",
256
- "outChannelVecs": "alignedOcVecs",
257
- "groups": "attrs.group",
258
- "outH": "dim(shapes.Y, 2)",
259
- "outW": "dim(shapes.Y, 3)",
260
- "kernelH": "dim(shapes.W, 2)",
261
- "kernelW": "dim(shapes.W, 3)",
262
- "strideH": "strideH",
263
- "strideW": "strideW",
264
- "dilationH": "dilationH",
265
- "dilationW": "dilationW",
266
- "padH": "padTop",
267
- "padW": "padLeft",
268
- "hasBias": true,
269
- "hasMask": true
270
- }
271
  },
272
- "bindings": "alignedBiasMask",
273
- "dispatch": { "threads": "numel(shapes.Y) / alignedOcBlock", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
274
  }
275
  ]
276
  },
@@ -282,143 +141,93 @@
282
  {
283
  "id": "main",
284
  "name": "DeformConv.AlignedGroupsBlocked",
285
- "source": {
286
- "shader": "deform-conv2d-aligned-groups.wgsl.jinja",
287
- "inputs": {
288
- "count": "numel(shapes.Y) / alignedOcBlock",
289
- "inC": "dim(shapes.X, 1)",
290
- "inCPerGroup": "dim(shapes.X, 1) / attrs.group",
291
- "inH": "dim(shapes.X, 2)",
292
- "inW": "dim(shapes.X, 3)",
293
- "outC": "dim(shapes.Y, 1)",
294
- "outCPerGroup": "dim(shapes.Y, 1) / attrs.group",
295
- "outChannelBlock": "alignedOcBlock",
296
- "outChannelVecs": "alignedOcVecs",
297
- "groups": "attrs.group",
298
- "outH": "dim(shapes.Y, 2)",
299
- "outW": "dim(shapes.Y, 3)",
300
- "kernelH": "dim(shapes.W, 2)",
301
- "kernelW": "dim(shapes.W, 3)",
302
- "strideH": "strideH",
303
- "strideW": "strideW",
304
- "dilationH": "dilationH",
305
- "dilationW": "dilationW",
306
- "padH": "padTop",
307
- "padW": "padLeft",
308
- "hasBias": false,
309
- "hasMask": false
310
- }
311
  },
312
- "bindings": "alignedPlain",
313
- "dispatch": { "threads": "numel(shapes.Y) / alignedOcBlock", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
314
  }
315
  ]
316
  },
317
  {
318
  "id": "nchw2d",
319
  "when": ["genericPlainOk"],
320
- "constants": { "hasBias": false, "hasMask": false },
321
  "passes": [
322
  {
323
  "id": "main",
324
  "name": "DeformConv",
325
  "shader": "deform-conv2d-nchw.wgsl.jinja",
326
- "bindings": "nchw2dPlain",
327
- "dispatch": { "threads": "numel(shapes.Y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
328
  }
329
  ]
330
  },
331
  {
332
  "id": "nchw2d_bias_mask",
 
333
  "when": ["genericBiasMaskOk"],
334
- "constants": { "hasBias": true, "hasMask": true },
335
  "passes": [
336
  {
337
  "id": "main",
338
  "name": "DeformConv",
339
  "shader": "deform-conv2d-nchw.wgsl.jinja",
340
- "bindings": "nchw2dBiasMask",
341
- "dispatch": { "threads": "numel(shapes.Y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
 
 
 
 
342
  }
343
- ],
344
- "priority": 10
345
  },
346
  {
347
  "id": "nchw2d_bias",
348
  "priority": 5,
349
  "when": ["genericBiasOk"],
350
- "constants": { "hasBias": true, "hasMask": false },
351
  "passes": [
352
  {
353
  "id": "main",
354
  "name": "DeformConv",
355
  "shader": "deform-conv2d-nchw.wgsl.jinja",
356
- "bindings": [
357
- {
358
- "name": "x",
359
- "arg": "x",
360
- "semantic": "X",
361
- "buffer": { "type": "read-only-storage" },
362
- "elementType": "f32"
363
- },
364
- {
365
- "name": "w",
366
- "arg": "w",
367
- "semantic": "W",
368
- "buffer": { "type": "read-only-storage" },
369
- "elementType": "f32"
370
- },
371
- {
372
- "name": "offset",
373
- "arg": "offset",
374
- "semantic": "offset",
375
- "buffer": { "type": "read-only-storage" },
376
- "elementType": "f32"
377
- },
378
- {
379
- "name": "bias",
380
- "arg": "bias",
381
- "semantic": "B",
382
- "buffer": { "type": "read-only-storage" },
383
- "elementType": "f32"
384
- },
385
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "f32" },
386
- {
387
- "name": "params",
388
- "semantic": "kernel.params",
389
- "buffer": { "type": "uniform" },
390
- "struct": {
391
- "name": "Params",
392
- "fields": [
393
- { "name": "count", "type": "u32", "value": "numel(shapes.Y)" },
394
- { "name": "inChannels", "type": "u32", "value": "dim(shapes.X, 1)" },
395
- { "name": "inH", "type": "u32", "value": "dim(shapes.X, 2)" },
396
- { "name": "inW", "type": "u32", "value": "dim(shapes.X, 3)" },
397
- { "name": "outChannels", "type": "u32", "value": "dim(shapes.W, 0)" },
398
- { "name": "weightInChannels", "type": "u32", "value": "dim(shapes.W, 1)" },
399
- { "name": "inChannelsPerGroup", "type": "u32", "value": "dim(shapes.X, 1) / attrs.group" },
400
- { "name": "outChannelsPerGroup", "type": "u32", "value": "dim(shapes.W, 0) / attrs.group" },
401
- {
402
- "name": "inChannelsPerOffsetGroup",
403
- "type": "u32",
404
- "value": "dim(shapes.X, 1) / attrs.offset_group"
405
- },
406
- { "name": "kernelH", "type": "u32", "value": "dim(shapes.W, 2)" },
407
- { "name": "kernelW", "type": "u32", "value": "dim(shapes.W, 3)" },
408
- { "name": "outH", "type": "u32", "value": "dim(shapes.Y, 2)" },
409
- { "name": "outW", "type": "u32", "value": "dim(shapes.Y, 3)" },
410
- { "name": "strideH", "type": "u32", "value": "strideH" },
411
- { "name": "strideW", "type": "u32", "value": "strideW" },
412
- { "name": "dilationH", "type": "u32", "value": "dilationH" },
413
- { "name": "dilationW", "type": "u32", "value": "dilationW" },
414
- { "name": "padH", "type": "i32", "value": "padTop" },
415
- { "name": "padW", "type": "i32", "value": "padLeft" },
416
- { "name": "offsetGroup", "type": "u32", "value": "attrs.offset_group" }
417
- ]
418
- }
419
- }
420
- ],
421
- "dispatch": { "threads": "numel(shapes.Y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
422
  }
423
  ]
424
  },
@@ -426,78 +235,18 @@
426
  "id": "nchw2d_mask",
427
  "priority": 5,
428
  "when": ["genericMaskOk"],
429
- "constants": { "hasBias": false, "hasMask": true },
430
  "passes": [
431
  {
432
  "id": "main",
433
  "name": "DeformConv",
434
  "shader": "deform-conv2d-nchw.wgsl.jinja",
435
- "bindings": [
436
- {
437
- "name": "x",
438
- "arg": "x",
439
- "semantic": "X",
440
- "buffer": { "type": "read-only-storage" },
441
- "elementType": "f32"
442
- },
443
- {
444
- "name": "w",
445
- "arg": "w",
446
- "semantic": "W",
447
- "buffer": { "type": "read-only-storage" },
448
- "elementType": "f32"
449
- },
450
- {
451
- "name": "offset",
452
- "arg": "offset",
453
- "semantic": "offset",
454
- "buffer": { "type": "read-only-storage" },
455
- "elementType": "f32"
456
- },
457
- {
458
- "name": "mask",
459
- "arg": "mask",
460
- "semantic": "mask",
461
- "buffer": { "type": "read-only-storage" },
462
- "elementType": "f32"
463
- },
464
- { "name": "y", "arg": "y", "semantic": "Y", "buffer": { "type": "storage" }, "elementType": "f32" },
465
- {
466
- "name": "params",
467
- "semantic": "kernel.params",
468
- "buffer": { "type": "uniform" },
469
- "struct": {
470
- "name": "Params",
471
- "fields": [
472
- { "name": "count", "type": "u32", "value": "numel(shapes.Y)" },
473
- { "name": "inChannels", "type": "u32", "value": "dim(shapes.X, 1)" },
474
- { "name": "inH", "type": "u32", "value": "dim(shapes.X, 2)" },
475
- { "name": "inW", "type": "u32", "value": "dim(shapes.X, 3)" },
476
- { "name": "outChannels", "type": "u32", "value": "dim(shapes.W, 0)" },
477
- { "name": "weightInChannels", "type": "u32", "value": "dim(shapes.W, 1)" },
478
- { "name": "inChannelsPerGroup", "type": "u32", "value": "dim(shapes.X, 1) / attrs.group" },
479
- { "name": "outChannelsPerGroup", "type": "u32", "value": "dim(shapes.W, 0) / attrs.group" },
480
- {
481
- "name": "inChannelsPerOffsetGroup",
482
- "type": "u32",
483
- "value": "dim(shapes.X, 1) / attrs.offset_group"
484
- },
485
- { "name": "kernelH", "type": "u32", "value": "dim(shapes.W, 2)" },
486
- { "name": "kernelW", "type": "u32", "value": "dim(shapes.W, 3)" },
487
- { "name": "outH", "type": "u32", "value": "dim(shapes.Y, 2)" },
488
- { "name": "outW", "type": "u32", "value": "dim(shapes.Y, 3)" },
489
- { "name": "strideH", "type": "u32", "value": "strideH" },
490
- { "name": "strideW", "type": "u32", "value": "strideW" },
491
- { "name": "dilationH", "type": "u32", "value": "dilationH" },
492
- { "name": "dilationW", "type": "u32", "value": "dilationW" },
493
- { "name": "padH", "type": "i32", "value": "padTop" },
494
- { "name": "padW", "type": "i32", "value": "padLeft" },
495
- { "name": "offsetGroup", "type": "u32", "value": "attrs.offset_group" }
496
- ]
497
- }
498
- }
499
- ],
500
- "dispatch": { "threads": "numel(shapes.Y)", "workgroupSize": "tunables.WORKGROUP_SIZE" }
501
  }
502
  ]
503
  }
 
2
  "domain": "ai.onnx",
3
  "name": "DeformConv",
4
  "sinceVersion": 22,
5
+ "inputs": {
6
+ "x": { "onnx": "X", "dtype": "T", "rank": 4 },
7
+ "w": { "onnx": "W", "dtype": "T", "rank": 4 },
8
+ "offset": { "dtype": "T", "rank": 4 },
9
+ "bias": { "onnx": "B", "dtype": "T", "rank": 1, "optional": true },
10
+ "mask": { "dtype": "T", "rank": 4, "optional": true }
11
+ },
12
+ "outputs": {
13
+ "y": {
14
+ "onnx": "Y",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  "dtype": "T",
 
16
  "rank": 4,
17
+ "shape": ["dim(shapes.x, 0)", "dim(shapes.w, 0)", "floor((dim(shapes.x, 2) + padTop + padBottom - ((dim(shapes.w, 2) - 1) * dilationH + 1)) / strideH) + 1", "floor((dim(shapes.x, 3) + padLeft + padRight - ((dim(shapes.w, 3) - 1) * dilationW + 1)) / strideW) + 1"]
18
  }
 
 
 
 
 
 
 
 
 
19
  },
20
+ "attributes": {
21
+ "group": { "default": 1 },
22
+ "offset_group": { "default": 1 },
23
+ "dilations": {},
24
+ "kernel_shape": {},
25
+ "pads": {},
26
+ "strides": {}
 
27
  },
28
+ "typeConstraints": { "T": ["float32"] },
29
+ "tunables": { "WORKGROUP_SIZE": { "default": 128 } },
30
  "derive": {
31
  "spatialRank": 2,
32
  "kernelShapeLengthOk": "not has(attrs, \"kernel_shape\") or (attrs.kernel_shape | length) == spatialRank",
 
43
  "padLeft": "attrs.pads[spatialRank - 1] if padsLengthOk and has(attrs, \"pads\") and spatialRank >= 1 else 0",
44
  "padBottom": "attrs.pads[2 * spatialRank - 2] if padsLengthOk and has(attrs, \"pads\") and spatialRank >= 2 else 0",
45
  "padRight": "attrs.pads[2 * spatialRank - 1] if padsLengthOk and has(attrs, \"pads\") and spatialRank >= 1 else 0",
46
+ "spatialAttributesOk": "kernelShapeLengthOk and stridesLengthOk and dilationsLengthOk and padsLengthOk and strideH >= 1 and strideW >= 1 and dilationH >= 1 and dilationW >= 1 and padTop >= 0 and padLeft >= 0 and padBottom >= 0 and padRight >= 0 and (not has(attrs, \"kernel_shape\") or (kernelH == dim(shapes.w, 2) and kernelW == dim(shapes.w, 3)))",
47
+ "alignedGroupsCoreOk": "spatialAttributesOk and ranks.x == 4 and ranks.w == 4 and ranks.offset == 4 and ranks.y == 4 and attrs.group >= 1 and attrs.offset_group == attrs.group and dim(shapes.w, 2) >= 1 and dim(shapes.w, 2) <= 7 and dim(shapes.w, 3) >= 1 and dim(shapes.w, 3) <= 7 and dim(shapes.w, 1) * attrs.group == dim(shapes.x, 1) and dim(shapes.w, 0) % attrs.group == 0 and dim(shapes.y, 0) == dim(shapes.x, 0) and dim(shapes.y, 1) == dim(shapes.w, 0) and dim(shapes.y, 2) == floor((dim(shapes.x, 2) + padTop + padBottom - ((dim(shapes.w, 2) - 1) * dilationH + 1)) / strideH) + 1 and dim(shapes.y, 3) == floor((dim(shapes.x, 3) + padLeft + padRight - ((dim(shapes.w, 3) - 1) * dilationW + 1)) / strideW) + 1 and dim(shapes.offset, 0) == dim(shapes.x, 0) and dim(shapes.offset, 1) == attrs.offset_group * dim(shapes.w, 2) * dim(shapes.w, 3) * 2 and dim(shapes.offset, 2) == dim(shapes.y, 2) and dim(shapes.offset, 3) == dim(shapes.y, 3)",
48
+ "alignedBiasMaskOk": "alignedGroupsCoreOk and present.bias and present.mask and ranks.bias == 1 and ranks.mask == 4 and dim(shapes.bias, 0) == dim(shapes.w, 0) and dim(shapes.mask, 0) == dim(shapes.x, 0) and dim(shapes.mask, 1) == attrs.offset_group * dim(shapes.w, 2) * dim(shapes.w, 3) and dim(shapes.mask, 2) == dim(shapes.y, 2) and dim(shapes.mask, 3) == dim(shapes.y, 3)",
49
  "alignedPlainOk": "alignedGroupsCoreOk and not present.bias and not present.mask",
50
+ "genericCoreOk": "spatialAttributesOk and ranks.x == 4 and ranks.w == 4 and ranks.offset == 4 and ranks.y == 4 and attrs.group >= 1 and attrs.offset_group >= 1 and dim(shapes.w, 1) * attrs.group == dim(shapes.x, 1) and dim(shapes.x, 1) % attrs.offset_group == 0 and dim(shapes.w, 0) % attrs.group == 0 and dim(shapes.y, 0) == dim(shapes.x, 0) and dim(shapes.y, 1) == dim(shapes.w, 0) and dim(shapes.y, 2) == floor((dim(shapes.x, 2) + padTop + padBottom - ((dim(shapes.w, 2) - 1) * dilationH + 1)) / strideH) + 1 and dim(shapes.y, 3) == floor((dim(shapes.x, 3) + padLeft + padRight - ((dim(shapes.w, 3) - 1) * dilationW + 1)) / strideW) + 1 and dim(shapes.offset, 0) == dim(shapes.x, 0) and dim(shapes.offset, 1) == attrs.offset_group * dim(shapes.w, 2) * dim(shapes.w, 3) * 2 and dim(shapes.offset, 2) == dim(shapes.y, 2) and dim(shapes.offset, 3) == dim(shapes.y, 3)",
51
+ "genericBiasShapeOk": "present.bias and ranks.bias == 1 and dim(shapes.bias, 0) == dim(shapes.w, 0)",
52
+ "genericMaskShapeOk": "present.mask and ranks.mask == 4 and dim(shapes.mask, 0) == dim(shapes.x, 0) and dim(shapes.mask, 1) == attrs.offset_group * dim(shapes.w, 2) * dim(shapes.w, 3) and dim(shapes.mask, 2) == dim(shapes.y, 2) and dim(shapes.mask, 3) == dim(shapes.y, 3)",
53
  "genericPlainOk": "genericCoreOk and not present.bias and not present.mask",
54
  "genericBiasOk": "genericCoreOk and genericBiasShapeOk and not present.mask",
55
  "genericMaskOk": "genericCoreOk and not present.bias and genericMaskShapeOk",
56
  "genericBiasMaskOk": "genericCoreOk and genericBiasShapeOk and genericMaskShapeOk",
57
+ "alignedOcBlock": "16 if (dim(shapes.w, 0) / attrs.group) % 16 == 0 else (8 if (dim(shapes.w, 0) / attrs.group) % 8 == 0 else (4 if (dim(shapes.w, 0) / attrs.group) % 4 == 0 else 1))",
58
  "alignedOcVecs": "4 if alignedOcBlock == 16 else (2 if alignedOcBlock == 8 else (1 if alignedOcBlock == 4 else 0))"
59
  },
60
+ "bindings": {
61
+ "x": { "buffer": "read-only-storage", "elementType": "f32" },
62
+ "w": { "buffer": "read-only-storage", "elementType": "f32" },
63
+ "offset": { "buffer": "read-only-storage", "elementType": "f32" },
64
+ "bias": { "buffer": "read-only-storage", "elementType": "f32" },
65
+ "mask": { "buffer": "read-only-storage", "elementType": "f32" },
66
+ "y": { "buffer": "storage", "elementType": "f32" },
67
+ "params": {
68
+ "buffer": "uniform",
69
+ "struct": [
70
+ { "name": "count", "type": "u32", "value": "numel(shapes.y)" },
71
+ { "name": "inChannels", "type": "u32", "value": "dim(shapes.x, 1)" },
72
+ { "name": "inH", "type": "u32", "value": "dim(shapes.x, 2)" },
73
+ { "name": "inW", "type": "u32", "value": "dim(shapes.x, 3)" },
74
+ { "name": "outChannels", "type": "u32", "value": "dim(shapes.w, 0)" },
75
+ { "name": "weightInChannels", "type": "u32", "value": "dim(shapes.w, 1)" },
76
+ { "name": "inChannelsPerGroup", "type": "u32", "value": "dim(shapes.x, 1) / attrs.group" },
77
+ { "name": "outChannelsPerGroup", "type": "u32", "value": "dim(shapes.w, 0) / attrs.group" },
78
+ { "name": "inChannelsPerOffsetGroup", "type": "u32", "value": "dim(shapes.x, 1) / attrs.offset_group" },
79
+ { "name": "kernelH", "type": "u32", "value": "dim(shapes.w, 2)" },
80
+ { "name": "kernelW", "type": "u32", "value": "dim(shapes.w, 3)" },
81
+ { "name": "outH", "type": "u32", "value": "dim(shapes.y, 2)" },
82
+ { "name": "outW", "type": "u32", "value": "dim(shapes.y, 3)" },
83
+ { "name": "strideH", "type": "u32", "value": "strideH" },
84
+ { "name": "strideW", "type": "u32", "value": "strideW" },
85
+ { "name": "dilationH", "type": "u32", "value": "dilationH" },
86
+ { "name": "dilationW", "type": "u32", "value": "dilationW" },
87
+ { "name": "padH", "type": "i32", "value": "padTop" },
88
+ { "name": "padW", "type": "i32", "value": "padLeft" },
89
+ { "name": "offsetGroup", "type": "u32", "value": "attrs.offset_group" }
90
+ ]
91
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  },
93
  "variants": [
94
  {
 
99
  {
100
  "id": "main",
101
  "name": "DeformConv.AlignedGroupsBlocked",
102
+ "shader": "deform-conv2d-aligned-groups.wgsl.jinja",
103
+ "derive": {
104
+ "count": "numel(shapes.y) / alignedOcBlock",
105
+ "inC": "dim(shapes.x, 1)",
106
+ "inCPerGroup": "dim(shapes.x, 1) / attrs.group",
107
+ "inH": "dim(shapes.x, 2)",
108
+ "inW": "dim(shapes.x, 3)",
109
+ "outC": "dim(shapes.y, 1)",
110
+ "outCPerGroup": "dim(shapes.y, 1) / attrs.group",
111
+ "outChannelBlock": "alignedOcBlock",
112
+ "outChannelVecs": "alignedOcVecs",
113
+ "groups": "attrs.group",
114
+ "outH": "dim(shapes.y, 2)",
115
+ "outW": "dim(shapes.y, 3)",
116
+ "kernelHSpec": "dim(shapes.w, 2)",
117
+ "kernelWSpec": "dim(shapes.w, 3)",
118
+ "strideHSpec": "strideH",
119
+ "strideWSpec": "strideW",
120
+ "dilationHSpec": "dilationH",
121
+ "dilationWSpec": "dilationW",
122
+ "padH": "padTop",
123
+ "padW": "padLeft",
124
+ "hasBias": true,
125
+ "hasMask": true
 
 
126
  },
127
+ "bindings": ["x", "w", "offset", "bias", "mask", "y"],
128
+ "dispatch": {
129
+ "x": "min(ceilDiv((numel(shapes.y) / alignedOcBlock), (tunables.WORKGROUP_SIZE)), 65535)",
130
+ "y": "ceilDiv(ceilDiv((numel(shapes.y) / alignedOcBlock), (tunables.WORKGROUP_SIZE)), 65535)",
131
+ "z": 1
132
+ }
133
  }
134
  ]
135
  },
 
141
  {
142
  "id": "main",
143
  "name": "DeformConv.AlignedGroupsBlocked",
144
+ "shader": "deform-conv2d-aligned-groups.wgsl.jinja",
145
+ "derive": {
146
+ "count": "numel(shapes.y) / alignedOcBlock",
147
+ "inC": "dim(shapes.x, 1)",
148
+ "inCPerGroup": "dim(shapes.x, 1) / attrs.group",
149
+ "inH": "dim(shapes.x, 2)",
150
+ "inW": "dim(shapes.x, 3)",
151
+ "outC": "dim(shapes.y, 1)",
152
+ "outCPerGroup": "dim(shapes.y, 1) / attrs.group",
153
+ "outChannelBlock": "alignedOcBlock",
154
+ "outChannelVecs": "alignedOcVecs",
155
+ "groups": "attrs.group",
156
+ "outH": "dim(shapes.y, 2)",
157
+ "outW": "dim(shapes.y, 3)",
158
+ "kernelHSpec": "dim(shapes.w, 2)",
159
+ "kernelWSpec": "dim(shapes.w, 3)",
160
+ "strideHSpec": "strideH",
161
+ "strideWSpec": "strideW",
162
+ "dilationHSpec": "dilationH",
163
+ "dilationWSpec": "dilationW",
164
+ "padH": "padTop",
165
+ "padW": "padLeft",
166
+ "hasBias": false,
167
+ "hasMask": false
 
 
168
  },
169
+ "bindings": ["x", "w", "offset", "y"],
170
+ "dispatch": {
171
+ "x": "min(ceilDiv((numel(shapes.y) / alignedOcBlock), (tunables.WORKGROUP_SIZE)), 65535)",
172
+ "y": "ceilDiv(ceilDiv((numel(shapes.y) / alignedOcBlock), (tunables.WORKGROUP_SIZE)), 65535)",
173
+ "z": 1
174
+ }
175
  }
176
  ]
177
  },
178
  {
179
  "id": "nchw2d",
180
  "when": ["genericPlainOk"],
181
+ "derive": { "hasBias": false, "hasMask": false },
182
  "passes": [
183
  {
184
  "id": "main",
185
  "name": "DeformConv",
186
  "shader": "deform-conv2d-nchw.wgsl.jinja",
187
+ "bindings": ["x", "w", "offset", "y", "params"],
188
+ "dispatch": {
189
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
190
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
191
+ "z": 1
192
+ }
193
  }
194
  ]
195
  },
196
  {
197
  "id": "nchw2d_bias_mask",
198
+ "priority": 10,
199
  "when": ["genericBiasMaskOk"],
200
+ "derive": { "hasBias": true, "hasMask": true },
201
  "passes": [
202
  {
203
  "id": "main",
204
  "name": "DeformConv",
205
  "shader": "deform-conv2d-nchw.wgsl.jinja",
206
+ "bindings": ["x", "w", "offset", "bias", "mask", "y", "params"],
207
+ "dispatch": {
208
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
209
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
210
+ "z": 1
211
+ }
212
  }
213
+ ]
 
214
  },
215
  {
216
  "id": "nchw2d_bias",
217
  "priority": 5,
218
  "when": ["genericBiasOk"],
219
+ "derive": { "hasBias": true, "hasMask": false },
220
  "passes": [
221
  {
222
  "id": "main",
223
  "name": "DeformConv",
224
  "shader": "deform-conv2d-nchw.wgsl.jinja",
225
+ "bindings": ["x", "w", "offset", "bias", "y", "params"],
226
+ "dispatch": {
227
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
228
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
229
+ "z": 1
230
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  }
232
  ]
233
  },
 
235
  "id": "nchw2d_mask",
236
  "priority": 5,
237
  "when": ["genericMaskOk"],
238
+ "derive": { "hasBias": false, "hasMask": true },
239
  "passes": [
240
  {
241
  "id": "main",
242
  "name": "DeformConv",
243
  "shader": "deform-conv2d-nchw.wgsl.jinja",
244
+ "bindings": ["x", "w", "offset", "mask", "y", "params"],
245
+ "dispatch": {
246
+ "x": "min(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
247
+ "y": "ceilDiv(ceilDiv((numel(shapes.y)), (tunables.WORKGROUP_SIZE)), 65535)",
248
+ "z": 1
249
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  }
251
  ]
252
  }
build/webgpu/metadata.json CHANGED
@@ -1,19 +1,29 @@
1
  {
2
  "name": "ai.onnx.DeformConv",
3
- "id": "_ai_onnx_deformconv_webgpu_3b61063",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
- "bench.json": "LJ4HwKvQ977QEVGUZvzpFnDdxEAfvPP7zF1Dll/uke0=",
11
- "deform-conv2d-aligned-groups.wgsl.jinja": "8Wli1NLpk4sMZgYYbCntOUWTq8HJjsxZVBRfi6mBofY=",
12
- "deform-conv2d-nchw.wgsl.jinja": "T7Q8v4p8H6Gpg9DCjp7TN4hzhAWyu6qHGI7URjr0Ri4=",
13
- "manifest.json": "KTQl1tyOvIadWeHKJFZs6b0bcYG2SH7m+zhqbGuYHgw=",
14
- "test.json": "evZiy5ZGtO0ZxwhNcN0uDHUwi27QlN+r5ghmsDXmUZw="
15
  }
16
  },
17
- "provenance": { "kernel": { "sha": "2e7068faf55e7f43df740015f6d1ee49391a41c5", "dirty": false } },
18
- "webgpu": { "manifestSpec": "1.0", "specialized": true, "opPath": "ops/ai.onnx.DeformConv" }
 
 
 
 
 
 
 
 
 
 
19
  }
 
1
  {
2
  "name": "ai.onnx.DeformConv",
3
+ "id": "_ai_onnx_deformconv_webgpu_bd95a88",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "backend": { "type": "webgpu" },
7
  "digest": {
8
  "algorithm": "sha256",
9
  "files": {
10
+ "bench.json": "QuUDCU5THdFkh2/r2/k4/Uo61MTtbMbE8R5uE3XZagU=",
11
+ "deform-conv2d-aligned-groups.wgsl.jinja": "dtmKq+oPJLAziEgWffUCuNR05TCDSWuFTpIeKrkfhXA=",
12
+ "deform-conv2d-nchw.wgsl.jinja": "hjYa940wbG47JZU6xEB1f+ocmLfCTdKYd2bZWwZ+8VU=",
13
+ "manifest.json": "p+fNwbHM9W6A5oca8kgfytxZOvic1x+qOPu44gH6Kts=",
14
+ "test.json": "868t0+sfE0Szo3ySh3cdEN+OTQ+77X7pAUXkgxxZqrc="
15
  }
16
  },
17
+ "provenance": { "kernel": { "sha": "91d990483a174128daf7673f3f37a7c890493ae1", "dirty": false } },
18
+ "webgpu": {
19
+ "manifestSpec": "2.0",
20
+ "variants": {
21
+ "nchw2d_bias_mask_aligned_groups_blocked": ["deform-conv2d-aligned-groups.wgsl.jinja"],
22
+ "nchw2d_aligned_groups_blocked": ["deform-conv2d-aligned-groups.wgsl.jinja"],
23
+ "nchw2d": ["deform-conv2d-nchw.wgsl.jinja"],
24
+ "nchw2d_bias_mask": ["deform-conv2d-nchw.wgsl.jinja"],
25
+ "nchw2d_bias": ["deform-conv2d-nchw.wgsl.jinja"],
26
+ "nchw2d_mask": ["deform-conv2d-nchw.wgsl.jinja"]
27
+ }
28
+ }
29
  }
build/webgpu/test.json CHANGED
@@ -1,5 +1,4 @@
1
  {
2
- "op": "ai.onnx.DeformConv",
3
  "fixtureArrays": {
4
  "onnx_backend_basic_deform_conv_without_padding_input_offset": [0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.10000000149011612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
5
  },
@@ -128,7 +127,7 @@
128
  "w": {
129
  "dtype": "float32",
130
  "shape": [1, 1, 2, 2],
131
- "data": { "kind": "values", "values": [1.0, 0.0, 0.0, -1.0] }
132
  },
133
  "offset": { "dtype": "float32", "shape": [1, 8, 2, 2], "data": { "kind": "constant", "value": 0.0 } }
134
  },
@@ -211,7 +210,7 @@
211
  {
212
  "name": "mask_zeros_preserve_bias",
213
  "inputs": {
214
- "x": { "dtype": "float32", "shape": [1, 1, 2, 2], "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0] } },
215
  "w": { "dtype": "float32", "shape": [1, 1, 1, 1], "data": { "kind": "values", "values": [7.0] } },
216
  "offset": { "dtype": "float32", "shape": [1, 2, 2, 2], "data": { "kind": "constant", "value": 0.0 } },
217
  "bias": { "dtype": "float32", "shape": [1], "data": { "kind": "values", "values": [0.5] } },
@@ -788,7 +787,7 @@
788
  "offset": {
789
  "dtype": "float32",
790
  "shape": [1, 2, 2, 2],
791
- "data": { "kind": "values", "values": [2.0, 2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0] }
792
  }
793
  },
794
  "outputs": {
@@ -796,7 +795,7 @@
796
  "dtype": "float32",
797
  "shape": [1, 1, 2, 2],
798
  "tolerance": 0,
799
- "data": { "kind": "values", "values": [0.0, 0.0, 0.0, 0.0] }
800
  }
801
  }
802
  },
 
1
  {
 
2
  "fixtureArrays": {
3
  "onnx_backend_basic_deform_conv_without_padding_input_offset": [0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.10000000149011612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
4
  },
 
127
  "w": {
128
  "dtype": "float32",
129
  "shape": [1, 1, 2, 2],
130
+ "data": { "kind": "values", "values": [1.0, 0.0, 0.0, -2.0] }
131
  },
132
  "offset": { "dtype": "float32", "shape": [1, 8, 2, 2], "data": { "kind": "constant", "value": 0.0 } }
133
  },
 
210
  {
211
  "name": "mask_zeros_preserve_bias",
212
  "inputs": {
213
+ "x": { "dtype": "float32", "shape": [1, 1, 2, 2], "data": { "kind": "constant", "value": 2.0 } },
214
  "w": { "dtype": "float32", "shape": [1, 1, 1, 1], "data": { "kind": "values", "values": [7.0] } },
215
  "offset": { "dtype": "float32", "shape": [1, 2, 2, 2], "data": { "kind": "constant", "value": 0.0 } },
216
  "bias": { "dtype": "float32", "shape": [1], "data": { "kind": "values", "values": [0.5] } },
 
787
  "offset": {
788
  "dtype": "float32",
789
  "shape": [1, 2, 2, 2],
790
+ "data": { "kind": "values", "values": [2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] }
791
  }
792
  },
793
  "outputs": {
 
795
  "dtype": "float32",
796
  "shape": [1, 1, 2, 2],
797
  "tolerance": 0,
798
+ "data": { "kind": "values", "values": [0.0, 0.0, 3.0, 4.0] }
799
  }
800
  }
801
  },