Repository navigation
[WebGPU] Fix FP16 MatMulNBits wide-tile results on Qualcomm D3D12 - #33449
Open
Yang Gu (gyagp) wants to merge 2 commits into
Open
Yang Gu (gyagp) wants to merge 2 commits into
Yang Gu (gyagp) wants to merge 2 commits into
Conversation
Use direct workgroup-memory reads for FP16 wide-tile MatMulNBits on Qualcomm D3D12. The subgroup-shuffle path produces incorrect results for both 4-bit and 8-bit weights on Adreno. Keep other adapters, FP32 inputs, backends and optimized kernel selection unchanged. Reproduced with October 14 and October 19 ORT builds on webgfx-11. The existing tile tests cover 80 shape/bias/zero-point combinations. The corrected ARM64 provider-test suite passes 814 cases with 9 skips and no failures; selection and tolerances are unchanged. Independent ONNX/FP32-reference fixtures also pass for both bit widths, with unchanged DP4A controls. Configured C++ formatting checks pass.
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Bitcast vec4<f16> to vec2<u32> for subgroupShuffle and bitcast back. Keep the existing tile geometry, subgroup algorithm, storage and accumulation precision. Enable this only for Qualcomm D3D12 FP16 when subgroup shuffles are supported, and include it in the cache key. A primitive broadcast probe reproduces adjacent FP16 component swaps on Qualcomm with both ARM64 and x64 runtimes. Identical WGSL and x64 runtime/compiler binaries pass on AMD with the same 64-lane subgroup size; Intel and NVIDIA also pass. Packed u32 transport preserves every value. This avoids disabling the optimization while leaving other vendors' shader paths unchanged.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fix incorrect FP16
MatMulNBitsWideTileresults on Qualcomm D3D12 while preserving subgroup shuffles and the 32-row tile. Bitcast eachvec4<f16>tovec2<u32>forsubgroupShuffle, then bitcast the result back. This transports the exact FP16 bits and leaves storage, arithmetic and accumulation precision unchanged.Enable this only for Qualcomm + D3D12 + FP16, when subgroup shuffles are available. Include the choice in the shader cache key. Other vendors and backends retain their existing shader paths; the NVIDIA direct-read preference is unchanged. Test tolerances and selection are unchanged.
Motivation and Context
The affected path was introduced by #31703, commit
1bc68c1d25b374e13bbf9a0649441af1ad9f1501, merged August 13, 2026. The two tests added in #32796 expose the older issue:WebGpuMatMulNBitsTest.Float16_4Bits_SubgroupMatrixTilesWebGpuMatMulNBitsTest.Float16_8Bits_SubgroupMatrixTilesThey use synthetic operator inputs, not a language model. At
(M,N,K)=(32,128,64), independent reference checks found 2,675 INT4 and 2,677 INT8 mismatches out of 4,096 outputs. Both October 14 and October 19 runtimes reproduce the same errors.Isolating the cause
Rebuilding the before/after #31703 kernels with the same current ORT/Dawn/compiler dependencies, in two interleaved repetitions, gives zero mismatches before the optimization and the same failures afterward. This identifies the affected source change; the complete historical dependency stack was not rebuilt.
A separate subgroup-broadcast probe removes matrix arithmetic and checks known FP16 payloads loaded from storage and workgroup memory. Qualcomm returns adjacent components swapped:
[x,y,z,w] -> [y,x,w,z]. All 32,512 checked values per floating-point mode/source match that exact permutation. Packedu32transport preserves every value.u32shufflesThe AMD and Qualcomm x64 probes use identical WGSL bodies, ORT/DXC binaries and 64-lane subgroups. All source lane indices in the primitive probe are valid. The failure therefore reproduces independently of MatMul arithmetic and indexing; a 64-lane subgroup alone does not explain it, and it also reproduces outside the ARM64 build.
Reducing the tile to 16 rows, splitting vector loads, and widening FP16 values before a floating-point shuffle did not repair the error. Packed integer transport does, without disabling the optimization. Adreno validation used driver
31.0.133.1, Windows 11 build26300.9711.Attribution and workaround scope
A standalone D3D12 reproduction confirms the failure in Qualcomm's D3D12 driver/compiler/execution path. Handwritten HLSL was compiled once on the build host; Qualcomm and AMD then executed the exact same hash-verified DXIL, without ORT, Dawn/Tint or device-side DXC compilation. Both ran 64-lane waves for the shuffle variants. Every one of the 65,536 checked floating-point components per variant/source/repetition is swapped on Qualcomm; AMD has zero mismatches. Packed-integer shuffles and direct reads pass on both devices. Two interleaved repetitions agree.
The inspected DXIL loads and extracts components 0, 1, 2, 3, calls
waveReadLaneAt.f16on the corresponding values with valid source lanes, and stores the results in the original order. It contains no component permutation. This places the observed swap below the offline DXIL compilation boundary. The exact internal Qualcomm compiler pass or GPU instruction responsible is still unknown; a general ORT MatMul algorithm change is not indicated by this evidence.The new local Dawn end-to-end test,
SubgroupsF16ShuffleTests.DistinctComponents, also reproduces the problem on Dawn160806fc202be2725ca6535605ade24bc9721b1f(October 19). It uses distinct buffer-supplied FP16 values and exact CPU expectations, testing scalar, vector, widened-FP32 and packed-integer shuffles from storage and workgroup memory. Qualcomm has 6 failures / 2 passes / 0 skips; AMD has 8 passes / 0 failures / 0 skips, in each of two repetitions. This test is maintained in the local Dawn checkout and is not part of this ORT source diff.The direct-D3D12 shader uses
-E main -T cs_6_2 -HV 2021 -enable-16bit-types -O3. The storage-vector DXIL SHA-256 isb0ea63a9d0515f348b3d0e09b98e508d289b435d547f6f67577d62ac7b4a93cb. Qualcomm validation used Adreno X1-85 driver31.0.133.1; the AMD control used Radeon RX 9070 XT driver32.0.31007.2064.ORT #31703 introduced the path that exposes this failure on the tested stack. This PR provides an ORT-side compatibility workaround, scoped to Qualcomm D3D12 FP16. It preserves the subgroup algorithm by transporting the payload as integer bits:
No numerical conversion is involved. FP32 shuffles of values originating from FP16 also failed in the probe, so widening was insufficient; that observation does not establish a failure for arbitrary FP32 inputs.
Validation
Windows Release, based on
c1ea5bffb8with this patch. ORT DLLs and statically linked provider-test executables rebuilt for ARM64 and x64.git diff --check, and 23 local runner/build/patch-provenance checks pass.Targeted reproduction from the provider-test output directory: