Skip to content

feat(ffai): vision/audio/TTS/video kernel batch + kernel_tests_harness dead-strip fix - #252

Merged
ekryski merged 36 commits into
0xClandestine:devfrom
thewafflehaus:ek/transpose-tokens-heads-kernel
Jun 3, 2026
Merged

feat(ffai): vision/audio/TTS/video kernel batch + kernel_tests_harness dead-strip fix#252
ekryski merged 36 commits into
0xClandestine:devfrom
thewafflehaus:ek/transpose-tokens-heads-kernel

Conversation

@ekryski

@ekryski ekryski commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

This branch adds the GPU kernels FFAI (an Apple-Silicon LLM/VLM/audio inference engine built on metaltile) needs to move its remaining vision, audio, video, and TTS paths off CPU, plus a test-infrastructure fix that closes a silent gap in the kernel-correctness gate. Every kernel ships a paired #[test_kernel] GPU-correctness test against a CPU oracle in the same commit; all pass via tile test.

Stacked on #247 (ek/bench-metrics). The first commit (feat(bench): perf metrics …) belongs to #247 and only appears here because the .flops() annotations below depend on it. Once #247 merges I'll rebase to drop it; review the commits above it.

What changed

New FFAI kernels (each with a #[test_kernel] GPU-correctness test + #[bench]):

Kernel Purpose
transpose_th token↔head-major transpose for GPU vision attention
clamp_scalar element-wise scalar clamp (Gemma-4 clipped linears)
resize_normalize (+ _bicubic) fused resize + per-channel normalize + interleaved→NCHW (VL preprocess)
depthwise_conv2d_nhwc channel-last depthwise conv (FastVLM)
sdpa_bidirectional_windowed (d80) block-diagonal windowed bidirectional SDPA (Qwen2.5-VL)
sdpa_rel_pos_conformer (d128) content-dependent Transformer-XL relative-position SDPA (Conformer audio)
sdpa_decode_sink_buf per-head attention-sink decode (GPT-OSS)
avg_pool2d_nhwc, im2col_patch, pos_emb_2d_add, frame_diff_luma vision/video front-end ops
mel_spectrogram_magnitude magnitude log-Mel front-end
lstm (ffai_lstm full-sequence + lstm_cell), adain1d TTS acoustic stack (StyleTTS2/Kokoro)
conv1d_dilated, conv1d_transpose HiFi-GAN-style vocoder convs

Cleanups

  • De-model-named the stdlib: kokoro.rsadain1d.rs/lstm.rs, fishspeech_conv1d.rsconv1d_dilated_transpose.rs (kernels named by what they compute, not the model).
  • Migrated the new kernels to the bare #[kernel] + #[bench] form after the macro refactor.
  • .flops() annotations on the compute-bound kernels (conformer/windowed/sink SDPA, depthwise conv, LSTM, conv1d) so feat(bench): wall-clock latency, GFLOP/s, roofline %-of-peak + bottleneck verdict #247's GFLOP/s + roofline columns populate; memory-bound kernels left blank.

Test-infrastructure fix (test(harness): anchor kernel TUs …)

  • kernel_tests_harness only called all_tests(); with nothing referencing kernel code, macOS -dead_strip removed every metaltile-std kernel translation unit — and its #[test_kernel] inventory submissions — so the harness iterated zero entries and passed vacuously (~1.4 s, exercising nothing). It now references all_kernels() to anchor the TUs (the same anchor kernel_registry_consistency relies on) and asserts both registries are non-empty. The harness now actually runs all GPU #[test_kernel] checks (~24 s) under cargo test --workspace.

Docs

  • docs/KERNEL_ORGANIZATION.md — proposed organization of the kernel/ops tree by kernel family, reconciled with the v4 CLI subprocess rewrite. For discussion.

Update — complete the StyleTTS2 / Kokoro GPU chain

The original TTS batch above (adain1d, lstm, conv1d_dilated/conv1d_transpose) shipped the building blocks. These follow-on commits add the remaining kernels so the entire Kokoro / StyleTTS2 text→waveform chain (PLBERT text encoder → prosody predictor → AdaIN decoder → iSTFTNet generator → vocoder) runs GPU-resident in FFAI, verified end-to-end against the CPU / mlx-audio reference.

New kernels (each with a paired #[test_kernel] + #[bench]):

Kernel Purpose
leaky_relu runtime-slope LeakyReLU — the generator / AdaIN-resblock activation (slope 0.2 / 0.1 / 0.01)
snake1d x + (1/α)·sin²(αx) with per-channel learnable α — the BigVGAN / iSTFTNet periodic activation
upsample_nearest1d nearest 1-D up-sample — the AdainResBlk1d shortcut + F0/N predictor up-sampler
conv1d_transpose_depthwise grouped (depthwise, groups == channels) conv-transpose — the StyleTTS2 decoder pool (the dense conv1d_transpose sums over all in-channels)
gelu_erf (+ a new erf prelude intrinsic) exact 0.5·x·(1+erf(x/√2)) GELU — PLBERT's nn.GELU() default; the tanh-approx mt_gelu drifts enough over 12 ALBERT layers to shift the predictor's rounding-sensitive durations

Correctness fix to an existing kernel

  • adain1d: clamp variance ≥ 0 before rsqrt. Variance was computed as E[x²] − E[x]², which suffers catastrophic f32 cancellation when a channel is near-constant over a long time axis (the Kokoro generator runs adain over ~7,800 frames): the result can go slightly negative → rsqrt(negative) = NaN, poisoning the whole utterance. The true variance is ≥ 0, so clamp it; this matches the CPU two-pass reference. Latent bug affecting any adain1d consumer, not just Kokoro.

LSTM coverage

  • Added a ffai_lstm GPU-correctness test at the real Kokoro predictor.shared shape (hidden 256 = 8 full simdgroups, seqLen 65, input 640) — the prior tests capped at hidden 40 / seqLen 8, never exercising the cross-simdgroup threadgroup recurrence over a long sequence. Passes. (FFAI side carries a note: the Kokoro front-end LSTMs run on CPU as a workaround for a context-dependent NaN that only appears after the full chain's many prior GPU dispatches and is not reproducible standalone — the kernel is bit-exact in isolation.)

Verification

  • tile test — every new kernel passes against its CPU oracle (e.g. sdpa_decode_sink_buf 6/6, sdpa_rel_pos_conformer 6/6, ffai_lstm (incl. hidden-256/seqLen-65), conv1d_* + conv1d_transpose_depthwise, snake1d, leaky_relu, upsample_nearest1d, gelu_erf, resize_normalize_bicubic).
  • make test — full workspace gate green (0 failures); make emit-all — every kernel emits valid MSL under xcrun metal; the empty-body MSL detector is clean.
  • kernel_registry_consistency + the fixed kernel_tests_harness (all GPU checks, ~24 s) pass.
  • Downstream end-to-end: FFAI regenerates these kernels and runs the full Kokoro chain GPU-resident — PLBERT (gemm/sdpa/gelu_erf/layerNorm) → predictor F0/N AdaIN curves → AdaIN decoder (conv1d_transpose_depthwise pool) → iSTFTNet generator (leaky_relu/snake1d/conv1d_*) → vocoder iSTFT — producing a waveform identical to the verified CPU path (energy 75.2; per-stage parity ~1e-5). Also: GPT-OSS-20B greedy decode through sdpa_decode_sink_buf produces coherent output (156/200 unique tokens).

Representative bench (tile bench -f sdpa_rel_pos_conformer, M1 Max), showing the GFLOP/s column from the .flops() annotation:

  Shape         │  MT(µs) │ MT(GB/s) │ GFLOP/s │ ok
  N=512K f32    │  3536.5 │      2.4 │   455.4 │  ✓
  N=512K f16    │  1792.9 │      2.3 │   898.3 │  ✓
  N=512K bf16   │  2605.0 │      1.6 │   618.3 │  ✓

AI assistance

This PR used AI assistance (Claude) under human direction and review — spanning kernel design, DSL authoring, CPU-oracle tests, the dead-strip root-cause analysis, the adain1d NaN diagnosis, the bench annotations, and this description. Each kernel was validated against its CPU reference before inclusion.

@github-actions github-actions Bot added the feature New feature label Jun 1, 2026
@ekryski ekryski closed this Jun 2, 2026
@ekryski ekryski reopened this Jun 2, 2026
@ekryski

ekryski commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

This is still following the current messy kernel convention I think I'd like to merge #247, then this PR, and then do a pass on clean up similar to what #251 is doing.

@ekryski
ekryski marked this pull request as ready for review June 2, 2026 03:15
@ekryski
ekryski force-pushed the ek/transpose-tokens-heads-kernel branch from c3b62b6 to 223aafc Compare June 3, 2026 04:17
ekryski added a commit that referenced this pull request Jun 3, 2026
## Summary

Lands `docs/KERNEL_ORGANIZATION.md` as a standalone, **docs-only**
change so it can be reviewed and discussed without wading through
kernel-implementation diffs.

The doc is a **proposal** for `metaltile-std`'s kernel source layout —
file granularity, the canonical per-kernel file shape (kernel →
`kernel_tests` → `kernel_benches`), and a family-by-family migration
plan that intentionally avoids a big-bang pass. It coordinates with (but
is orthogonal to) the CLI subprocess-rewrite spec: that spec governs the
crate/CLI architecture; this one governs the kernel files inside
`metaltile-std`.

## Why split it out

It was originally written alongside the vision/audio/TTS/video kernel
batch in #252. Pulling it into its own PR lets reviewers evaluate the
proposed organization on its own merits and land it independently — #252
drops the file in the same change.

## Changes

- Add `docs/KERNEL_ORGANIZATION.md` (extracted verbatim from #252).
- Index it under **Design & planning** in `docs/README.md`.

No code changes.
0xClandestine pushed a commit that referenced this pull request Jun 3, 2026
## Summary

Lands `docs/KERNEL_ORGANIZATION.md` as a standalone, **docs-only**
change so it can be reviewed and discussed without wading through
kernel-implementation diffs.

The doc is a **proposal** for `metaltile-std`'s kernel source layout —
file granularity, the canonical per-kernel file shape (kernel →
`kernel_tests` → `kernel_benches`), and a family-by-family migration
plan that intentionally avoids a big-bang pass. It coordinates with (but
is orthogonal to) the CLI subprocess-rewrite spec: that spec governs the
crate/CLI architecture; this one governs the kernel files inside
`metaltile-std`.

## Why split it out

It was originally written alongside the vision/audio/TTS/video kernel
batch in #252. Pulling it into its own PR lets reviewers evaluate the
proposed organization on its own merits and land it independently — #252
drops the file in the same change.

## Changes

- Add `docs/KERNEL_ORGANIZATION.md` (extracted verbatim from #252).
- Index it under **Design & planning** in `docs/README.md`.

No code changes.
ekryski added 20 commits June 3, 2026 13:05
… attention

Grid3D element-copy kernel reshaping [n_tokens, n_heads, head_dim]
token-major to [n_heads, n_tokens, head_dim] head-major, so a vision /
audio tower's attention can stay GPU-resident: stage-1 (per-head RMSNorm +
ffai_rope_2d) emits token-major Q/K/V, but ffai_sdpa_bidirectional reads
K/V head-major. Paired #[test_kernel] oracle, exact (tol 0) across
f32/f16/bf16; bench at the SigLIP production shape (576×16×64).
… linears

ffai_clamp_scalar: out[i] = min(max(x[i], lo), hi) with lo/hi as 1-element
f32 buffers (runtime scalars). Gemma 4's Gemma4ClippableLinear (vision tower
+ audio encoder, use_clipped_linears=True) clamps each projection's input
and output to per-linear scalar bounds from the checkpoint; this applies one
on the GPU so the attention pipeline stays GPU-resident. Grid3D, paired
oracle test exact across f32/f16/bf16.
…rnel

ffai_resize_normalize: the one GPU op every VL preprocess needs — bilinearly
resample an interleaved [src_h,src_w,3] [0,1] image to target_h×target_w,
normalize (v-mean[c])/std[c] per channel, write planar NCHW [3,th,tw], in one
Grid3D dispatch (replaces the CPU triple-loop resize + normalize). align_corners
=false half-pixel sampling (HF convention); runtime u32 dim buffers so one
kernel serves every variable-resolution size. Paired oracle test vs a CPU
bilinear reference — max|Δ| 2.4e-7 f32, exact f16/bf16.
…d to FFAI

The emit pipeline enumerates via the #[bench] registry; without a
kernel_benches module the kernel built + passed correctness but wasn't emitted
into the metallib/Swift wrappers. Add the bench (640x480->448x448).
Channel-last sibling of depthwise_conv2d for the FastVLM / FastViTHD
(MobileCLIP) vision stem, which keeps its whole feature pyramid in NHWC —
wrapping the NCHW kernel would force a transpose around every depthwise conv.
Same Grid3D one-thread-per-output-element structure, NHWC input/output
indexing (channel fastest), independent k_h/k_w (FastViT mixes square +
non-square depthwise). kernel_tests (3x3 s1/s2 + 7x7 non-square) pass GPU
correctness 9/9 (max|Δ| <= 4e-6 f32); kernel_benches included so it emits.
ffai_sdpa_bidirectional_windowed_d80 — each query attends only the keys in
its own contiguous window [seg_start[i], seg_start[i]+seg_len[i]) instead of
the full [0,n_kv). The variant Qwen2.5-VL's windowed-attention blocks (and
the MiniCPM-V resampler) need: caller permutes tokens into window order so a
window is one slice, passes per-query seg_start/seg_len. Verbatim copy of
sdpa_bidirectional_d80's online-softmax + cross-simdgroup reduction (same
ragged 3-elem d80 layout, same TPG=1024 reduction dispatch — no freeze
hazard), only the K walk is bounded to the segment. kernel_tests (uniform +
ragged windows) pass GPU correctness 6/6 (max|Δ| 7e-7 f32); benches incl.
ffai_sdpa_rel_pos_conformer_d128 — the ESPnet/Transformer-XL
RelPositionMultiHeadedAttention the Conformer acoustic encoders use
(Parakeet, FireRedASR2, GraniteSpeech, Gemma4Audio). Score =
scale·((Q+u_h)·K + (Q+v_h)·rel_emb[dist]) before bidirectional softmax —
a CONTENT-DEPENDENT position term (per-distance embedding vector dotted with
the query), unlike the scalar bias[head,rel] of sdpa_bidirectional_d128_relpos.
Folds q/u/v by scale at the load. GraniteSpeech is the u=v=0 special case.
Models on the d128_relpos reduction structure (TPG=1024, 4-elem lanes). Both
oracle cases (full u/v + zero-uv) pass GPU correctness 6/6 (max|Δ| 1.4e-6 f32).
avg_pool2d_nhwc — k_h×k_w box average with stride, channel-last, for the
Gemma 4 vision pool + tile-model downsampling (replaces a CPU pool loop).
count_include_pad=false so edge windows divide by their real tap count.
Grid3D one-thread-per-output. 6/6 GPU correctness (exact for in-budget).
frame_diff_luma — box-downscale two RGB frames to BT.601 luma and write
|luma0-luma1| per cell. Summing gives a global motion score to gate the
real-time video tower (skip near-static frames; process only on motion) so a
30fps capture pays tower cost only a few times/sec (multi-modal §7c). Grid3D
one-thread-per-output-cell. 6/6 GPU correctness.
mel_spectrogram_magnitude — |STFT| (amplitude) front-end: sqrt(re²+im²)
through the Mel filterbank, vs the power (re²+im²) front-end of
mel_spectrogram. The Gemma 4 audio encoder + several streaming-ASR
front-ends are magnitude-trained; feeding power degrades them. Replaces the
Gemma 4 CPU DFT. Direct-DFT Grid3D one-thread-per-(frame,mel). 3/3 GPU
correctness (looser f32 tol — sqrt+log amplifies the benign DFT
accumulation-order delta).
im2col_patch — unfold an NCHW image into one row per non-overlapping
patch×patch block, flattened (c,py,px), feeding the patch-embed GEMM.
Replaces the scalar CPU unfold (Gemma 4 unfoldPatches + any patch-embed
tower) with a single GPU gather. Handles non-square (variable-resolution)
grids. Grid3D one-thread-per-output. 6/6 GPU correctness (exact).
pos_emb_2d_add — out[patch,d] = tokens[patch,d] + pos_x[col,d] + pos_y[row,d]
(col=patch%grid_w, row=patch/grid_w), the post-patch-embed position step for
Gemma 4 + factorized-2D-pos towers (replaces a CPU loop). Variable-resolution
towers interpolate the learned tables to grid_w/grid_h first, then this does
the gather+add. Grid3D one-thread-per-output. 6/6 GPU correctness (exact).
ffai_sdpa_decode_sink_buf — decode SDPA reading a PER-HEAD learned attention
sink from a [n_q_heads] buffer (load sink[q_head]), vs sdpa_decode's scalar
sink_logit constexpr that forces a per-head host sync. All heads run in one
dispatch; removes the host-side per-layer sink sync in GPTOSSAttention.
The sink is a virtual key (score sink[q_head], value 0) folded into the
softmax denominator. Adds ragged bounds-masking (4 elems/lane, masked) so it
covers GPT-OSS d64 AND d128 in one kernel — sink-token range + sliding window
preserved. 6/6 GPU correctness (d64+d128, max|Δ| 4.8e-7 f32).
…ant)

