Skip to content

WebGPU: Fix DequantizeLinear 1-D block_size handling - #32734

Merged
Jiajia Qin (qjia7) merged 1 commit into
mainfrom
copilot/issue-32730-autofix
Oct 14, 2026
Merged

Jiajia Qin (qjia7) merged 1 commit into
mainfrom
copilot/issue-32730-autofix

Conversation

@qjia7

@qjia7 Jiajia Qin (qjia7) commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Closes #32730

Summary

Corrected WebGPU rank-1 blocked DequantizeLinear classification and added an int8 regression test with zero-point.

Local tests

  • WebGPU provider-test build: passed (onnxruntime_provider_test built successfully.)
  • DequantizeLinearOp21BlockedTest: passed (37 tests ran and passed; SignedInt_UseZeroPoint_OneDimension executed successfully.)
  • lintrunner -a: passed (No lint issues; all patches applied successfully.)
  • Initial provider-test build: failed (diagnostic/superseded; the corrected build succeeded.)

This pr was prepared by compilot-cli.

Corrected WebGPU rank-1 blocked DequantizeLinear classification and added an int8 regression test with zero-point.

Local tests:
- python tools\ci_build\build.py --build_dir D:\jiajia\PR-Manager\.build-slot-pool\slots\ort-webgpu-1\build --config Release --build_shared_lib --use_webgpu --parallel --build --skip_tests --target onnxruntime_provider_test: passed (Reconfigured pooled slot for this worktree; onnxruntime_provider_test built successfully.)
- onnxruntime_provider_test.exe --gtest_filter="DequantizeLinearOp21BlockedTest.*": passed (37 tests ran and passed; SignedInt_UseZeroPoint_OneDimension executed successfully.)
- lintrunner -a: passed (No lint issues; all patches applied successfully.)
- Initial provider-test build with inherited Git configuration: failed (diagnostic/superseded) (Dawn version generation rejected an empty GIT_CONFIG_VALUE_3; retry with the malformed inherited entry removed succeeded.)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟒 Approval recommended

The focused fix matches the operator semantics and includes regression coverage for the reported failure.

Review effort: Balanced
Findings: None

What changed in this PR

Fixes WebGPU’s classification of rank-1 blocked DequantizeLinear inputs.

Changes:

  • Treats explicit positive block_size as blocked quantization.
  • Adds an int8 regression test with zero-point values.
File Description
onnxruntime/​core/​providers/​webgpu/​quantization/​quantize_linear.cc Corrects blocked-quantization classification.
onnxruntime/​test/​providers/​cpu/​tensor/​quantize_linear_test.cc Tests rank-1 blocked dequantization on WebGPU and CPU.

πŸ’‘ Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@qjia7 Jiajia Qin (qjia7) changed the title Fix #32730: [Web] WebGPU EP: DequantizeLinear with a 1-D input and block_size is treated as per-axis (wrong output, no error) WebGPU: Fix DequantizeLinear 1-D block_size handling Sep 25, 2026
@qjia7
Jiajia Qin (qjia7) marked this pull request as ready for review September 25, 2026 07:55
@qjia7
Jiajia Qin (qjia7) merged commit 7c1103e into main Oct 14, 2026
91 checks passed
@qjia7
Jiajia Qin (qjia7) deleted the copilot/issue-32730-autofix branch October 14, 2026 06:57
Edward Chen (edgchen1) added a commit that referenced this pull request Oct 27, 2026
### Description
<!-- Describe your changes. -->

This cherry-picks the following commits for the release:

| Commit ID | PR Number | Commit Title |
|-----------|-----------|-------------|
| 7c1103e | #32734 | WebGPU: Fix DequantizeLinear 1-D block_size
handling |
| 3b4a0ff | #33571 | Fix OrtMemoryInfo ABI mismatch across the plugin
EP boundary |

### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->

WebGPU EP 0.5.0 release.

---------

Co-authored-by: Jiajia Qin <jiajiaqin@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: adrastogi <aditya.rastogi@microsoft.com>
adrastogi added a commit that referenced this pull request Oct 30, 2026
### Description
Adds a WebGPU `QuantizeLinear` kernel and int8 support in `Cast`, and fixes byte-lane packing in the segmented (>`maxStorageBufferBindingSize`) output setters. All gaps were found by the W3C WebNN conformance (WPT) suite running on the WebGPU EP with CPU fallback disabled.

