[js/webgpu] Add DFT operator - #29454
Conversation
|
@microsoft-github-policy-service agree |
|
@guschmue , know this might take a bit, but was hoping a maintainer could kick off the CI checks, so we've got a place to start? |
|
@qjia7, just pinging to try and unblock this, since it currently has no reviewers and CI hasn't run yet. |
|
Thanks for the contribution. Could you also add the WebGPU implementation under the WebGPU provider directory? JSEP will be deprecated soon and replaced by the WASM + WebGPU architecture. |
This good? |
|
Sorry for the late response. I am OOF. Add @hariharans29 to take a look, thanks. |
There was a problem hiding this comment.
Pull request overview
Adds ONNX DFT (opset 17–20) support to the WebGPU (JSEP) execution path, enabling FFT/DFT workloads (audio/spectral preprocessing) to run on WebGPU instead of falling back to slower CPU paths.
Changes:
- Registers
DFTfor the WebGPU EP and implements a mixed-radix Stockham FFT with a direct-DFT fallback. - Adds corresponding JS/JSEP operator plumbing and a WebGPU TS implementation + test vectors.
- Extends CPU signal op tests and updates WebGPU operator documentation/listing.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| onnxruntime/test/providers/cpu/signal/signal_ops_test.cc | Adds additional DFT test coverage (prime length + zero-padding paths). |
| onnxruntime/core/providers/webgpu/webgpu_execution_provider.cc | Registers WebGPU DFT kernels for opset 17–20. |
| onnxruntime/core/providers/webgpu/math/dft.h | Declares WebGPU DFT kernel/program classes. |
| onnxruntime/core/providers/webgpu/math/dft.cc | Implements WebGPU DFT/FFT shaders and kernel compute logic. |
| onnxruntime/core/providers/js/operators/dft.h | Adds JS EP kernel attribute wiring for DFT. |
| onnxruntime/core/providers/js/operators/dft.cc | Registers DFT kernels for the JS EP (opset 17–20). |
| onnxruntime/core/providers/js/js_execution_provider.cc | Adds DFT to the JS EP kernel registry table. |
| js/web/test/suite-test-list.jsonc | Adds dft.jsonc to the web test suite list. |
| js/web/test/data/ops/dft.jsonc | New WebGPU test vectors for DFT forward/inverse/RFFT/IRFFT + opset-20 optional inputs. |
| js/web/lib/wasm/jsep/webgpu/ops/dft.ts | WebGPU implementation of DFT (FFT + direct fallback) in TS/WGSL. |
| js/web/lib/wasm/jsep/webgpu/op-resolve-rules.ts | Wires DFT into WebGPU op resolve rules. |
| js/web/docs/webgpu-operators.md | Documents DFT as supported by WebGPU. |
Comments suppressed due to low confidence (1)
js/web/lib/wasm/jsep/webgpu/ops/dft.ts:371
- DFT needs to reject non-positive transform lengths. In particular,
plan.length === 0can hang infactorizeToRadices()(since0 % radix === 0andremaining /= radixnever changes). Adding a simple runtime validation before attempting radix factorization avoids a potential infinite loop and prevents emitting invalid WGSL for negative lengths.
const plan = computeDftPlan(input, fftAxis, inverse, onesided, dftLength);
const useSharedMemoryFft = plan.length <= MAX_SHARED_MEMORY_LENGTH && factorizeToRadices(plan.length) !== undefined;
const programInfo = useSharedMemoryFft ? createFftProgramInfo(plan) : createDirectDftProgramInfo(plan);
e4bd9aa to
58716cb
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
onnxruntime/core/providers/webgpu/math/dft.cc:230
- In the IRFFT path,
spectrum()assumes that for every k >= uniforms.signal_length, the mirrored bin(N - k)is within the provided half-spectrum. If opset-20dft_lengthsets N such that the input half-spectrum is shorter thanfloor(N/2)+1,length_ - kcan be >= uniforms.signal_length andread_sample(length_ - k)will read past the end of the input. CPU DFT treats missing bins as zero-padding. Consider bounding the usable half-spectrum length and returning 0 for bins whose mirror is also out of range.
if (is_inverse_ && is_onesided_) {
// For IRFFT the spectrum is the Hermitian extension of the half-spectrum input.
shader.AdditionalImplementation()
<< "fn spectrum(in_base: u32, k: u32) -> vec2<f32> {\n"
<< " if (k < uniforms.signal_length) { return " << read_sample("k") << "; }\n"
<< " let h = " << read_sample(std::to_string(length_) + "u - k") << ";\n"
<< " return vec2<f32>(h.x, -h.y);\n"
<< "}\n";
js/web/lib/wasm/jsep/webgpu/ops/dft.ts:285
- In the IRFFT direct-DFT path,
spectrum()assumes the mirrored bin${length}u - kalways exists in the provided half-spectrum. If opset-20dft_lengthsets N such that the input half-spectrum is shorter thanfloor(N/2)+1,${length}u - kcan be >= uniforms.signalLength andreadSample()will index past the end of the input buffer. CPU DFT treats missing bins as zero-padding. Consider clamping the usable half-spectrum length and returning 0 when both k and its mirror are out of range.
// For IRFFT the spectrum is the Hermitian extension of the half-spectrum input.
const spectrum =
inverse && onesided
? `fn spectrum(inBase: u32, k: u32) -> vec2<f32> {
if (k < uniforms.signalLength) { return ${readSample('k')}; }
let h = ${readSample(`${length}u - k`)};
return vec2<f32>(h.x, -h.y);
}`
: `fn spectrum(inBase: u32, n: u32) -> vec2<f32> {
if (n < uniforms.signalLength) { return ${readSample('n')}; }
return vec2<f32>(0.0, 0.0);
}`;
Review: PR #29454 — [js/webgpu] Add DFT operator (head
|
|
Could you please manually resolve already addressed copilot comments ? Thanks |
Should be done! |
Thanks - it was resolved when I refreshed my page. I think there are 2 more copilot comments and some minor observations in my review comment. Can you please check if they are relevant / adressable ? |
Copilot comments #1-#4 on the validation issues are addressed. |
Head branch was pushed to by a user without write access
|
CI failures my fault this time. CPU-excluded radix-2 test cases now skipped explicitly with GTEST_SKIP if no eligible EP (now just WebGPU). |
Description
Implements the
DFToperator (opset 17–20) for the WebGPU (JSEP) execution provider: a batched shared-memory mixed-radix (2/3/4/5) Stockham FFT, with an O(N^2) direct-DFT fallback for non-5-smooth lengths. Handles real/complex input, forward/inverse (1/N-normalized), one-sided RFFT and one-sided-inverse IRFFT, and the opset-20dft_length/axistensor inputs — matching the semantics of the CPU kernel incore/providers/cpu/signal/dft.cc.Motivation and Context
The WebGPU EP had no DFT/FFT kernel, so spectral and audio-preprocessing models fall back to a slow path (#20997). This adds it to the JSEP EP. The op test suite was extended to cover the WebGPU backend; I also verified the kernel against ORT's CPU DFT on real WebGPU across the forward, inverse, RFFT, and IRFFT paths. As with other
onnxruntime-webop PRs, CI is the source of truth for the full web build.Closes #20997