ffai_resize_normalize_bicubic — higher-quality sibling of
ffai_resize_normalize: a 4×4 cubic-convolution sampler (Keys, a=-0.75)
instead of 2×2 bilinear, preserving edges far better when down-scaling a
large photo to a tower's native grid (where bilinear aliases). Same fused
interface (interleaved→normalize→planar NCHW, runtime u32 dim buffers, one
Grid3D dispatch). Matches torch interpolate(mode='bicubic',
align_corners=False): half-pixel centers, 4 taps floor(src)-1..+2, edge-clamp
per tap, partition-of-unity weights (no renormalize). Per-tap branch
specialization avoids a runtime select and is exact at boundaries. 6/6 GPU
correctness (down + up-scale, max|Δ| 2.7e-6 f32).
ffai_adain1d — per-row instance-normalize over the time axis then per-row
affine from the style vector: out[r,t] = gamma[r]·(x[r,t]-mean_r)·rsqrt(var_r
+eps) + beta[r]. The style-conditioning op all through the Kokoro acoustic
decoder + prosody/duration predictors (distinct from RMS/LayerNorm: it
normalizes across TIME per channel, scale/shift are per-row scalars).
Reduction kernel, one threadgroup per row (simd_sum + cross-simdgroup
combine), two passes (reduce, write). 9/9 GPU correctness (short + long seq).
ffai_lstm — runs the WHOLE LSTM sequence recurrence on the GPU in one
threadgroup (thread per hidden unit, h/c in threadgroup memory across
timesteps, barrier between state read/write). Complements the existing
kokoro.rs lstm_cell which does one step + host recurrence; this is the
'GPU from the start' BiLSTM the session-plan flagged — bidirectional = two
dispatches (forward out_offset 0, backward out_offset hidden) into a shared
[seq_len, 2*hidden] buffer. reverse flag for the backward walk; gate order
i,f,g,o; manual sigmoid/tanh via exp (exact tanh identity). 9 tests pass GPU
correctness (fwd/bwd/h40, max|Δ| 8e-7 f32).