Commits:
1. **Add WebNN regression test for 1-D blocked uint8 DequantizeLinear.** Adds the WebNN vector "dequantizeLinear uint8 1D tensor with 1D scale, implicit block_size = 2" (#32730) for f32 and f16 scales, with `block_size` both explicit and inferred. #32734 fixed this path but only tested int8 with block_size 4.
2. **Add QuantizeLinear kernel.**
   - f32/f16 input, int8/uint8 output. Registered for opsets 10-12, 13-18, 19-20, 21-22 and 23-24. From opset 23, x / y_scale is computed in the `precision` type (default: the y_scale type). The kernel divides in f32, so opset 23/24 is registered for f32 `y_scale` only. Both GetCapability paths (in-tree `webgpu_execution_provider.cc` and plugin `ep/ep.cc`) leave nodes with an explicit non-f32 `precision` to another EP.
   - Each invocation writes one packed u32 word (4 outputs), so output bytes are never shared between invocations.
   - DequantizeLinear's per-tensor/per-axis/blocked classification and shape validation (including block_size inference and the element-wise axis 0, block_size 1 form) is factored into a helper used by both kernels.
   - Adds an `Int8x4` case to `ShaderVariableHelper::SetByOffset` that packs the low byte of each lane.
   - WGSL f32 division is only accurate to 2.5 ULP, which flips results near .5 (e.g. `QuantizeLinearOpTest.Int8`: -5 / 0.039215686 = -127.4999...). The shader recomputes the quotient with integer long division when 0.25 <= |q| < 1024, so round-half-to-even matches the CPU kernel bit-exactly.
3. **Support int8 in Cast.** Adds int8 to T1/T2. Input is unpacked with `unpack4xI8` (sign-extending) on both the generic and to-int64 paths; output uses the new Int8x4 packing, which keeps each lane's low byte like a narrowing conversion.
4. **Pack byte lanes in segmented output setters.** Outputs larger than `maxStorageBufferBindingSize` are written through a generated per-segment setter that stored the packed u32 value unchanged. Uint8x4/Int8x4 writers (Cast, Expand, Gather, QuantizeLinear) pass one value per lane, so those shaders failed to compile. The setter now takes `vec4<u32>`/`vec4<i32>` lanes and packs them like the unsegmented `SetByOffset`. This also fixes a latent uint8 bug on main for Cast/Expand/Gather.

Tests (WebGPU-pinned, CPU EP fallback disabled) are in `onnxruntime/test/providers/webgpu/tensor/{quantize_linear_test,cast_op_test}.cc`. They cover the WebNN conformance vectors, ties, saturation, per-axis, blocked (first/last axis, partial block), all opsets, `precision` routing, near-tie quotients against the CPU reference, Q -> DQ round trip, int8 casts to/from f32/f16/int32/int64/uint8/bool, and outputs spanning buffer segments.

Validation (Windows ARM64, NVIDIA RTX Spark N1X / D3D12, Release):
- `build.py --use_webgpu ... --target onnxruntime_provider_test`, then `onnxruntime_provider_test --gtest_filter="*WebGpu*:CastOpTest.*:*QuantizeLinear*:*DequantizeLinear*:ExpandOpTest.*:GatherOpTest.*"`: 1159 tests, 1147 passed, 0 failed, 12 skipped. The skips existed before this change and are unrelated: `QuantizeLinearOpTest.*_DML*` (DML only) and hardware/build-feature skips in `WebGpuMatMulAlgorithmTest`, `WebGpuMatMulNBitsTest.DP4A*` and `WebGpuContextTest.AdapterIndexRejectsUnsupportedBuild`.
  - New tests that ran and passed: `WebGpuQuantizeLinearTest.*` (including `OpsetVersions` 10-24, `Opset23PrecisionOtherThanFloat`, `Opset23ExplicitFloatPrecision`, `SegmentedOutput`), `CastOpTest.Int8ToOtherTypes_WebGpu`, `CastOpTest.OtherTypesToInt8_WebGpu`, `CastOpTest.ByteOutputsAcrossBufferSegments_WebGpu` and `WebGpuDequantizeLinearTest.*`.
  - The existing CPU `QuantizeLinearOpTest.*` suite also runs on WebGPU and passes, including `Int8`, which depends on the exact-quotient rounding.
- Plugin EP build: `build.py --use_webgpu shared_lib --cmake_extra_defines onnxruntime_BUILD_UNIT_TESTS=OFF --target onnxruntime_providers_webgpu` succeeds. This compiles the `ep/ep.cc` GetCapability change.
- Sensitivity check: without the fixes, `Opset23PrecisionOtherThanFloat` returned 5 and 3 (expected 4 and 2), and `SegmentedOutput` and `CastOpTest.ByteOutputsAcrossBufferSegments_WebGpu` failed.

Related work: this overlaps with @edgchen1's branch [`edgchen1/webgpu_quantizelinear`](https://github.com/microsoft/onnxruntime/tree/edgchen1/webgpu_quantizelinear) (last updated 2026-04-10, no PR yet). That branch also adds int4/uint4 QuantizeLinear output, which this PR does not cover; int4/uint4 is planned as a follow-up PR.

### Motivation and Context
WebNN's `quantizeLinear`, qdq subgraphs and int8 `cast` lower to ONNX `QuantizeLinear`/`Cast`, which had no WebGPU kernel (or no int8 type), so graph build failed with CPU fallback disabled.

WPT results (webgfx/webnn-test full suite, Edge Canary 158 arm64, NVIDIA RTX Spark N1X (D3D12), ORT 1.28.3 runtime, WebGPU plugin EP). Before = plugin built from main at 8120434; after = plugin with this change and four companion WebGPU PRs applied, attributed by op. This PR's ops fix 67 subtests (57 required):

| WPT test | Before | After | Notes |
|---|---|---|---|
| quantizeLinear | 4/29 | 15/29 | remaining 14 are optional int4/uint4/int32 outputs |
| qdq_subgraph | 1/43 | 43/43 | |
| dequantizeLinear | 25/33 | 27/33 | "quantizeLinear then dequantizeLinear" cases |
| cast | 38/51 | 50/51 | all int8 casts; remaining failure is a separate f32->f16 rounding issue |

For context, with this PR and its four companions applied, failing WPT subtests drop from 343 to 68 (required failures 275 -> 11).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Web] WebGPU EP: DequantizeLinear with a 1-D input and block_size is treated as per-axis (wrong output, no error)

4 participants