[ExecuTorch][WebGPU] Add optimized conv2d op#20849
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20849
Note: Links to docs will display an error until the docs builds have been completed. ❌ 128 New Failures, 3 Unrelated Failures, 7 Unclassified FailuresAs of commit 5619098 with merge base c2b273e ( NEW FAILURES - The following jobs have failed:
UNCLASSIFIED FAILURES - DrCI could not classify the following jobs because the workflow did not run on the merge base. The failures may be pre-existing on trunk or introduced by this PR:
FLAKY - The following jobs failed but were likely due to flakiness present on trunk:
BROKEN TRUNK - The following job failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
Stack from ghstack (oldest at bottom):
Adds a general fp32 2D convolution kernel — direct and transposed — to the WebGPU backend, enabling the patch-embed / conv-stem and spatial-downsample layers of vision encoders (Florence-2 DaViT, SigLIP) to run GPU-accelerated in the browser.
Problem —
aten.convolution.defaulthad no WebGPU kernel, so any model with a convolutional stem or downsample block (the DaViT patch-embed and its strided downsample convs) could not lower into the delegate and broke the graph. Both the direct and the transposed forms serialize to the sameaten.convolution.defaultop (distinguished only by thetransposedarg), so a single handler has to cover both.Solution
conv2d_implhandler (registered once foraten.convolution.default) routes to three WGSL kernels by shape:conv2d_vec4.wgsl(vec4 fast path over input channels),conv2d.wgsl(scalar general path), andconv_transpose2d.wgsl(gather-form transposed conv). Thetransposedarg folds the transpose path into the same registration (a secondWEBGPU_REGISTER_OP(aten.convolution.default)would be silently dropped), and among the non-transposed kernels the host picks vec4 vs scalar from the input-channels-per-group count.Implementation
(b, oc, oh, ow)output element, looping over input-channel-per-group then(kh, kw), withcontinueguards skipping out-of-bounds padded taps;conv2d.wgslaccumulates scalarinput[...] * weight[...].icpg % 4 == 0(use_vec4in the handler); because NCHW's channel dim has strideIH*IW(not memory-contiguous),conv2d_vec4.wgslgathers four strided scalar loads into avec4<f32>and usesdot(in4, w4)— a register-packing vec4, not a coalesced load — cutting the input-channel loop trip count 4x. A real RGB stem (icpg=3) correctly falls to the scalar path.conv_transpose2d.wgslimplements the scatter-inversion as a gather: for each tap(kh, kw)an input row contributes only when(oh + pH - kh*dH)is divisible bysHand in range; weight is read in the un-flipped torch transposed layout[IC, OC/groups, KH, KW].ConvParamsuniform (16-byte-aligned); the handler parsesstride/padding/dilationint-lists viautils::parse_hw(broadcasting a single value to both spatial dims).utils::compute_dispatch_grid(workgroup clamped to the device max, default 256) with a 2D spill past the 65535 per-dimension ceiling; thestride_xoverride constant lets the shader decodei = gid.y*stride_x + gid.x. Pipeline creation, the uniform upload, the grid override constants, and the optional-bias binding go through the sharedutils::make_compute_pipeline,utils::make_uniform,utils::make_grid_constants, andutils::make_optional_bindinghelpers (a 4-byte dummy storage satisfies the bias binding when bias isNone, gated in WGSL byhas_bias).backends/vulkan/runtime/graph/ops/impl/Convolution.cpp(itsConv2dMethodspecializationsDepthwise/Pointwise/SlidingWindow/Transposedcollapse here into the vec4 / scalar / transpose routing).Constraints — fp32 only (bails if
nbytes != numel * sizeof(float)); 4D input/weight/output in NCHW;groupsmust divide bothICandOCandweight.dims[1]must equalIC/groups; output element count must fituint32for the (up to 2D) dispatch; non-zerooutput_paddingis rejected on the non-transposed path (andoutput_padding >= strideon the transposed path); the fusedet_vk.conv_with_clampvariant is not handled and would error clearly at load.Co-authored-with: Claude Code.
@exported-using-ghexport
Differential Revision: D110836669
Differential Revision: D110836669