Also removes the redundant standalone adain1d.rs added earlier — kokoro.rs
already ships an identical AdaIN1d kernel (same subop).
…d exp

The body parser exposes sigmoid/tanh (kernel/body.rs) and the codegen emits
the mt_sigmoid preamble + Metal's builtin tanh, so the gate activations
should use them directly (as kokoro.rs::lstm_cell already does) rather than
the manual 1/(1+exp(-x)) / 2/(1+exp(-2x))-1 forms. Clearer and marginally
more accurate. 15/15 GPU correctness.
Kernels shouldn't be filed under a model name (many models share them).
Split ffai/kokoro.rs:
  - adain1d kernel → adain1d.rs (op kokoro→norm); generic AdaIN doc.
  - lstm_cell kernel → lstm.rs alongside ffai_lstm (the per-step form next to
    the full-sequence form — cohesive LSTM file); op kokoro→lstm, and it now
    uses the sigmoid()/tanh() DSL intrinsics like ffai_lstm.
Kernel function names unchanged (adain1d, lstm_cell) so the FFAI by-name emit
is unaffected. 18 GPU-correctness tests pass (lstm 15, adain 3).
Rename + scrub the model name: op 'fishspeech_conv1d'→'conv1d', bench names
'ffai/fishspeech_conv1d/*'→'ffai/conv1d/*', doc reframed generically (the
dilated + transposed Conv1d a HiFi-GAN-style vocoder uses). Kernel fns
(conv1d_dilated, conv1d_transpose) unchanged → FFAI by-name emit unaffected.
15/15 GPU correctness. Also fixes the stale super::kokoro doc link in lstm.rs.
Cohesive-family grouping (no DSL reason to keep separate): the bicubic
sampler variant lives alongside the bilinear in resize_normalize.rs (shared
tests via cpu_ref_bicubic + reused u32_bytes). Kernel fns unchanged. 9/9 GPU
correctness.
ekryski added 15 commits June 3, 2026 13:05
The magnitude (|STFT|) front-end variant lives alongside the power front-end
(+ the FFT-route stft_window/filterbank) in mel_spectrogram.rs, sharing the
hann/triangular_filterbank/naive helpers. Kernel fns unchanged. 6/6 GPU
correctness.
Proposes the target metaltile-std kernel layout: organize by KERNEL FAMILY
(sdpa/, rope/, norm/, moe/, conv/, ssm/, quant/, core/, …) under a kernels/
umbrella, dissolving the ffai/ vs mlx/ split — which is organized by 'does MLX
have it', a per-bench attribute (mlx=/MetalRef) that's already optional and
losing value as we supersede MLX. Defines: one cohesive file per kernel family
(all dtype/bit-width/head-dim/shape variants together, with kernel+bench+test
spec); when a new file IS warranted (distinct algorithm, not a permutation);
the quant/ umbrella for the in-flight fp4/fp8/int8 + affine/aura/turbo work
(scheme=file, format=macro cell); the lane-packing macro as the prerequisite
that collapses the sdpa d-variants; no-model-names rule; and an incremental,
conflict-safe family-by-family migration plan. Not executed — proposal.
The v4 gist restructures the crate/CLI architecture (subprocess runner,
tile.toml, harness, dep-graph reduction); this spec restructures the kernel
files. They're orthogonal but touch the same surface (metaltile-std/src/lib.rs
+ every kernel file's test/bench imports), so add §10 'Coordination with v4':
v4 lands first, the kernel reorg applies on its end-state. Folds in v4's
constraints — top-level src = only lib.rs+utils.rs (kernels/ is a dir module,
fine); harness types move to metaltile::harness::{bench,test} (kernel-file
'use' updates); bench_types.rs deleted → dtype_label to utils.rs; probe/
deleted (not carried into kernels/); metaltile-std facade-only; MLX ref becomes
the ProtocolMessage ref_gbps field (reinforces §7). Net order: v4 → lane-pack
macro → family migration.
…actor)

Strip the legacy #[kernel(bench(...))] inline bench metadata from the
kernels added on this branch; the registry now sources bench specs from
the separate #[bench] kernel_benches modules (dev #228/#244). Test specs
already use in-source #[test_kernel]. Registry-consistency gates pass.
Adds dense-equivalent FLOP counts to the compute-bound kernels' benches so
the #247 roofline/GFLOP/s columns are populated: conformer rel-pos SDPA
(6·H·Nq·Nkv·D), windowed SDPA (4·H·Nq·win·D), decode sink (4·H·Nkv·D),
depthwise conv2d, and both LSTM variants. Memory-bound kernels (resize,
mel, im2col, pool, pos-emb, frame-diff, adain, transpose, clamp) leave it
blank per the spec convention.
…acuously

The cargo `kernel_tests_harness` only called `all_tests()`. With nothing
in the binary referencing the kernel code, macOS `-dead_strip` removed every
metaltile-std kernel translation unit — and with them the `#[test_kernel]`
inventory submissions — so `all_tests()` iterated zero entries and the test
passed while exercising nothing (runtime ~1.4s).

Reference `metaltile_std::all_kernels()` to anchor the kernel TUs (the same
anchor `kernel_registry_consistency` already relies on for `all_benches()`),
and assert both the kernel and test registries are non-empty so this can
never silently regress. The harness now runs all ~428 GPU #[test_kernel]
checks (~24s) under `cargo test --workspace`.
Annotate the kernels that do real arithmetic so the #247 roofline/AI/GFLOP-s
columns populate and the bottleneck verdict uses the principled AI-vs-ridge
path: mel_spectrogram + mel_spectrogram_magnitude (per-thread full DFT power
spectrum — compute-bound despite tiny bytes), avg_pool2d_nhwc (k² adds),
adain1d (mean/var/affine), resize_normalize bilinear (4-tap) + bicubic
(16-tap). Pure data-movement kernels (im2col, transpose, pos_emb_add, clamp,
frame_diff) stay blank — flops≈0 adds noise and the bandwidth/occupancy
fallback already labels them correctly.
The kernel-layout proposal now lands independently as a docs-only change
so it can be reviewed without the kernel-implementation diffs in this PR.
Removed here to avoid duplicating the file across two PRs.
…oro GPU vocoder

Three elementwise/gather kernels the StyleTTS2 decoder + iSTFTNet generator
need between convs to stay GPU-resident across thousands of frames:

- ffai_leaky_relu: out = x>0 ? x : slope*x (runtime f32 slope; the
  AdaIN res-block / generator activation, slope 0.2/0.1/0.01)
- ffai_snake1d: x + (1/a)*sin^2(a*x), learnable per-channel alpha over a
  channels-first [C, length] map (the BigVGAN/iSTFTNet periodic activation)
- ffai_upsample_nearest1d: nearest [C, in_len] -> [C, factor*in_len] gather
  (the AdainResBlk shortcut + F0/N predictor up-sample)

All Grid3D one-thread-per-element (no reduction TPG / freeze hazard), f32/
f16/bf16, with kernel_tests oracles (pass) and benches. Full make test green;
MSL compiles under xcrun metal.
…oder pool

ffai_conv1d_transpose_depthwise: ConvTranspose1d with groups == channels —
each output channel uses only its own input channel + per-channel kernel
[C, k]. The decoder AdainResBlk pool (ConvTranspose1d(C,C,3,stride=2,
padding=1,groups=C)) needs this; the dense conv1d_transpose sums over all
input channels. Gather/adjoint form, Grid3D one-thread-per-output, f32/f16/
bf16, kernel_tests oracle (pass) + bench.
Expose erf in the kernel prelude (the IR/macro already lower it to the
hardware erf) and add ffai_gelu_erf = 0.5*x*(1+erf(x/sqrt2)) — PyTorch
nn.GELU() default. The tanh-approx mt_gelu drifts enough over PLBERT's 12
shared layers to shift the prosody predictor's rounding-sensitive durations,
so the GPU front-end needs the exact form. Grid3D, f32/f16/bf16, test + bench.
…nstant rows)

The variance was computed as E[x^2] - E[x]^2, which suffers catastrophic f32
cancellation when a channel is near-constant over a long time axis (e.g. the
Kokoro iSTFTNet generator at ~7800 frames): the result can go slightly
negative and rsqrt(negative) is NaN, poisoning the whole utterance. The true
variance is >= 0, so clamp it. Matches the CPU two-pass reference.
…edictor.shared shape)

The existing tests cap at hidden 40 / seqLen 8. Add the production Kokoro
predictor.shared shape (hidden 256 = 8 full simdgroups, 65 steps, input 640)
to exercise the cross-simdgroup threadgroup recurrence over a long sequence.
Passes — the kernel is correct in isolation; the Kokoro full-chain NaN that
motivated this is context-dependent (FFAI known-issues.md).
The de-model-naming renames in this branch (fishspeech_conv1d ->
conv1d_dilated_transpose, resize_normalize_bicubic folded into
resize_normalize) collided with upstream additions that kept the old module
names; the rebase's union merge re-added the now-orphaned 'pub mod' decls.
Drop them (the files are gone) and repoint the upstream
fishspeech_conv1d_block_scaled doc comment at the renamed file.
@ekryski
ekryski force-pushed the ek/transpose-tokens-heads-kernel branch from 60d3b24 to 7281e93 Compare June 3, 2026 19:23
- gelu_erf test oracle now computes the Abramowitz-Stegun erf in f64 (its
  coefficients carry more than f32 precision, tripping
  clippy::excessive_precision), casting the result to f32.
- conv1d_transpose_depthwise oracle uses num.is_multiple_of(stride) instead
  of num % stride != 0 (clippy::manual_is_multiple_of).

cargo clippy --all-targets --all-features -- -D warnings runs clean.
@ekryski
ekryski merged commit ca778c6 into 0xClandestine:dev Jun 3, 2026
13 checks passed
@ekryski
ekryski deleted the ek/transpose-tokens-heads-kernel branch June 3, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant