summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.cache.ts26
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts64
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.cache.ts24
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts57
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.cache.ts24
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts20
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts20
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.cache.ts24
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts57
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.cache.ts18
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts41
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.cache.ts25
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts52
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.cache.ts35
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.cache.ts32
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts63
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAdd.spec.ts4
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAnd.spec.ts4
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicCompareExchangeWeak.spec.ts18
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicExchange.spec.ts8
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicLoad.spec.ts4
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMax.spec.ts4
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMin.spec.ts4
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicOr.spec.ts4
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicStore.spec.ts8
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicSub.spec.ts4
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicXor.spec.ts4
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts7
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts837
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts1142
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts8
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts26
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts79
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts131
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts140
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.cache.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts57
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.cache.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts47
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts6
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts6
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts6
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.cache.ts25
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts79
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.cache.ts24
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts52
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/derivatives.cache.ts14
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/derivatives.ts215
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts99
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts112
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.cache.ts49
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts208
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.cache.ts118
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts238
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts74
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts59
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts16
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts16
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts16
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts16
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts16
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts16
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.cache.ts44
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts69
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.cache.ts44
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts69
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts20
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.cache.ts125
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts198
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts6
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts6
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.cache.ts26
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts61
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.cache.ts26
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts70
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.cache.ts50
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts81
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts103
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts304
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts18
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.cache.ts44
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts60
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.cache.ts61
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts94
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.cache.ts42
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts146
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.cache.ts30
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts62
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.cache.ts30
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts62
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.cache.ts18
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts106
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.cache.ts18
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts106
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.cache.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts210
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.cache.ts75
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts192
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.cache.ts25
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts88
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.cache.ts55
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts68
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts15
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts15
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts69
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts73
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts54
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts57
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.cache.ts24
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts67
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.cache.ts41
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts46
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.cache.ts18
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts44
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.cache.ts26
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts151
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.cache.ts116
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts191
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts6
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.cache.ts24
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts55
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.cache.ts18
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts129
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.cache.ts31
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts66
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.cache.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts57
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.cache.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts47
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.cache.ts25
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts71
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.cache.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts47
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.cache.ts41
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts66
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.cache.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts57
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.cache.ts18
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts41
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts160
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimensions.spec.ts518
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts99
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts22
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts23
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts809
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.cache.ts27
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts85
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.cache.ts17
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts39
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.cache.ts20
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts25
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.cache.ts20
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts25
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.cache.ts20
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts25
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.cache.ts20
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts25
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.cache.ts20
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts25
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts56
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts48
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupUniformLoad.spec.ts182
167 files changed, 7494 insertions, 4544 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.cache.ts
new file mode 100644
index 0000000000..9634ae8f34
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.cache.ts
@@ -0,0 +1,26 @@
+import { abstractInt } from '../../../../../util/conversion.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { absBigInt, fullI64Range } from '../../../../../util/math.js';
+import { CaseListBuilder, makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract_float|abstract_int]
+const cases: Record<string, CaseListBuilder> = {
+ ...(['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait === 'abstract' ? 'abstract_float' : trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ 'unfiltered',
+ FP[trait].absInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {}),
+ abstract_int: () => {
+ return fullI64Range().map(e => {
+ return { input: abstractInt(e), expected: abstractInt(absBigInt(e)) };
+ });
+ },
+};
+
+export const d = makeCaseCache('abs', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts
index 05d5242f73..75d41ab163 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts
@@ -1,14 +1,14 @@
export const description = `
Execution tests for the 'abs' builtin function
-S is AbstractInt, i32, or u32
+S is abstract-int, i32, or u32
T is S or vecN<S>
@const fn abs(e: T ) -> T
The absolute value of e. Component-wise when T is a vector. If e is a signed
integral scalar type and evaluates to the largest negative value, then the
result is e. If e is an unsigned integral type, then the result is e.
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn abs(e: T ) -> T
Returns the absolute value of e (e.g. e with a positive sign bit).
@@ -18,47 +18,26 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
import { kBit } from '../../../../../util/constants.js';
-import {
- i32Bits,
- TypeF32,
- TypeF16,
- TypeI32,
- TypeU32,
- u32Bits,
- TypeAbstractFloat,
-} from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range, fullF64Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type, i32Bits, u32Bits } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { d } from './abs.cache.js';
+import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('abs', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'unfiltered', FP.f32.absInterval);
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'unfiltered', FP.f16.absInterval);
- },
- abstract: () => {
- return FP.abstract.generateScalarToIntervalCases(
- fullF64Range(),
- 'unfiltered',
- FP.abstract.absInterval
- );
- },
-});
-
g.test('abstract_int')
.specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
.desc(`abstract int tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_int');
+ await run(t, abstractIntBuiltin('abs'), [Type.abstractInt], Type.abstractInt, t.params, cases);
+ });
g.test('u32')
.specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
@@ -67,7 +46,7 @@ g.test('u32')
u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
)
.fn(async t => {
- await run(t, builtin('abs'), [TypeU32], TypeU32, t.params, [
+ await run(t, builtin('abs'), [Type.u32], Type.u32, t.params, [
// Min and Max u32
{ input: u32Bits(kBit.u32.min), expected: u32Bits(kBit.u32.min) },
{ input: u32Bits(kBit.u32.max), expected: u32Bits(kBit.u32.max) },
@@ -114,7 +93,7 @@ g.test('i32')
u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
)
.fn(async t => {
- await run(t, builtin('abs'), [TypeI32], TypeI32, t.params, [
+ await run(t, builtin('abs'), [Type.i32], Type.i32, t.params, [
// Min and max i32
// If e evaluates to the largest negative value, then the result is e.
{ input: i32Bits(kBit.i32.negative.min), expected: i32Bits(kBit.i32.negative.min) },
@@ -166,8 +145,15 @@ g.test('abstract_float')
.combine('vectorize', [undefined, 2, 3, 4] as const)
)
.fn(async t => {
- const cases = await d.get('abstract');
- await run(t, abstractBuiltin('abs'), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases);
+ const cases = await d.get('abstract_float');
+ await run(
+ t,
+ abstractFloatBuiltin('abs'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
});
g.test('f32')
@@ -178,7 +164,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('abs'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('abs'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -192,5 +178,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('abs'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('abs'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.cache.ts
new file mode 100644
index 0000000000..466618b6db
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.cache.ts
@@ -0,0 +1,24 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ [...linearRange(-1, 1, 100), ...FP[trait].scalarRange()], // acos is defined on [-1, 1]
+ nonConst ? 'unfiltered' : 'finite',
+ // acos has an ulp or inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].acosInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('acos', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts
index 5755c07905..46089d8576 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'acos' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn acos(e: T ) -> T
Returns the arc cosine of e. Component-wise when T is a vector.
@@ -9,48 +9,33 @@ Returns the arc cosine of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { linearRange, fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { d } from './acos.cache.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
export const g = makeTestGroup(GPUTest);
-const f32_inputs = [
- ...linearRange(-1, 1, 100), // acos is defined on [-1, 1]
- ...fullF32Range(),
-];
-
-const f16_inputs = [
- ...linearRange(-1, 1, 100), // acos is defined on [-1, 1]
- ...fullF16Range(),
-];
-
-export const d = makeCaseCache('acos', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.acosInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.acosInterval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.acosInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.acosInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('acos'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -60,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('acos'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('acos'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -74,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('acos'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('acos'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.cache.ts
new file mode 100644
index 0000000000..587131140d
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.cache.ts
@@ -0,0 +1,24 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { biasedRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ [...biasedRange(1, 2, 100), ...FP[trait].scalarRange()], // x near 1 can be problematic to implement
+ nonConst ? 'unfiltered' : 'finite',
+ // acosh has an inherited accuracy, so is only expected to be as accurate as f32
+ ...FP[trait !== 'abstract' ? trait : 'f32'].acoshIntervals
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('acosh', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts
index cc78ce3eee..531a2479c6 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'acosh' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn acosh(e: T ) -> T
Returns the hyperbolic arc cosine of e. The result is 0 when e < 1.
@@ -13,47 +13,33 @@ Note: The result is not mathematically meaningful when e < 1.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { biasedRange, fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { d } from './acosh.cache.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
export const g = makeTestGroup(GPUTest);
-const f32_inputs = [
- ...biasedRange(1, 2, 100), // x near 1 can be problematic to implement
- ...fullF32Range(),
-];
-const f16_inputs = [
- ...biasedRange(1, 2, 100), // x near 1 can be problematic to implement
- ...fullF16Range(),
-];
-
-export const d = makeCaseCache('acosh', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', ...FP.f32.acoshIntervals);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', ...FP.f32.acoshIntervals);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', ...FP.f16.acoshIntervals);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', ...FP.f16.acoshIntervals);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('acosh'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -63,7 +49,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('acosh'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('acosh'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -77,5 +63,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('acosh'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('acosh'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts
index 9a2938c1d5..74e072703d 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts
@@ -10,15 +10,7 @@ Returns true if each component of e is true if e is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import {
- False,
- True,
- TypeBool,
- TypeVec,
- vec2,
- vec3,
- vec4,
-} from '../../../../../util/conversion.js';
+import { False, True, Type, vec2, vec3, vec4 } from '../../../../../util/conversion.js';
import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -36,14 +28,14 @@ g.test('bool')
.fn(async t => {
const overloads = {
scalar: {
- type: TypeBool,
+ type: Type.bool,
cases: [
{ input: False, expected: False },
{ input: True, expected: True },
],
},
vec2: {
- type: TypeVec(2, TypeBool),
+ type: Type.vec(2, Type.bool),
cases: [
{ input: vec2(False, False), expected: False },
{ input: vec2(True, False), expected: False },
@@ -52,7 +44,7 @@ g.test('bool')
],
},
vec3: {
- type: TypeVec(3, TypeBool),
+ type: Type.vec(3, Type.bool),
cases: [
{ input: vec3(False, False, False), expected: False },
{ input: vec3(True, False, False), expected: False },
@@ -65,7 +57,7 @@ g.test('bool')
],
},
vec4: {
- type: TypeVec(4, TypeBool),
+ type: Type.vec(4, Type.bool),
cases: [
{ input: vec4(False, False, False, False), expected: False },
{ input: vec4(False, True, False, False), expected: False },
@@ -88,5 +80,5 @@ g.test('bool')
};
const overload = overloads[t.params.overload];
- await run(t, builtin('all'), [overload.type], TypeBool, t.params, overload.cases);
+ await run(t, builtin('all'), [overload.type], Type.bool, t.params, overload.cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts
index 19ed7d186f..43c599e2aa 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts
@@ -10,15 +10,7 @@ Returns true if any component of e is true if e is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import {
- False,
- True,
- TypeBool,
- TypeVec,
- vec2,
- vec3,
- vec4,
-} from '../../../../../util/conversion.js';
+import { False, True, Type, vec2, vec3, vec4 } from '../../../../../util/conversion.js';
import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -36,14 +28,14 @@ g.test('bool')
.fn(async t => {
const overloads = {
scalar: {
- type: TypeBool,
+ type: Type.bool,
cases: [
{ input: False, expected: False },
{ input: True, expected: True },
],
},
vec2: {
- type: TypeVec(2, TypeBool),
+ type: Type.vec(2, Type.bool),
cases: [
{ input: vec2(False, False), expected: False },
{ input: vec2(True, False), expected: True },
@@ -52,7 +44,7 @@ g.test('bool')
],
},
vec3: {
- type: TypeVec(3, TypeBool),
+ type: Type.vec(3, Type.bool),
cases: [
{ input: vec3(False, False, False), expected: False },
{ input: vec3(True, False, False), expected: True },
@@ -65,7 +57,7 @@ g.test('bool')
],
},
vec4: {
- type: TypeVec(4, TypeBool),
+ type: Type.vec(4, Type.bool),
cases: [
{ input: vec4(False, False, False, False), expected: False },
{ input: vec4(False, True, False, False), expected: True },
@@ -88,5 +80,5 @@ g.test('bool')
};
const overload = overloads[t.params.overload];
- await run(t, builtin('any'), [overload.type], TypeBool, t.params, overload.cases);
+ await run(t, builtin('any'), [overload.type], Type.bool, t.params, overload.cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.cache.ts
new file mode 100644
index 0000000000..d9e6280e0d
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.cache.ts
@@ -0,0 +1,24 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ [...linearRange(-1, 1, 100), ...FP[trait].scalarRange()], // asin is defined on [-1, 1]
+ nonConst ? 'unfiltered' : 'finite',
+ // asin has an ulp or inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].asinInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('asin', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts
index 8d18ebb303..f368c3838c 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'asin' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn asin(e: T ) -> T
Returns the arc sine of e. Component-wise when T is a vector.
@@ -9,48 +9,33 @@ Returns the arc sine of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { linearRange, fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { d } from './asin.cache.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
export const g = makeTestGroup(GPUTest);
-const f32_inputs = [
- ...linearRange(-1, 1, 100), // asin is defined on [-1, 1]
- ...fullF32Range(),
-];
-
-const f16_inputs = [
- ...linearRange(-1, 1, 100), // asin is defined on [-1, 1]
- ...fullF16Range(),
-];
-
-export const d = makeCaseCache('asin', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.asinInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.asinInterval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.asinInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.asinInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('asin'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -60,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('asin'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('asin'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -74,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('asin'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('asin'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.cache.ts
new file mode 100644
index 0000000000..4ee66f1ea6
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.cache.ts
@@ -0,0 +1,18 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ 'unfiltered',
+ // asinh has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].asinhInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('asinh', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts
index 9a8384e090..60867a9bce 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'sinh' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn asinh(e: T ) -> T
Returns the hyperbolic arc sine of e.
@@ -12,32 +12,33 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { d } from './asinh.cache.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('asinh', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'unfiltered', FP.f32.asinhInterval);
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'unfiltered', FP.f16.asinhInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float test`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('asinh'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -47,7 +48,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('asinh'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('asinh'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -61,5 +62,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('asinh'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('asinh'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.cache.ts
new file mode 100644
index 0000000000..e39e40448e
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.cache.ts
@@ -0,0 +1,25 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+const known_values = [-Math.sqrt(3), -1, -1 / Math.sqrt(3), 0, 1, 1 / Math.sqrt(3), Math.sqrt(3)];
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ [...known_values, ...FP[trait].scalarRange()],
+ nonConst ? 'unfiltered' : 'finite',
+ // atan has an ulp accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].atanInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('atan', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts
index 3d0d3e6725..824a7cd0b5 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'atan' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn atan(e: T ) -> T
Returns the arc tangent of e. Component-wise when T is a vector.
@@ -10,43 +10,33 @@ Returns the arc tangent of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { d } from './atan.cache.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
export const g = makeTestGroup(GPUTest);
-const known_values = [-Math.sqrt(3), -1, -1 / Math.sqrt(3), 0, 1, 1 / Math.sqrt(3), Math.sqrt(3)];
-
-const f32_inputs = [...known_values, ...fullF32Range()];
-const f16_inputs = [...known_values, ...fullF16Range()];
-
-export const d = makeCaseCache('atan', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.atanInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.atanInterval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.atanInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.atanInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('atan'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -62,7 +52,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1]
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('atan'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('atan'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -76,5 +66,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('atan'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('atan'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.cache.ts
new file mode 100644
index 0000000000..be25c2dffb
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.cache.ts
@@ -0,0 +1,35 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ // Using sparse range since there are N^2 cases being generated, and also including extra values
+ // around 0, where there is a discontinuity that implementations may behave badly at.
+ const numeric_range = [
+ ...FP[trait].sparseScalarRange(),
+ ...linearRange(
+ FP[trait].constants().negative.max,
+ FP[trait].constants().positive.min,
+ 10
+ ),
+ ];
+ return FP[trait].generateScalarPairToIntervalCases(
+ numeric_range,
+ numeric_range,
+ nonConst ? 'unfiltered' : 'finite',
+ // atan2 has an ulp accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].atan2Interval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('atan2', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts
index fbace73dd2..067d1fdc66 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'atan2' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn atan2(e1: T ,e2: T ) -> T
Returns the arc tangent of e1 over e2. Component-wise when T is a vector.
@@ -9,47 +9,33 @@ Returns the arc tangent of e1 over e2. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { linearRange, sparseF32Range, sparseF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { d } from './atan2.cache.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
export const g = makeTestGroup(GPUTest);
-const cases = (['f32', 'f16'] as const)
- .flatMap(kind =>
- ([true, false] as const).map(nonConst => ({
- [`${kind}_${nonConst ? 'non_const' : 'const'}`]: () => {
- const fp = FP[kind];
- // Using sparse range since there are N^2 cases being generated, and also including extra values
- // around 0, where there is a discontinuity that implementations may behave badly at.
- const numeric_range = [
- ...(kind === 'f32' ? sparseF32Range() : sparseF16Range()),
- ...linearRange(fp.constants().negative.max, fp.constants().positive.min, 10),
- ];
- return fp.generateScalarPairToIntervalCases(
- numeric_range,
- numeric_range,
- nonConst ? 'unfiltered' : 'finite',
- fp.atan2Interval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-export const d = makeCaseCache('atan2', cases);
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get(`abstract_const`);
+ await run(
+ t,
+ abstractFloatBuiltin('atan2'),
+ [Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -65,7 +51,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1]
)
.fn(async t => {
const cases = await d.get(`f32_${t.params.inputSource === 'const' ? 'const' : 'non_const'}`);
- await run(t, builtin('atan2'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('atan2'), [Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -79,5 +65,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(`f16_${t.params.inputSource === 'const' ? 'const' : 'non_const'}`);
- await run(t, builtin('atan2'), [TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('atan2'), [Type.f16, Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.cache.ts
new file mode 100644
index 0000000000..bcd000bf61
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.cache.ts
@@ -0,0 +1,32 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { biasedRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ [
+ // discontinuity at x = -1
+ ...biasedRange(FP[trait].constants().negative.less_than_one, -0.9, 20),
+ -1,
+ // discontinuity at x = 1
+ ...biasedRange(FP[trait].constants().positive.less_than_one, 0.9, 20),
+ 1,
+ ...FP[trait].scalarRange(),
+ ],
+ nonConst ? 'unfiltered' : 'finite',
+ // atanh has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].atanhInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('atanh', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts
index 90f322a7ea..644efafd2f 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'atanh' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn atanh(e: T ) -> T
Returns the hyperbolic arc tangent of e. The result is 0 when abs(e) ≥ 1.
@@ -12,54 +12,33 @@ Note: The result is not mathematically meaningful when abs(e) >= 1.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { kValue } from '../../../../../util/constants.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { biasedRange, fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { d } from './atanh.cache.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
export const g = makeTestGroup(GPUTest);
-const f32_inputs = [
- ...biasedRange(kValue.f32.negative.less_than_one, -0.9, 20), // discontinuity at x = -1
- -1,
- ...biasedRange(kValue.f32.positive.less_than_one, 0.9, 20), // discontinuity at x = 1
- 1,
- ...fullF32Range(),
-];
-const f16_inputs = [
- ...biasedRange(kValue.f16.negative.less_than_one, -0.9, 20), // discontinuity at x = -1
- -1,
- ...biasedRange(kValue.f16.positive.less_than_one, 0.9, 20), // discontinuity at x = 1
- 1,
- ...fullF16Range(),
-];
-
-export const d = makeCaseCache('atanh', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.atanhInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.atanhInterval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.atanhInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.atanhInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('atanh'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -69,7 +48,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('atanh'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('atanh'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -83,5 +62,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('atanh'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('atanh'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAdd.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAdd.spec.ts
index 37d3ce5292..187a555449 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAdd.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAdd.spec.ts
@@ -35,7 +35,7 @@ fn atomicAdd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
u
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -72,7 +72,7 @@ fn atomicAdd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
u
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
// Allocate one extra element to ensure it doesn't get modified
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAnd.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAnd.spec.ts
index ed5cfa84a3..ad05bd851d 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAnd.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAnd.spec.ts
@@ -38,7 +38,7 @@ fn atomicAnd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -91,7 +91,7 @@ fn atomicAnd(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize;
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicCompareExchangeWeak.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicCompareExchangeWeak.spec.ts
index 2556bb744b..79e0597af6 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicCompareExchangeWeak.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicCompareExchangeWeak.spec.ts
@@ -48,7 +48,7 @@ struct __atomic_compare_exchange_result<T> {
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(async t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -187,7 +187,7 @@ struct __atomic_compare_exchange_result<T> {
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(async t => {
const numInvocations = t.params.workgroupSize;
@@ -333,12 +333,17 @@ struct __atomic_compare_exchange_result<T> {
.params(u =>
u
.combine('workgroupSize', onlyWorkgroupSizes) //
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(async t => {
const numInvocations = t.params.workgroupSize;
const scalarType = t.params.scalarType;
+ t.skipIf(
+ numInvocations > t.device.limits.maxComputeWorkgroupSizeX,
+ `${numInvocations} > maxComputeWorkgroupSizeX(${t.device.limits.maxComputeWorkgroupSizeX})`
+ );
+
// Number of times each workgroup attempts to exchange the same value to the same memory address
const numWrites = 4;
@@ -550,12 +555,17 @@ struct __atomic_compare_exchange_result<T> {
.params(u =>
u
.combine('workgroupSize', onlyWorkgroupSizes) //
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(async t => {
const numInvocations = t.params.workgroupSize;
const scalarType = t.params.scalarType;
+ t.skipIf(
+ numInvocations > t.device.limits.maxComputeWorkgroupSizeX,
+ `${numInvocations} > maxComputeWorkgroupSizeX(${t.device.limits.maxComputeWorkgroupSizeX})`
+ );
+
// Number of times each workgroup attempts to exchange the same value to the same memory address
const numWrites = 4;
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicExchange.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicExchange.spec.ts
index 540ac16b07..00b6ddb7e3 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicExchange.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicExchange.spec.ts
@@ -26,7 +26,7 @@ fn atomicExchange(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -125,7 +125,7 @@ fn atomicLoad(atomic_ptr: ptr<AS, atomic<T>, read_write>) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize;
@@ -236,7 +236,7 @@ fn atomicExchange(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(async t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -350,7 +350,7 @@ fn atomicLoad(atomic_ptr: ptr<AS, atomic<T>, read_write>) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(async t => {
const numInvocations = t.params.workgroupSize;
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicLoad.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicLoad.spec.ts
index 2aac7bb9b9..23ca127cae 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicLoad.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicLoad.spec.ts
@@ -26,7 +26,7 @@ fn atomicLoad(atomic_ptr: ptr<AS, atomic<T>, read_write>) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -117,7 +117,7 @@ fn atomicLoad(atomic_ptr: ptr<AS, atomic<T>, read_write>) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize;
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMax.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMax.spec.ts
index 066d673018..86bb8b460d 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMax.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMax.spec.ts
@@ -35,7 +35,7 @@ fn atomicMax(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
u
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -72,7 +72,7 @@ fn atomicMax(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
u
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
// Allocate one extra element to ensure it doesn't get modified
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMin.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMin.spec.ts
index ad880c4182..d9a42d15ee 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMin.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMin.spec.ts
@@ -35,7 +35,7 @@ fn atomicMin(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
u
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
// Allocate one extra element to ensure it doesn't get modified
@@ -71,7 +71,7 @@ fn atomicMin(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
u
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
// Allocate one extra element to ensure it doesn't get modified
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicOr.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicOr.spec.ts
index 3892d41b38..8ddba24385 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicOr.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicOr.spec.ts
@@ -38,7 +38,7 @@ fn atomicOr(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -90,7 +90,7 @@ fn atomicOr(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize;
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicStore.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicStore.spec.ts
index 18ff72975d..4d226e312b 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicStore.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicStore.spec.ts
@@ -32,7 +32,7 @@ fn atomicStore(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T)
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -72,7 +72,7 @@ fn atomicStore(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T)
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize;
@@ -117,7 +117,7 @@ one of the values written.
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(async t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -210,7 +210,7 @@ one of the values written.
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(async t => {
const numInvocations = t.params.workgroupSize;
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicSub.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicSub.spec.ts
index 6cea190299..8fdced1df2 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicSub.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicSub.spec.ts
@@ -35,7 +35,7 @@ fn atomicSub(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
u
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -72,7 +72,7 @@ fn atomicSub(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
u
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
// Allocate one extra element to ensure it doesn't get modified
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicXor.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicXor.spec.ts
index 99192fd9fe..2240043590 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicXor.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicXor.spec.ts
@@ -38,7 +38,7 @@ fn atomicXor(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize * t.params.dispatchSize;
@@ -91,7 +91,7 @@ fn atomicXor(atomic_ptr: ptr<AS, atomic<T>, read_write>, v: T) -> T
.combine('workgroupSize', workgroupSizes)
.combine('dispatchSize', dispatchSizes)
.combine('mapId', keysOf(kMapId))
- .combine('scalarType', ['u32', 'i32'])
+ .combine('scalarType', ['u32', 'i32'] as const)
)
.fn(t => {
const numInvocations = t.params.workgroupSize;
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts
index ed02467f80..e12cf537cd 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts
@@ -25,15 +25,14 @@ export const kMapId = {
},
};
-export function typedArrayCtor(scalarType: string): TypedArrayBufferViewConstructor {
+export function typedArrayCtor(
+ scalarType: 'u32' | 'i32'
+): TypedArrayBufferViewConstructor<Uint32Array | Int32Array> {
switch (scalarType) {
case 'u32':
return Uint32Array;
case 'i32':
return Int32Array;
- default:
- assert(false, 'Atomic variables can only by u32 or i32');
- return Uint8Array;
}
}
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts
new file mode 100644
index 0000000000..75dfea0086
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts
@@ -0,0 +1,837 @@
+import { assert } from '../../../../../../common/util/util.js';
+import { Comparator, alwaysPass, anyOf } from '../../../../../util/compare.js';
+import { kBit, kValue } from '../../../../../util/constants.js';
+import {
+ ScalarValue,
+ VectorValue,
+ f16,
+ f32,
+ i32,
+ toVector,
+ u32,
+ abstractFloat,
+ abstractInt,
+} from '../../../../../util/conversion.js';
+import { FP, FPInterval } from '../../../../../util/floating_point.js';
+import {
+ cartesianProduct,
+ fullI32Range,
+ fullU32Range,
+ isFiniteF16,
+ isFiniteF32,
+ isSubnormalNumberF16,
+ isSubnormalNumberF32,
+ linearRange,
+ scalarF16Range,
+ scalarF32Range,
+} from '../../../../../util/math.js';
+import {
+ reinterpretF16AsU16,
+ reinterpretF32AsI32,
+ reinterpretF32AsU32,
+ reinterpretI32AsF32,
+ reinterpretI32AsU32,
+ reinterpretU16AsF16,
+ reinterpretU32AsF32,
+ reinterpretU32AsI32,
+} from '../../../../../util/reinterpret.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+const numNaNs = 11;
+const f32InfAndNaNInU32: number[] = [
+ // Cover NaNs evenly in integer space.
+ // The positive NaN with the lowest integer representation is the integer
+ // for infinity, plus one.
+ // The positive NaN with the highest integer representation is i32.max (!)
+ ...linearRange(kBit.f32.positive.infinity + 1, kBit.i32.positive.max, numNaNs),
+ // The negative NaN with the lowest integer representation is the integer
+ // for negative infinity, plus one.
+ // The negative NaN with the highest integer representation is u32.max (!)
+ ...linearRange(kBit.f32.negative.infinity + 1, kBit.u32.max, numNaNs),
+ kBit.f32.positive.infinity,
+ kBit.f32.negative.infinity,
+];
+const f32InfAndNaNInF32 = f32InfAndNaNInU32.map(u => reinterpretU32AsF32(u));
+const f32InfAndNaNInI32 = f32InfAndNaNInU32.map(u => reinterpretU32AsI32(u));
+
+const f32ZerosInU32 = [0, kBit.f32.negative.zero];
+const f32ZerosInF32 = f32ZerosInU32.map(u => reinterpretU32AsF32(u));
+const f32ZerosInI32 = f32ZerosInU32.map(u => reinterpretU32AsI32(u));
+const f32ZerosInterval: FPInterval = new FPInterval('f32', -0.0, 0.0);
+
+// f32FiniteRange is a list of finite f32s. fullF32Range() already
+// has +0, we only need to add -0.
+const f32FiniteRange: number[] = [...scalarF32Range(), kValue.f32.negative.zero];
+const f32RangeWithInfAndNaN: number[] = [...f32FiniteRange, ...f32InfAndNaNInF32];
+
+// Type.f16 values, finite, Inf/NaN, and zeros. Represented in float and u16.
+const f16FiniteInF16: number[] = [...scalarF16Range(), kValue.f16.negative.zero];
+const f16FiniteInU16: number[] = f16FiniteInF16.map(u => reinterpretF16AsU16(u));
+
+const f16InfAndNaNInU16: number[] = [
+ // Cover NaNs evenly in integer space.
+ // The positive NaN with the lowest integer representation is the integer
+ // for infinity, plus one.
+ // The positive NaN with the highest integer representation is u16 0x7fff i.e. 32767.
+ ...linearRange(kBit.f16.positive.infinity + 1, 32767, numNaNs).map(v => Math.ceil(v)),
+ // The negative NaN with the lowest integer representation is the integer
+ // for negative infinity, plus one.
+ // The negative NaN with the highest integer representation is u16 0xffff i.e. 65535
+ ...linearRange(kBit.f16.negative.infinity + 1, 65535, numNaNs).map(v => Math.floor(v)),
+ kBit.f16.positive.infinity,
+ kBit.f16.negative.infinity,
+];
+const f16InfAndNaNInF16 = f16InfAndNaNInU16.map(u => reinterpretU16AsF16(u));
+
+const f16ZerosInU16 = [kBit.f16.negative.zero, 0];
+
+// f16 interval that match +/-0.0.
+const f16ZerosInterval: FPInterval = new FPInterval('f16', -0.0, 0.0);
+
+/**
+ * @returns an u32 whose lower and higher 16bits are the two elements of the
+ * given array of two u16 respectively, in little-endian.
+ */
+function u16x2ToU32(u16x2: readonly number[]): number {
+ assert(u16x2.length === 2);
+ // Create a DataView with 4 bytes buffer.
+ const buffer = new ArrayBuffer(4);
+ const view = new DataView(buffer);
+ // Enforce little-endian.
+ view.setUint16(0, u16x2[0], true);
+ view.setUint16(2, u16x2[1], true);
+ return view.getUint32(0, true);
+}
+
+/**
+ * @returns an array of two u16, respectively the lower and higher 16bits of
+ * given u32 in little-endian.
+ */
+function u32ToU16x2(u32: number): number[] {
+ // Create a DataView with 4 bytes buffer.
+ const buffer = new ArrayBuffer(4);
+ const view = new DataView(buffer);
+ // Enforce little-endian.
+ view.setUint32(0, u32, true);
+ return [view.getUint16(0, true), view.getUint16(2, true)];
+}
+
+/**
+ * @returns a vec2<f16> from an array of two u16, each reinterpreted as f16.
+ */
+function u16x2ToVec2F16(u16x2: number[]): VectorValue {
+ assert(u16x2.length === 2);
+ return toVector(u16x2.map(reinterpretU16AsF16), f16);
+}
+
+/**
+ * @returns a vec4<f16> from an array of four u16, each reinterpreted as f16.
+ */
+function u16x4ToVec4F16(u16x4: number[]): VectorValue {
+ assert(u16x4.length === 4);
+ return toVector(u16x4.map(reinterpretU16AsF16), f16);
+}
+
+/**
+ * @returns true if and only if a given u32 can bitcast to a vec2<f16> with all elements
+ * being finite f16 values.
+ */
+function canU32BitcastToFiniteVec2F16(u32: number): boolean {
+ return u32ToU16x2(u32)
+ .map(u16 => isFiniteF16(reinterpretU16AsF16(u16)))
+ .reduce((a, b) => a && b, true);
+}
+
+/**
+ * @returns an array of N elements with the i-th element being an array of len elements
+ * [a_i, a_((i+1)%N), ..., a_((i+len-1)%N)], for the input array of N element [a_1, ... a_N]
+ * and the given len. For example, slidingSlice([1, 2, 3], 2) result in
+ * [[1, 2], [2, 3], [3, 1]].
+ * This helper function is used for generating vector cases from scalar values array.
+ */
+function slidingSlice(input: number[], len: number) {
+ const result: number[][] = [];
+ for (let i = 0; i < input.length; i++) {
+ const sub: number[] = [];
+ for (let j = 0; j < len; j++) {
+ sub.push(input[(i + j) % input.length]);
+ }
+ result.push(sub);
+ }
+ return result;
+}
+
+// vec2<f16> interesting (zeros, Inf, and NaN) values for testing cases.
+// vec2<f16> values that has at least one Inf/NaN f16 element, reinterpreted as u32/i32.
+const f16Vec2InfAndNaNInU32 = [
+ ...cartesianProduct(f16InfAndNaNInU16, [...f16InfAndNaNInU16, ...f16FiniteInU16]),
+ ...cartesianProduct(f16FiniteInU16, f16InfAndNaNInU16),
+].map(u16x2ToU32);
+const f16Vec2InfAndNaNInI32 = f16Vec2InfAndNaNInU32.map(u => reinterpretU32AsI32(u));
+// vec2<f16> values with two f16 0.0 element, reinterpreted as u32/i32.
+const f16Vec2ZerosInU32 = cartesianProduct(f16ZerosInU16, f16ZerosInU16).map(u16x2ToU32);
+const f16Vec2ZerosInI32 = f16Vec2ZerosInU32.map(u => reinterpretU32AsI32(u));
+
+// i32/u32/f32 range for bitcasting to vec2<f16>
+// u32 values for bitcasting to vec2<f16> finite, Inf, and NaN.
+const u32RangeForF16Vec2FiniteInfNaN: number[] = [
+ ...fullU32Range(),
+ ...f16Vec2ZerosInU32,
+ ...f16Vec2InfAndNaNInU32,
+];
+// u32 values for bitcasting to finite only vec2<f16>, used for constant evaluation.
+const u32RangeForF16Vec2Finite: number[] = u32RangeForF16Vec2FiniteInfNaN.filter(
+ canU32BitcastToFiniteVec2F16
+);
+// i32 values for bitcasting to vec2<f16> finite, zeros, Inf, and NaN.
+const i32RangeForF16Vec2FiniteInfNaN: number[] = [
+ ...fullI32Range(),
+ ...f16Vec2ZerosInI32,
+ ...f16Vec2InfAndNaNInI32,
+];
+// i32 values for bitcasting to finite only vec2<f16>, used for constant evaluation.
+const i32RangeForF16Vec2Finite: number[] = i32RangeForF16Vec2FiniteInfNaN.filter(u =>
+ canU32BitcastToFiniteVec2F16(reinterpretI32AsU32(u))
+);
+// f32 values with finite/Inf/NaN f32, for bitcasting to vec2<f16> finite, zeros, Inf, and NaN.
+const f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN: number[] = [
+ ...f32RangeWithInfAndNaN,
+ ...u32RangeForF16Vec2FiniteInfNaN.map(reinterpretU32AsF32),
+];
+// Finite f32 values for bitcasting to finite only vec2<f16>, used for constant evaluation.
+const f32FiniteRangeForF16Vec2Finite: number[] = f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN
+ .filter(isFiniteF32)
+ .filter(u => canU32BitcastToFiniteVec2F16(reinterpretF32AsU32(u)));
+
+// vec2<f16> cases for bitcasting to i32/u32/f32, by combining f16 values into pairs
+const f16Vec2FiniteInU16x2 = slidingSlice(f16FiniteInU16, 2);
+const f16Vec2FiniteInfNanInU16x2 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 2);
+// vec4<f16> cases for bitcasting to vec2<i32/u32/f32>, by combining f16 values 4-by-4
+const f16Vec2FiniteInU16x4 = slidingSlice(f16FiniteInU16, 4);
+const f16Vec2FiniteInfNanInU16x4 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 4);
+
+// alwaysPass comparator for i32/u32/f32 cases. For f32/f16 we also use unbound interval, which
+// allow per-element unbounded expectation for vector.
+const anyF32 = alwaysPass('any f32');
+const anyI32 = alwaysPass('any i32');
+const anyU32 = alwaysPass('any u32');
+
+// Unbounded FPInterval
+const f32UnboundedInterval = FP.f32.constants().unboundedInterval;
+const f16UnboundedInterval = FP.f16.constants().unboundedInterval;
+
+// i32 and u32 cases for bitcasting to f32.
+// i32 cases for bitcasting to f32 finite, zeros, Inf, and NaN.
+const i32RangeForF32FiniteInfNaN: number[] = [
+ ...fullI32Range(),
+ ...f32ZerosInI32,
+ ...f32InfAndNaNInI32,
+];
+// i32 cases for bitcasting to f32 finite only.
+const i32RangeForF32Finite: number[] = i32RangeForF32FiniteInfNaN.filter(i =>
+ isFiniteF32(reinterpretI32AsF32(i))
+);
+// u32 cases for bitcasting to f32 finite, zeros, Inf, and NaN.
+const u32RangeForF32FiniteInfNaN: number[] = [
+ ...fullU32Range(),
+ ...f32ZerosInU32,
+ ...f32InfAndNaNInU32,
+];
+// u32 cases for bitcasting to f32 finite only.
+const u32RangeForF32Finite: number[] = u32RangeForF32FiniteInfNaN.filter(u =>
+ isFiniteF32(reinterpretU32AsF32(u))
+);
+
+/**
+ * @returns a Comparator for checking if a f32 value is a valid
+ * bitcast conversion from f32.
+ */
+function bitcastF32ToF32Comparator(f: number): Comparator {
+ if (!isFiniteF32(f)) return anyF32;
+ const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])];
+ return anyOf(...acceptable.map(f32));
+}
+
+/**
+ * @returns a Comparator for checking if a u32 value is a valid
+ * bitcast conversion from f32.
+ */
+function bitcastF32ToU32Comparator(f: number): Comparator {
+ if (!isFiniteF32(f)) return anyU32;
+ const acceptable: number[] = [
+ reinterpretF32AsU32(f),
+ ...(isSubnormalNumberF32(f) ? f32ZerosInU32 : []),
+ ];
+ return anyOf(...acceptable.map(u32));
+}
+
+/**
+ * @returns a Comparator for checking if a i32 value is a valid
+ * bitcast conversion from f32.
+ */
+function bitcastF32ToI32Comparator(f: number): Comparator {
+ if (!isFiniteF32(f)) return anyI32;
+ const acceptable: number[] = [
+ reinterpretF32AsI32(f),
+ ...(isSubnormalNumberF32(f) ? f32ZerosInI32 : []),
+ ];
+ return anyOf(...acceptable.map(i32));
+}
+
+/**
+ * @returns a Comparator for checking if a f32 value is a valid
+ * bitcast conversion from i32.
+ */
+function bitcastI32ToF32Comparator(i: number): Comparator {
+ const f: number = reinterpretI32AsF32(i);
+ if (!isFiniteF32(f)) return anyI32;
+ // Positive or negative zero bit pattern map to any zero.
+ if (f32ZerosInI32.includes(i)) return anyOf(...f32ZerosInF32.map(f32));
+ const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])];
+ return anyOf(...acceptable.map(f32));
+}
+
+/**
+ * @returns a Comparator for checking if a f32 value is a valid
+ * bitcast conversion from u32.
+ */
+function bitcastU32ToF32Comparator(u: number): Comparator {
+ const f: number = reinterpretU32AsF32(u);
+ if (!isFiniteF32(f)) return anyU32;
+ // Positive or negative zero bit pattern map to any zero.
+ if (f32ZerosInU32.includes(u)) return anyOf(...f32ZerosInF32.map(f32));
+ const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])];
+ return anyOf(...acceptable.map(f32));
+}
+
+/**
+ * @returns an array of expected f16 FPInterval for the given bitcasted f16 value, which may be
+ * subnormal, Inf, or NaN. Test cases that bitcasted to vector of f16 use this function to get
+ * per-element expectation and build vector expectation using cartesianProduct.
+ */
+function generateF16ExpectationIntervals(bitcastedF16Value: number): FPInterval[] {
+ // If the bitcasted f16 value is inf or nan, the result is unbounded
+ if (!isFiniteF16(bitcastedF16Value)) {
+ return [f16UnboundedInterval];
+ }
+ // If the casted f16 value is +/-0.0, the result can be one of both. Note that in JS -0.0 === 0.0.
+ if (bitcastedF16Value === 0.0) {
+ return [f16ZerosInterval];
+ }
+ const exactInterval = FP.f16.toInterval(bitcastedF16Value);
+ // If the casted f16 value is subnormal, it also may be flushed to +/-0.0.
+ return [exactInterval, ...(isSubnormalNumberF16(bitcastedF16Value) ? [f16ZerosInterval] : [])];
+}
+
+/**
+ * @returns a Comparator for checking if a f16 value is a valid
+ * bitcast conversion from f16.
+ */
+function bitcastF16ToF16Comparator(f: number): Comparator {
+ if (!isFiniteF16(f)) return anyOf(f16UnboundedInterval);
+ return anyOf(...generateF16ExpectationIntervals(f));
+}
+
+/**
+ * @returns a Comparator for checking if a vec2<f16> is a valid bitcast
+ * conversion from u32.
+ */
+function bitcastU32ToVec2F16Comparator(u: number): Comparator {
+ const bitcastedVec2F16InU16x2 = u32ToU16x2(u).map(reinterpretU16AsF16);
+ // Generate expection for vec2 f16 result, by generating expected intervals for each elements and
+ // then do cartesian product.
+ const expectedIntervalsCombination = cartesianProduct(
+ ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals)
+ );
+ return anyOf(...expectedIntervalsCombination);
+}
+
+/**
+ * @returns a Comparator for checking if a vec2<f16> value is a valid
+ * bitcast conversion from i32.
+ */
+function bitcastI32ToVec2F16Comparator(i: number): Comparator {
+ const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretI32AsU32(i)).map(reinterpretU16AsF16);
+ // Generate expection for vec2 f16 result, by generating expected intervals for each elements and
+ // then do cartesian product.
+ const expectedIntervalsCombination = cartesianProduct(
+ ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals)
+ );
+ return anyOf(...expectedIntervalsCombination);
+}
+
+/**
+ * @returns a Comparator for checking if a vec2<f16> value is a valid
+ * bitcast conversion from f32.
+ */
+function bitcastF32ToVec2F16Comparator(f: number): Comparator {
+ // If input f32 is not finite, it can be evaluated to any value and thus any result f16 vec2 is
+ // possible.
+ if (!isFiniteF32(f)) {
+ return anyOf([f16UnboundedInterval, f16UnboundedInterval]);
+ }
+ const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretF32AsU32(f)).map(reinterpretU16AsF16);
+ // Generate expection for vec2 f16 result, by generating expected intervals for each elements and
+ // then do cartesian product.
+ const expectedIntervalsCombination = cartesianProduct(
+ ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals)
+ );
+ return anyOf(...expectedIntervalsCombination);
+}
+
+/**
+ * @returns a Comparator for checking if a vec4<f16> is a valid
+ * bitcast conversion from vec2<u32>.
+ */
+function bitcastVec2U32ToVec4F16Comparator(u32x2: number[]): Comparator {
+ assert(u32x2.length === 2);
+ const bitcastedVec4F16InU16x4 = u32x2.flatMap(u32ToU16x2).map(reinterpretU16AsF16);
+ // Generate expection for vec4 f16 result, by generating expected intervals for each elements and
+ // then do cartesian product.
+ const expectedIntervalsCombination = cartesianProduct(
+ ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals)
+ );
+ return anyOf(...expectedIntervalsCombination);
+}
+
+/**
+ * @returns a Comparator for checking if a vec4<f16> is a valid
+ * bitcast conversion from vec2<i32>.
+ */
+function bitcastVec2I32ToVec4F16Comparator(i32x2: number[]): Comparator {
+ assert(i32x2.length === 2);
+ const bitcastedVec4F16InU16x4 = i32x2
+ .map(reinterpretI32AsU32)
+ .flatMap(u32ToU16x2)
+ .map(reinterpretU16AsF16);
+ // Generate expection for vec4 f16 result, by generating expected intervals for each elements and
+ // then do cartesian product.
+ const expectedIntervalsCombination = cartesianProduct(
+ ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals)
+ );
+ return anyOf(...expectedIntervalsCombination);
+}
+
+/**
+ * @returns a Comparator for checking if a vec4<f16> is a valid
+ * bitcast conversion from vec2<f32>.
+ */
+function bitcastVec2F32ToVec4F16Comparator(f32x2: number[]): Comparator {
+ assert(f32x2.length === 2);
+ const bitcastedVec4F16InU16x4 = f32x2
+ .map(reinterpretF32AsU32)
+ .flatMap(u32ToU16x2)
+ .map(reinterpretU16AsF16);
+ // Generate expection for vec4 f16 result, by generating expected intervals for each elements and
+ // then do cartesian product.
+ const expectedIntervalsCombination = cartesianProduct(
+ ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals)
+ );
+ return anyOf(...expectedIntervalsCombination);
+}
+
+// Structure that store the expectations of a single 32bit scalar/element bitcasted from two f16.
+interface ExpectionFor32BitsScalarFromF16x2 {
+ // possibleExpectations is Scalar array if the expectation is for i32/u32 and FPInterval array for
+ // f32. Note that if the expectation for i32/u32 is unbound, possibleExpectations is meaningless.
+ possibleExpectations: (ScalarValue | FPInterval)[];
+ isUnbounded: boolean;
+}
+
+/**
+ * @returns the array of possible 16bits, represented in u16, that bitcasted
+ * from a given finite f16 represented in u16, handling the possible subnormal
+ * flushing. Used to build up 32bits or larger results.
+ */
+function possibleBitsInU16FromFiniteF16InU16(f16InU16: number): number[] {
+ const h = reinterpretU16AsF16(f16InU16);
+ assert(isFiniteF16(h));
+ return [f16InU16, ...(isSubnormalNumberF16(h) ? f16ZerosInU16 : [])];
+}
+
+/**
+ * @returns the expectation for a single 32bit scalar bitcasted from given pair of
+ * f16, result in ExpectionFor32BitsScalarFromF16x2.
+ */
+function possible32BitScalarIntervalsFromF16x2(
+ f16x2InU16x2: number[],
+ type: 'i32' | 'u32' | 'f32'
+): ExpectionFor32BitsScalarFromF16x2 {
+ assert(f16x2InU16x2.length === 2);
+ let reinterpretFromU32: (x: number) => number;
+ let expectationsForValue: (x: number) => ScalarValue[] | FPInterval[];
+ let unboundedExpectations: FPInterval[] | ScalarValue[];
+ if (type === 'u32') {
+ reinterpretFromU32 = (x: number) => x;
+ expectationsForValue = x => [u32(x)];
+ // Scalar expectation can not express "unbounded" for i32 and u32, so use 0 here as a
+ // placeholder, and the possibleExpectations should be ignored if the result is unbounded.
+ unboundedExpectations = [u32(0)];
+ } else if (type === 'i32') {
+ reinterpretFromU32 = (x: number) => reinterpretU32AsI32(x);
+ expectationsForValue = x => [i32(x)];
+ // Scalar expectation can not express "unbounded" for i32 and u32, so use 0 here as a
+ // placeholder, and the possibleExpectations should be ignored if the result is unbounded.
+ unboundedExpectations = [i32(0)];
+ } else {
+ assert(type === 'f32');
+ reinterpretFromU32 = (x: number) => reinterpretU32AsF32(x);
+ expectationsForValue = x => {
+ // Handle the possible Inf/NaN/zeros and subnormal cases for f32 result.
+ if (!isFiniteF32(x)) {
+ return [f32UnboundedInterval];
+ }
+ // If the casted f16 value is +/-0.0, the result can be one of both. Note that in JS -0.0 === 0.0.
+ if (x === 0.0) {
+ return [f32ZerosInterval];
+ }
+ const exactInterval = FP.f32.toInterval(x);
+ // If the casted f16 value is subnormal, it also may be flushed to +/-0.0.
+ return [exactInterval, ...(isSubnormalNumberF32(x) ? [f32ZerosInterval] : [])];
+ };
+ unboundedExpectations = [f32UnboundedInterval];
+ }
+ // Return unbounded expection if f16 Inf/NaN occurs
+ if (
+ !isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[0])) ||
+ !isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[1]))
+ ) {
+ return { possibleExpectations: unboundedExpectations, isUnbounded: true };
+ }
+ const possibleU16Bits = f16x2InU16x2.map(possibleBitsInU16FromFiniteF16InU16);
+ const possibleExpectations = cartesianProduct(...possibleU16Bits).flatMap<
+ ScalarValue | FPInterval
+ >((possibleBitsU16x2: readonly number[]) => {
+ assert(possibleBitsU16x2.length === 2);
+ return expectationsForValue(reinterpretFromU32(u16x2ToU32(possibleBitsU16x2)));
+ });
+ return { possibleExpectations, isUnbounded: false };
+}
+
+/**
+ * @returns a Comparator for checking if a u32 value is a valid
+ * bitcast conversion from vec2 f16.
+ */
+function bitcastVec2F16ToU32Comparator(vec2F16InU16x2: number[]): Comparator {
+ assert(vec2F16InU16x2.length === 2);
+ const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'u32');
+ // Return alwaysPass if result is expected unbounded.
+ if (expectations.isUnbounded) {
+ return anyU32;
+ }
+ return anyOf(...expectations.possibleExpectations);
+}
+
+/**
+ * @returns a Comparator for checking if a i32 value is a valid
+ * bitcast conversion from vec2 f16.
+ */
+function bitcastVec2F16ToI32Comparator(vec2F16InU16x2: number[]): Comparator {
+ assert(vec2F16InU16x2.length === 2);
+ const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'i32');
+ // Return alwaysPass if result is expected unbounded.
+ if (expectations.isUnbounded) {
+ return anyI32;
+ }
+ return anyOf(...expectations.possibleExpectations);
+}
+
+/**
+ * @returns a Comparator for checking if a i32 value is a valid
+ * bitcast conversion from vec2 f16.
+ */
+function bitcastVec2F16ToF32Comparator(vec2F16InU16x2: number[]): Comparator {
+ assert(vec2F16InU16x2.length === 2);
+ const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'f32');
+ // Return alwaysPass if result is expected unbounded.
+ if (expectations.isUnbounded) {
+ return anyF32;
+ }
+ return anyOf(...expectations.possibleExpectations);
+}
+
+/**
+ * @returns a Comparator for checking if a vec2 u32 value is a valid
+ * bitcast conversion from vec4 f16.
+ */
+function bitcastVec4F16ToVec2U32Comparator(vec4F16InU16x4: number[]): Comparator {
+ assert(vec4F16InU16x4.length === 4);
+ const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e =>
+ possible32BitScalarIntervalsFromF16x2(e, 'u32')
+ );
+ // Return alwaysPass if any element is expected unbounded. Although it may be only one unbounded
+ // element in the result vector, currently we don't have a way to build a comparator that expect
+ // only one element of i32/u32 vector unbounded.
+ if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) {
+ return alwaysPass('any vec2<u32>');
+ }
+ return anyOf(
+ ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(
+ e => new VectorValue(e as ScalarValue[])
+ )
+ );
+}
+
+/**
+ * @returns a Comparator for checking if a vec2 i32 value is a valid
+ * bitcast conversion from vec4 f16.
+ */
+function bitcastVec4F16ToVec2I32Comparator(vec4F16InU16x4: number[]): Comparator {
+ assert(vec4F16InU16x4.length === 4);
+ const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e =>
+ possible32BitScalarIntervalsFromF16x2(e, 'i32')
+ );
+ // Return alwaysPass if any element is expected unbounded. Although it may be only one unbounded
+ // element in the result vector, currently we don't have a way to build a comparator that expect
+ // only one element of i32/u32 vector unbounded.
+ if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) {
+ return alwaysPass('any vec2<i32>');
+ }
+ return anyOf(
+ ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(
+ e => new VectorValue(e as ScalarValue[])
+ )
+ );
+}
+
+/**
+ * @returns a Comparator for checking if a vec2 f32 value is a valid
+ * bitcast conversion from vec4 f16.
+ */
+function bitcastVec4F16ToVec2F32Comparator(vec4F16InU16x4: number[]): Comparator {
+ assert(vec4F16InU16x4.length === 4);
+ const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e =>
+ possible32BitScalarIntervalsFromF16x2(e, 'f32')
+ );
+ return anyOf(
+ ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(e => [
+ e[0] as FPInterval,
+ e[1] as FPInterval,
+ ])
+ );
+}
+
+export const d = makeCaseCache('bitcast', {
+ // Identity Cases
+ i32_to_i32: () => fullI32Range().map(e => ({ input: i32(e), expected: i32(e) })),
+ u32_to_u32: () => fullU32Range().map(e => ({ input: u32(e), expected: u32(e) })),
+ f32_inf_nan_to_f32: () =>
+ f32RangeWithInfAndNaN.map(e => ({
+ input: f32(e),
+ expected: bitcastF32ToF32Comparator(e),
+ })),
+ f32_to_f32: () =>
+ f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToF32Comparator(e) })),
+ f16_inf_nan_to_f16: () =>
+ [...f16FiniteInF16, ...f16InfAndNaNInF16].map(e => ({
+ input: f16(e),
+ expected: bitcastF16ToF16Comparator(e),
+ })),
+ f16_to_f16: () =>
+ f16FiniteInF16.map(e => ({ input: f16(e), expected: bitcastF16ToF16Comparator(e) })),
+
+ // i32,u32,f32,Abstract to different i32,u32,f32
+ i32_to_u32: () => fullI32Range().map(e => ({ input: i32(e), expected: u32(e) })),
+ i32_to_f32: () =>
+ i32RangeForF32Finite.map(e => ({
+ input: i32(e),
+ expected: bitcastI32ToF32Comparator(e),
+ })),
+ ai_to_i32: () => fullI32Range().map(e => ({ input: abstractInt(BigInt(e)), expected: i32(e) })),
+ ai_to_u32: () => fullU32Range().map(e => ({ input: abstractInt(BigInt(e)), expected: u32(e) })),
+ ai_to_f32: () =>
+ // AbstractInt is converted to i32, because there is no explicit overload
+ i32RangeForF32Finite.map(e => ({
+ input: abstractInt(BigInt(e)),
+ expected: bitcastI32ToF32Comparator(e),
+ })),
+ i32_to_f32_inf_nan: () =>
+ i32RangeForF32FiniteInfNaN.map(e => ({
+ input: i32(e),
+ expected: bitcastI32ToF32Comparator(e),
+ })),
+ u32_to_i32: () => fullU32Range().map(e => ({ input: u32(e), expected: i32(e) })),
+ u32_to_f32: () =>
+ u32RangeForF32Finite.map(e => ({
+ input: u32(e),
+ expected: bitcastU32ToF32Comparator(e),
+ })),
+ u32_to_f32_inf_nan: () =>
+ u32RangeForF32FiniteInfNaN.map(e => ({
+ input: u32(e),
+ expected: bitcastU32ToF32Comparator(e),
+ })),
+ f32_inf_nan_to_i32: () =>
+ f32RangeWithInfAndNaN.map(e => ({
+ input: f32(e),
+ expected: bitcastF32ToI32Comparator(e),
+ })),
+ f32_to_i32: () =>
+ f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToI32Comparator(e) })),
+
+ f32_inf_nan_to_u32: () =>
+ f32RangeWithInfAndNaN.map(e => ({
+ input: f32(e),
+ expected: bitcastF32ToU32Comparator(e),
+ })),
+ f32_to_u32: () =>
+ f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToU32Comparator(e) })),
+
+ // i32,u32,f32,AbstractInt to vec2<f16>
+ u32_to_vec2_f16_inf_nan: () =>
+ u32RangeForF16Vec2FiniteInfNaN.map(e => ({
+ input: u32(e),
+ expected: bitcastU32ToVec2F16Comparator(e),
+ })),
+ u32_to_vec2_f16: () =>
+ u32RangeForF16Vec2Finite.map(e => ({
+ input: u32(e),
+ expected: bitcastU32ToVec2F16Comparator(e),
+ })),
+ i32_to_vec2_f16_inf_nan: () =>
+ i32RangeForF16Vec2FiniteInfNaN.map(e => ({
+ input: i32(e),
+ expected: bitcastI32ToVec2F16Comparator(e),
+ })),
+ i32_to_vec2_f16: () =>
+ i32RangeForF16Vec2Finite.map(e => ({
+ input: i32(e),
+ expected: bitcastI32ToVec2F16Comparator(e),
+ })),
+ ai_to_vec2_f16: () =>
+ // AbstractInt is converted to i32, because there is no explicit overload
+ i32RangeForF16Vec2Finite.map(e => ({
+ input: abstractInt(BigInt(e)),
+ expected: bitcastI32ToVec2F16Comparator(e),
+ })),
+ f32_inf_nan_to_vec2_f16_inf_nan: () =>
+ f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN.map(e => ({
+ input: f32(e),
+ expected: bitcastF32ToVec2F16Comparator(e),
+ })),
+ f32_to_vec2_f16: () =>
+ f32FiniteRangeForF16Vec2Finite.map(e => ({
+ input: f32(e),
+ expected: bitcastF32ToVec2F16Comparator(e),
+ })),
+ af_to_vec2_f16: () =>
+ f32FiniteRangeForF16Vec2Finite.map(e => ({
+ input: abstractFloat(e),
+ expected: bitcastF32ToVec2F16Comparator(e),
+ })),
+
+ // vec2<i32>, vec2<u32>, vec2<f32>, vec2<AbstractInt> to vec4<f16>
+ vec2_i32_to_vec4_f16_inf_nan: () =>
+ slidingSlice(i32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({
+ input: toVector(e, i32),
+ expected: bitcastVec2I32ToVec4F16Comparator(e),
+ })),
+ vec2_i32_to_vec4_f16: () =>
+ slidingSlice(i32RangeForF16Vec2Finite, 2).map(e => ({
+ input: toVector(e, i32),
+ expected: bitcastVec2I32ToVec4F16Comparator(e),
+ })),
+ vec2_ai_to_vec4_f16: () =>
+ // AbstractInt is converted to i32, because there is no explicit overload
+ slidingSlice(i32RangeForF16Vec2Finite, 2).map(e => ({
+ input: toVector(e, (n: number) => abstractInt(BigInt(n))),
+ expected: bitcastVec2I32ToVec4F16Comparator(e),
+ })),
+ vec2_u32_to_vec4_f16_inf_nan: () =>
+ slidingSlice(u32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({
+ input: toVector(e, u32),
+ expected: bitcastVec2U32ToVec4F16Comparator(e),
+ })),
+ vec2_u32_to_vec4_f16: () =>
+ slidingSlice(u32RangeForF16Vec2Finite, 2).map(e => ({
+ input: toVector(e, u32),
+ expected: bitcastVec2U32ToVec4F16Comparator(e),
+ })),
+ vec2_f32_inf_nan_to_vec4_f16_inf_nan: () =>
+ slidingSlice(f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN, 2).map(e => ({
+ input: toVector(e, f32),
+ expected: bitcastVec2F32ToVec4F16Comparator(e),
+ })),
+ vec2_f32_to_vec4_f16: () =>
+ slidingSlice(f32FiniteRangeForF16Vec2Finite, 2).map(e => ({
+ input: toVector(e, f32),
+ expected: bitcastVec2F32ToVec4F16Comparator(e),
+ })),
+ vec2_af_to_vec4_f16: () =>
+ slidingSlice(f32FiniteRangeForF16Vec2Finite, 2).map(e => ({
+ input: toVector(e, abstractFloat),
+ expected: bitcastVec2F32ToVec4F16Comparator(e),
+ })),
+
+ // vec2<f16> to i32, u32, f32
+ vec2_f16_to_u32: () =>
+ f16Vec2FiniteInU16x2.map(e => ({
+ input: u16x2ToVec2F16(e),
+ expected: bitcastVec2F16ToU32Comparator(e),
+ })),
+ vec2_f16_inf_nan_to_u32: () =>
+ f16Vec2FiniteInfNanInU16x2.map(e => ({
+ input: u16x2ToVec2F16(e),
+ expected: bitcastVec2F16ToU32Comparator(e),
+ })),
+ vec2_f16_to_i32: () =>
+ f16Vec2FiniteInU16x2.map(e => ({
+ input: u16x2ToVec2F16(e),
+ expected: bitcastVec2F16ToI32Comparator(e),
+ })),
+ vec2_f16_inf_nan_to_i32: () =>
+ f16Vec2FiniteInfNanInU16x2.map(e => ({
+ input: u16x2ToVec2F16(e),
+ expected: bitcastVec2F16ToI32Comparator(e),
+ })),
+ vec2_f16_to_f32_finite: () =>
+ f16Vec2FiniteInU16x2
+ .filter(u16x2 => isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x2))))
+ .map(e => ({
+ input: u16x2ToVec2F16(e),
+ expected: bitcastVec2F16ToF32Comparator(e),
+ })),
+ vec2_f16_inf_nan_to_f32: () =>
+ f16Vec2FiniteInfNanInU16x2.map(e => ({
+ input: u16x2ToVec2F16(e),
+ expected: bitcastVec2F16ToF32Comparator(e),
+ })),
+
+ // vec4<f16> to vec2 of i32, u32, f32
+ vec4_f16_to_vec2_u32: () =>
+ f16Vec2FiniteInU16x4.map(e => ({
+ input: u16x4ToVec4F16(e),
+ expected: bitcastVec4F16ToVec2U32Comparator(e),
+ })),
+ vec4_f16_inf_nan_to_vec2_u32: () =>
+ f16Vec2FiniteInfNanInU16x4.map(e => ({
+ input: u16x4ToVec4F16(e),
+ expected: bitcastVec4F16ToVec2U32Comparator(e),
+ })),
+ vec4_f16_to_vec2_i32: () =>
+ f16Vec2FiniteInU16x4.map(e => ({
+ input: u16x4ToVec4F16(e),
+ expected: bitcastVec4F16ToVec2I32Comparator(e),
+ })),
+ vec4_f16_inf_nan_to_vec2_i32: () =>
+ f16Vec2FiniteInfNanInU16x4.map(e => ({
+ input: u16x4ToVec4F16(e),
+ expected: bitcastVec4F16ToVec2I32Comparator(e),
+ })),
+ vec4_f16_to_vec2_f32_finite: () =>
+ f16Vec2FiniteInU16x4
+ .filter(
+ u16x4 =>
+ isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(0, 2)))) &&
+ isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(2, 4))))
+ )
+ .map(e => ({
+ input: u16x4ToVec4F16(e),
+ expected: bitcastVec4F16ToVec2F32Comparator(e),
+ })),
+ vec4_f16_inf_nan_to_vec2_f32: () =>
+ f16Vec2FiniteInfNanInU16x4.map(e => ({
+ input: u16x4ToVec4F16(e),
+ expected: bitcastVec4F16ToVec2F32Comparator(e),
+ })),
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts
index 390129f2c7..02acf98ef4 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts
@@ -11,6 +11,9 @@ S is i32, u32, f32
T is i32, u32, f32, and T is not S
Reinterpretation of bits. Beware non-normal f32 values.
+@const @must_use fn bitcast<u32>(e : Type.abstractInt) -> T
+@const @must_use fn bitcast<vecN<u32>>(e : vecN<Type.abstractInt>) -> T
+
@const @must_use fn bitcast<T>(e: vec2<f16> ) -> T
@const @must_use fn bitcast<vec2<T>>(e: vec4<f16> ) -> vec2<T>
@const @must_use fn bitcast<vec2<f16>>(e: T ) -> vec2<f16>
@@ -20,823 +23,26 @@ T is i32, u32, f32
import { TestParams } from '../../../../../../common/framework/fixture.js';
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
-import { assert } from '../../../../../../common/util/util.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { Comparator, alwaysPass, anyOf } from '../../../../../util/compare.js';
-import { kBit, kValue } from '../../../../../util/constants.js';
+import { anyOf } from '../../../../../util/compare.js';
import {
f32,
- i32,
u32,
- f16,
- TypeF32,
- TypeI32,
- TypeU32,
- TypeF16,
- TypeVec,
- Vector,
- Scalar,
- toVector,
+ i32,
+ abstractFloat,
+ uint32ToFloat32,
+ u32Bits,
+ Type,
} from '../../../../../util/conversion.js';
-import { FPInterval, FP } from '../../../../../util/floating_point.js';
-import {
- fullF32Range,
- fullI32Range,
- fullU32Range,
- fullF16Range,
- linearRange,
- isSubnormalNumberF32,
- isSubnormalNumberF16,
- cartesianProduct,
- isFiniteF32,
- isFiniteF16,
-} from '../../../../../util/math.js';
-import {
- reinterpretI32AsF32,
- reinterpretI32AsU32,
- reinterpretF32AsI32,
- reinterpretF32AsU32,
- reinterpretU32AsF32,
- reinterpretU32AsI32,
- reinterpretU16AsF16,
- reinterpretF16AsU16,
-} from '../../../../../util/reinterpret.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run, ShaderBuilder } from '../../expression.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { scalarF32Range } from '../../../../../util/math.js';
+import { ShaderBuilder, allInputSources, onlyConstInputSource, run } from '../../expression.js';
+import { d } from './bitcast.cache.js';
import { builtinWithPredeclaration } from './builtin.js';
export const g = makeTestGroup(GPUTest);
-const numNaNs = 11;
-const f32InfAndNaNInU32: number[] = [
- // Cover NaNs evenly in integer space.
- // The positive NaN with the lowest integer representation is the integer
- // for infinity, plus one.
- // The positive NaN with the highest integer representation is i32.max (!)
- ...linearRange(kBit.f32.positive.infinity + 1, kBit.i32.positive.max, numNaNs),
- // The negative NaN with the lowest integer representation is the integer
- // for negative infinity, plus one.
- // The negative NaN with the highest integer representation is u32.max (!)
- ...linearRange(kBit.f32.negative.infinity + 1, kBit.u32.max, numNaNs),
- kBit.f32.positive.infinity,
- kBit.f32.negative.infinity,
-];
-const f32InfAndNaNInF32 = f32InfAndNaNInU32.map(u => reinterpretU32AsF32(u));
-const f32InfAndNaNInI32 = f32InfAndNaNInU32.map(u => reinterpretU32AsI32(u));
-
-const f32ZerosInU32 = [0, kBit.f32.negative.zero];
-const f32ZerosInF32 = f32ZerosInU32.map(u => reinterpretU32AsF32(u));
-const f32ZerosInI32 = f32ZerosInU32.map(u => reinterpretU32AsI32(u));
-const f32ZerosInterval: FPInterval = new FPInterval('f32', -0.0, 0.0);
-
-// f32FiniteRange is a list of finite f32s. fullF32Range() already
-// has +0, we only need to add -0.
-const f32FiniteRange: number[] = [...fullF32Range(), kValue.f32.negative.zero];
-const f32RangeWithInfAndNaN: number[] = [...f32FiniteRange, ...f32InfAndNaNInF32];
-
-// F16 values, finite, Inf/NaN, and zeros. Represented in float and u16.
-const f16FiniteInF16: number[] = [...fullF16Range(), kValue.f16.negative.zero];
-const f16FiniteInU16: number[] = f16FiniteInF16.map(u => reinterpretF16AsU16(u));
-
-const f16InfAndNaNInU16: number[] = [
- // Cover NaNs evenly in integer space.
- // The positive NaN with the lowest integer representation is the integer
- // for infinity, plus one.
- // The positive NaN with the highest integer representation is u16 0x7fff i.e. 32767.
- ...linearRange(kBit.f16.positive.infinity + 1, 32767, numNaNs).map(v => Math.ceil(v)),
- // The negative NaN with the lowest integer representation is the integer
- // for negative infinity, plus one.
- // The negative NaN with the highest integer representation is u16 0xffff i.e. 65535
- ...linearRange(kBit.f16.negative.infinity + 1, 65535, numNaNs).map(v => Math.floor(v)),
- kBit.f16.positive.infinity,
- kBit.f16.negative.infinity,
-];
-const f16InfAndNaNInF16 = f16InfAndNaNInU16.map(u => reinterpretU16AsF16(u));
-
-const f16ZerosInU16 = [kBit.f16.negative.zero, 0];
-
-// f16 interval that match +/-0.0.
-const f16ZerosInterval: FPInterval = new FPInterval('f16', -0.0, 0.0);
-
-/**
- * @returns an u32 whose lower and higher 16bits are the two elements of the
- * given array of two u16 respectively, in little-endian.
- */
-function u16x2ToU32(u16x2: readonly number[]): number {
- assert(u16x2.length === 2);
- // Create a DataView with 4 bytes buffer.
- const buffer = new ArrayBuffer(4);
- const view = new DataView(buffer);
- // Enforce little-endian.
- view.setUint16(0, u16x2[0], true);
- view.setUint16(2, u16x2[1], true);
- return view.getUint32(0, true);
-}
-
-/**
- * @returns an array of two u16, respectively the lower and higher 16bits of
- * given u32 in little-endian.
- */
-function u32ToU16x2(u32: number): number[] {
- // Create a DataView with 4 bytes buffer.
- const buffer = new ArrayBuffer(4);
- const view = new DataView(buffer);
- // Enforce little-endian.
- view.setUint32(0, u32, true);
- return [view.getUint16(0, true), view.getUint16(2, true)];
-}
-
-/**
- * @returns a vec2<f16> from an array of two u16, each reinterpreted as f16.
- */
-function u16x2ToVec2F16(u16x2: number[]): Vector {
- assert(u16x2.length === 2);
- return toVector(u16x2.map(reinterpretU16AsF16), f16);
-}
-
-/**
- * @returns a vec4<f16> from an array of four u16, each reinterpreted as f16.
- */
-function u16x4ToVec4F16(u16x4: number[]): Vector {
- assert(u16x4.length === 4);
- return toVector(u16x4.map(reinterpretU16AsF16), f16);
-}
-
-/**
- * @returns true if and only if a given u32 can bitcast to a vec2<f16> with all elements
- * being finite f16 values.
- */
-function canU32BitcastToFiniteVec2F16(u32: number): boolean {
- return u32ToU16x2(u32)
- .map(u16 => isFiniteF16(reinterpretU16AsF16(u16)))
- .reduce((a, b) => a && b, true);
-}
-
-/**
- * @returns an array of N elements with the i-th element being an array of len elements
- * [a_i, a_((i+1)%N), ..., a_((i+len-1)%N)], for the input array of N element [a_1, ... a_N]
- * and the given len. For example, slidingSlice([1, 2, 3], 2) result in
- * [[1, 2], [2, 3], [3, 1]].
- * This helper function is used for generating vector cases from scalar values array.
- */
-function slidingSlice(input: number[], len: number) {
- const result: number[][] = [];
- for (let i = 0; i < input.length; i++) {
- const sub: number[] = [];
- for (let j = 0; j < len; j++) {
- sub.push(input[(i + j) % input.length]);
- }
- result.push(sub);
- }
- return result;
-}
-
-// vec2<f16> interesting (zeros, Inf, and NaN) values for testing cases.
-// vec2<f16> values that has at least one Inf/NaN f16 element, reinterpreted as u32/i32.
-const f16Vec2InfAndNaNInU32 = [
- ...cartesianProduct(f16InfAndNaNInU16, [...f16InfAndNaNInU16, ...f16FiniteInU16]),
- ...cartesianProduct(f16FiniteInU16, f16InfAndNaNInU16),
-].map(u16x2ToU32);
-const f16Vec2InfAndNaNInI32 = f16Vec2InfAndNaNInU32.map(u => reinterpretU32AsI32(u));
-// vec2<f16> values with two f16 0.0 element, reinterpreted as u32/i32.
-const f16Vec2ZerosInU32 = cartesianProduct(f16ZerosInU16, f16ZerosInU16).map(u16x2ToU32);
-const f16Vec2ZerosInI32 = f16Vec2ZerosInU32.map(u => reinterpretU32AsI32(u));
-
-// i32/u32/f32 range for bitcasting to vec2<f16>
-// u32 values for bitcasting to vec2<f16> finite, Inf, and NaN.
-const u32RangeForF16Vec2FiniteInfNaN: number[] = [
- ...fullU32Range(),
- ...f16Vec2ZerosInU32,
- ...f16Vec2InfAndNaNInU32,
-];
-// u32 values for bitcasting to finite only vec2<f16>, used for constant evaluation.
-const u32RangeForF16Vec2Finite: number[] = u32RangeForF16Vec2FiniteInfNaN.filter(
- canU32BitcastToFiniteVec2F16
-);
-// i32 values for bitcasting to vec2<f16> finite, zeros, Inf, and NaN.
-const i32RangeForF16Vec2FiniteInfNaN: number[] = [
- ...fullI32Range(),
- ...f16Vec2ZerosInI32,
- ...f16Vec2InfAndNaNInI32,
-];
-// i32 values for bitcasting to finite only vec2<f16>, used for constant evaluation.
-const i32RangeForF16Vec2Finite: number[] = i32RangeForF16Vec2FiniteInfNaN.filter(u =>
- canU32BitcastToFiniteVec2F16(reinterpretI32AsU32(u))
-);
-// f32 values with finite/Inf/NaN f32, for bitcasting to vec2<f16> finite, zeros, Inf, and NaN.
-const f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN: number[] = [
- ...f32RangeWithInfAndNaN,
- ...u32RangeForF16Vec2FiniteInfNaN.map(reinterpretU32AsF32),
-];
-// Finite f32 values for bitcasting to finite only vec2<f16>, used for constant evaluation.
-const f32FiniteRangeForF16Vec2Finite: number[] = f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN
- .filter(isFiniteF32)
- .filter(u => canU32BitcastToFiniteVec2F16(reinterpretF32AsU32(u)));
-
-// vec2<f16> cases for bitcasting to i32/u32/f32, by combining f16 values into pairs
-const f16Vec2FiniteInU16x2 = slidingSlice(f16FiniteInU16, 2);
-const f16Vec2FiniteInfNanInU16x2 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 2);
-// vec4<f16> cases for bitcasting to vec2<i32/u32/f32>, by combining f16 values 4-by-4
-const f16Vec2FiniteInU16x4 = slidingSlice(f16FiniteInU16, 4);
-const f16Vec2FiniteInfNanInU16x4 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 4);
-
-// alwaysPass comparator for i32/u32/f32 cases. For f32/f16 we also use unbound interval, which
-// allow per-element unbounded expectation for vector.
-const anyF32 = alwaysPass('any f32');
-const anyI32 = alwaysPass('any i32');
-const anyU32 = alwaysPass('any u32');
-
-// Unbounded FPInterval
-const f32UnboundedInterval = FP.f32.constants().unboundedInterval;
-const f16UnboundedInterval = FP.f16.constants().unboundedInterval;
-
-// i32 and u32 cases for bitcasting to f32.
-// i32 cases for bitcasting to f32 finite, zeros, Inf, and NaN.
-const i32RangeForF32FiniteInfNaN: number[] = [
- ...fullI32Range(),
- ...f32ZerosInI32,
- ...f32InfAndNaNInI32,
-];
-// i32 cases for bitcasting to f32 finite only.
-const i32RangeForF32Finite: number[] = i32RangeForF32FiniteInfNaN.filter(i =>
- isFiniteF32(reinterpretI32AsF32(i))
-);
-// u32 cases for bitcasting to f32 finite, zeros, Inf, and NaN.
-const u32RangeForF32FiniteInfNaN: number[] = [
- ...fullU32Range(),
- ...f32ZerosInU32,
- ...f32InfAndNaNInU32,
-];
-// u32 cases for bitcasting to f32 finite only.
-const u32RangeForF32Finite: number[] = u32RangeForF32FiniteInfNaN.filter(u =>
- isFiniteF32(reinterpretU32AsF32(u))
-);
-
-/**
- * @returns a Comparator for checking if a f32 value is a valid
- * bitcast conversion from f32.
- */
-function bitcastF32ToF32Comparator(f: number): Comparator {
- if (!isFiniteF32(f)) return anyF32;
- const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])];
- return anyOf(...acceptable.map(f32));
-}
-
-/**
- * @returns a Comparator for checking if a u32 value is a valid
- * bitcast conversion from f32.
- */
-function bitcastF32ToU32Comparator(f: number): Comparator {
- if (!isFiniteF32(f)) return anyU32;
- const acceptable: number[] = [
- reinterpretF32AsU32(f),
- ...(isSubnormalNumberF32(f) ? f32ZerosInU32 : []),
- ];
- return anyOf(...acceptable.map(u32));
-}
-
-/**
- * @returns a Comparator for checking if a i32 value is a valid
- * bitcast conversion from f32.
- */
-function bitcastF32ToI32Comparator(f: number): Comparator {
- if (!isFiniteF32(f)) return anyI32;
- const acceptable: number[] = [
- reinterpretF32AsI32(f),
- ...(isSubnormalNumberF32(f) ? f32ZerosInI32 : []),
- ];
- return anyOf(...acceptable.map(i32));
-}
-
-/**
- * @returns a Comparator for checking if a f32 value is a valid
- * bitcast conversion from i32.
- */
-function bitcastI32ToF32Comparator(i: number): Comparator {
- const f: number = reinterpretI32AsF32(i);
- if (!isFiniteF32(f)) return anyI32;
- // Positive or negative zero bit pattern map to any zero.
- if (f32ZerosInI32.includes(i)) return anyOf(...f32ZerosInF32.map(f32));
- const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])];
- return anyOf(...acceptable.map(f32));
-}
-
-/**
- * @returns a Comparator for checking if a f32 value is a valid
- * bitcast conversion from u32.
- */
-function bitcastU32ToF32Comparator(u: number): Comparator {
- const f: number = reinterpretU32AsF32(u);
- if (!isFiniteF32(f)) return anyU32;
- // Positive or negative zero bit pattern map to any zero.
- if (f32ZerosInU32.includes(u)) return anyOf(...f32ZerosInF32.map(f32));
- const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])];
- return anyOf(...acceptable.map(f32));
-}
-
-/**
- * @returns an array of expected f16 FPInterval for the given bitcasted f16 value, which may be
- * subnormal, Inf, or NaN. Test cases that bitcasted to vector of f16 use this function to get
- * per-element expectation and build vector expectation using cartesianProduct.
- */
-function generateF16ExpectationIntervals(bitcastedF16Value: number): FPInterval[] {
- // If the bitcasted f16 value is inf or nan, the result is unbounded
- if (!isFiniteF16(bitcastedF16Value)) {
- return [f16UnboundedInterval];
- }
- // If the casted f16 value is +/-0.0, the result can be one of both. Note that in JS -0.0 === 0.0.
- if (bitcastedF16Value === 0.0) {
- return [f16ZerosInterval];
- }
- const exactInterval = FP.f16.toInterval(bitcastedF16Value);
- // If the casted f16 value is subnormal, it also may be flushed to +/-0.0.
- return [exactInterval, ...(isSubnormalNumberF16(bitcastedF16Value) ? [f16ZerosInterval] : [])];
-}
-
-/**
- * @returns a Comparator for checking if a f16 value is a valid
- * bitcast conversion from f16.
- */
-function bitcastF16ToF16Comparator(f: number): Comparator {
- if (!isFiniteF16(f)) return anyOf(f16UnboundedInterval);
- return anyOf(...generateF16ExpectationIntervals(f));
-}
-
-/**
- * @returns a Comparator for checking if a vec2<f16> is a valid bitcast
- * conversion from u32.
- */
-function bitcastU32ToVec2F16Comparator(u: number): Comparator {
- const bitcastedVec2F16InU16x2 = u32ToU16x2(u).map(reinterpretU16AsF16);
- // Generate expection for vec2 f16 result, by generating expected intervals for each elements and
- // then do cartesian product.
- const expectedIntervalsCombination = cartesianProduct(
- ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals)
- );
- return anyOf(...expectedIntervalsCombination);
-}
-
-/**
- * @returns a Comparator for checking if a vec2<f16> value is a valid
- * bitcast conversion from i32.
- */
-function bitcastI32ToVec2F16Comparator(i: number): Comparator {
- const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretI32AsU32(i)).map(reinterpretU16AsF16);
- // Generate expection for vec2 f16 result, by generating expected intervals for each elements and
- // then do cartesian product.
- const expectedIntervalsCombination = cartesianProduct(
- ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals)
- );
- return anyOf(...expectedIntervalsCombination);
-}
-
-/**
- * @returns a Comparator for checking if a vec2<f16> value is a valid
- * bitcast conversion from f32.
- */
-function bitcastF32ToVec2F16Comparator(f: number): Comparator {
- // If input f32 is not finite, it can be evaluated to any value and thus any result f16 vec2 is
- // possible.
- if (!isFiniteF32(f)) {
- return anyOf([f16UnboundedInterval, f16UnboundedInterval]);
- }
- const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretF32AsU32(f)).map(reinterpretU16AsF16);
- // Generate expection for vec2 f16 result, by generating expected intervals for each elements and
- // then do cartesian product.
- const expectedIntervalsCombination = cartesianProduct(
- ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals)
- );
- return anyOf(...expectedIntervalsCombination);
-}
-
-/**
- * @returns a Comparator for checking if a vec4<f16> is a valid
- * bitcast conversion from vec2<u32>.
- */
-function bitcastVec2U32ToVec4F16Comparator(u32x2: number[]): Comparator {
- assert(u32x2.length === 2);
- const bitcastedVec4F16InU16x4 = u32x2.flatMap(u32ToU16x2).map(reinterpretU16AsF16);
- // Generate expection for vec4 f16 result, by generating expected intervals for each elements and
- // then do cartesian product.
- const expectedIntervalsCombination = cartesianProduct(
- ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals)
- );
- return anyOf(...expectedIntervalsCombination);
-}
-
-/**
- * @returns a Comparator for checking if a vec4<f16> is a valid
- * bitcast conversion from vec2<i32>.
- */
-function bitcastVec2I32ToVec4F16Comparator(i32x2: number[]): Comparator {
- assert(i32x2.length === 2);
- const bitcastedVec4F16InU16x4 = i32x2
- .map(reinterpretI32AsU32)
- .flatMap(u32ToU16x2)
- .map(reinterpretU16AsF16);
- // Generate expection for vec4 f16 result, by generating expected intervals for each elements and
- // then do cartesian product.
- const expectedIntervalsCombination = cartesianProduct(
- ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals)
- );
- return anyOf(...expectedIntervalsCombination);
-}
-
-/**
- * @returns a Comparator for checking if a vec4<f16> is a valid
- * bitcast conversion from vec2<f32>.
- */
-function bitcastVec2F32ToVec4F16Comparator(f32x2: number[]): Comparator {
- assert(f32x2.length === 2);
- const bitcastedVec4F16InU16x4 = f32x2
- .map(reinterpretF32AsU32)
- .flatMap(u32ToU16x2)
- .map(reinterpretU16AsF16);
- // Generate expection for vec4 f16 result, by generating expected intervals for each elements and
- // then do cartesian product.
- const expectedIntervalsCombination = cartesianProduct(
- ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals)
- );
- return anyOf(...expectedIntervalsCombination);
-}
-
-// Structure that store the expectations of a single 32bit scalar/element bitcasted from two f16.
-interface ExpectionFor32BitsScalarFromF16x2 {
- // possibleExpectations is Scalar array if the expectation is for i32/u32 and FPInterval array for
- // f32. Note that if the expectation for i32/u32 is unbound, possibleExpectations is meaningless.
- possibleExpectations: (Scalar | FPInterval)[];
- isUnbounded: boolean;
-}
-
-/**
- * @returns the array of possible 16bits, represented in u16, that bitcasted
- * from a given finite f16 represented in u16, handling the possible subnormal
- * flushing. Used to build up 32bits or larger results.
- */
-function possibleBitsInU16FromFiniteF16InU16(f16InU16: number): number[] {
- const h = reinterpretU16AsF16(f16InU16);
- assert(isFiniteF16(h));
- return [f16InU16, ...(isSubnormalNumberF16(h) ? f16ZerosInU16 : [])];
-}
-
-/**
- * @returns the expectation for a single 32bit scalar bitcasted from given pair of
- * f16, result in ExpectionFor32BitsScalarFromF16x2.
- */
-function possible32BitScalarIntervalsFromF16x2(
- f16x2InU16x2: number[],
- type: 'i32' | 'u32' | 'f32'
-): ExpectionFor32BitsScalarFromF16x2 {
- assert(f16x2InU16x2.length === 2);
- let reinterpretFromU32: (x: number) => number;
- let expectationsForValue: (x: number) => Scalar[] | FPInterval[];
- let unboundedExpectations: FPInterval[] | Scalar[];
- if (type === 'u32') {
- reinterpretFromU32 = (x: number) => x;
- expectationsForValue = x => [u32(x)];
- // Scalar expectation can not express "unbounded" for i32 and u32, so use 0 here as a
- // placeholder, and the possibleExpectations should be ignored if the result is unbounded.
- unboundedExpectations = [u32(0)];
- } else if (type === 'i32') {
- reinterpretFromU32 = (x: number) => reinterpretU32AsI32(x);
- expectationsForValue = x => [i32(x)];
- // Scalar expectation can not express "unbounded" for i32 and u32, so use 0 here as a
- // placeholder, and the possibleExpectations should be ignored if the result is unbounded.
- unboundedExpectations = [i32(0)];
- } else {
- assert(type === 'f32');
- reinterpretFromU32 = (x: number) => reinterpretU32AsF32(x);
- expectationsForValue = x => {
- // Handle the possible Inf/NaN/zeros and subnormal cases for f32 result.
- if (!isFiniteF32(x)) {
- return [f32UnboundedInterval];
- }
- // If the casted f16 value is +/-0.0, the result can be one of both. Note that in JS -0.0 === 0.0.
- if (x === 0.0) {
- return [f32ZerosInterval];
- }
- const exactInterval = FP.f32.toInterval(x);
- // If the casted f16 value is subnormal, it also may be flushed to +/-0.0.
- return [exactInterval, ...(isSubnormalNumberF32(x) ? [f32ZerosInterval] : [])];
- };
- unboundedExpectations = [f32UnboundedInterval];
- }
- // Return unbounded expection if f16 Inf/NaN occurs
- if (
- !isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[0])) ||
- !isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[1]))
- ) {
- return { possibleExpectations: unboundedExpectations, isUnbounded: true };
- }
- const possibleU16Bits = f16x2InU16x2.map(possibleBitsInU16FromFiniteF16InU16);
- const possibleExpectations = cartesianProduct(...possibleU16Bits).flatMap<Scalar | FPInterval>(
- (possibleBitsU16x2: readonly number[]) => {
- assert(possibleBitsU16x2.length === 2);
- return expectationsForValue(reinterpretFromU32(u16x2ToU32(possibleBitsU16x2)));
- }
- );
- return { possibleExpectations, isUnbounded: false };
-}
-
-/**
- * @returns a Comparator for checking if a u32 value is a valid
- * bitcast conversion from vec2 f16.
- */
-function bitcastVec2F16ToU32Comparator(vec2F16InU16x2: number[]): Comparator {
- assert(vec2F16InU16x2.length === 2);
- const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'u32');
- // Return alwaysPass if result is expected unbounded.
- if (expectations.isUnbounded) {
- return anyU32;
- }
- return anyOf(...expectations.possibleExpectations);
-}
-
-/**
- * @returns a Comparator for checking if a i32 value is a valid
- * bitcast conversion from vec2 f16.
- */
-function bitcastVec2F16ToI32Comparator(vec2F16InU16x2: number[]): Comparator {
- assert(vec2F16InU16x2.length === 2);
- const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'i32');
- // Return alwaysPass if result is expected unbounded.
- if (expectations.isUnbounded) {
- return anyI32;
- }
- return anyOf(...expectations.possibleExpectations);
-}
-
-/**
- * @returns a Comparator for checking if a i32 value is a valid
- * bitcast conversion from vec2 f16.
- */
-function bitcastVec2F16ToF32Comparator(vec2F16InU16x2: number[]): Comparator {
- assert(vec2F16InU16x2.length === 2);
- const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'f32');
- // Return alwaysPass if result is expected unbounded.
- if (expectations.isUnbounded) {
- return anyF32;
- }
- return anyOf(...expectations.possibleExpectations);
-}
-
-/**
- * @returns a Comparator for checking if a vec2 u32 value is a valid
- * bitcast conversion from vec4 f16.
- */
-function bitcastVec4F16ToVec2U32Comparator(vec4F16InU16x4: number[]): Comparator {
- assert(vec4F16InU16x4.length === 4);
- const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e =>
- possible32BitScalarIntervalsFromF16x2(e, 'u32')
- );
- // Return alwaysPass if any element is expected unbounded. Although it may be only one unbounded
- // element in the result vector, currently we don't have a way to build a comparator that expect
- // only one element of i32/u32 vector unbounded.
- if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) {
- return alwaysPass('any vec2<u32>');
- }
- return anyOf(
- ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(
- e => new Vector(e as Scalar[])
- )
- );
-}
-
-/**
- * @returns a Comparator for checking if a vec2 i32 value is a valid
- * bitcast conversion from vec4 f16.
- */
-function bitcastVec4F16ToVec2I32Comparator(vec4F16InU16x4: number[]): Comparator {
- assert(vec4F16InU16x4.length === 4);
- const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e =>
- possible32BitScalarIntervalsFromF16x2(e, 'i32')
- );
- // Return alwaysPass if any element is expected unbounded. Although it may be only one unbounded
- // element in the result vector, currently we don't have a way to build a comparator that expect
- // only one element of i32/u32 vector unbounded.
- if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) {
- return alwaysPass('any vec2<i32>');
- }
- return anyOf(
- ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(
- e => new Vector(e as Scalar[])
- )
- );
-}
-
-/**
- * @returns a Comparator for checking if a vec2 f32 value is a valid
- * bitcast conversion from vec4 f16.
- */
-function bitcastVec4F16ToVec2F32Comparator(vec4F16InU16x4: number[]): Comparator {
- assert(vec4F16InU16x4.length === 4);
- const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e =>
- possible32BitScalarIntervalsFromF16x2(e, 'f32')
- );
- return anyOf(
- ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(e => [
- e[0] as FPInterval,
- e[1] as FPInterval,
- ])
- );
-}
-
-export const d = makeCaseCache('bitcast', {
- // Identity Cases
- i32_to_i32: () => fullI32Range().map(e => ({ input: i32(e), expected: i32(e) })),
- u32_to_u32: () => fullU32Range().map(e => ({ input: u32(e), expected: u32(e) })),
- f32_inf_nan_to_f32: () =>
- f32RangeWithInfAndNaN.map(e => ({
- input: f32(e),
- expected: bitcastF32ToF32Comparator(e),
- })),
- f32_to_f32: () =>
- f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToF32Comparator(e) })),
- f16_inf_nan_to_f16: () =>
- [...f16FiniteInF16, ...f16InfAndNaNInF16].map(e => ({
- input: f16(e),
- expected: bitcastF16ToF16Comparator(e),
- })),
- f16_to_f16: () =>
- f16FiniteInF16.map(e => ({ input: f16(e), expected: bitcastF16ToF16Comparator(e) })),
-
- // i32,u32,f32 to different i32,u32,f32
- i32_to_u32: () => fullI32Range().map(e => ({ input: i32(e), expected: u32(e) })),
- i32_to_f32: () =>
- i32RangeForF32Finite.map(e => ({
- input: i32(e),
- expected: bitcastI32ToF32Comparator(e),
- })),
- i32_to_f32_inf_nan: () =>
- i32RangeForF32FiniteInfNaN.map(e => ({
- input: i32(e),
- expected: bitcastI32ToF32Comparator(e),
- })),
- u32_to_i32: () => fullU32Range().map(e => ({ input: u32(e), expected: i32(e) })),
- u32_to_f32: () =>
- u32RangeForF32Finite.map(e => ({
- input: u32(e),
- expected: bitcastU32ToF32Comparator(e),
- })),
- u32_to_f32_inf_nan: () =>
- u32RangeForF32FiniteInfNaN.map(e => ({
- input: u32(e),
- expected: bitcastU32ToF32Comparator(e),
- })),
- f32_inf_nan_to_i32: () =>
- f32RangeWithInfAndNaN.map(e => ({
- input: f32(e),
- expected: bitcastF32ToI32Comparator(e),
- })),
- f32_to_i32: () =>
- f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToI32Comparator(e) })),
-
- f32_inf_nan_to_u32: () =>
- f32RangeWithInfAndNaN.map(e => ({
- input: f32(e),
- expected: bitcastF32ToU32Comparator(e),
- })),
- f32_to_u32: () =>
- f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToU32Comparator(e) })),
-
- // i32,u32,f32 to vec2<f16>
- u32_to_vec2_f16_inf_nan: () =>
- u32RangeForF16Vec2FiniteInfNaN.map(e => ({
- input: u32(e),
- expected: bitcastU32ToVec2F16Comparator(e),
- })),
- u32_to_vec2_f16: () =>
- u32RangeForF16Vec2Finite.map(e => ({
- input: u32(e),
- expected: bitcastU32ToVec2F16Comparator(e),
- })),
- i32_to_vec2_f16_inf_nan: () =>
- i32RangeForF16Vec2FiniteInfNaN.map(e => ({
- input: i32(e),
- expected: bitcastI32ToVec2F16Comparator(e),
- })),
- i32_to_vec2_f16: () =>
- i32RangeForF16Vec2Finite.map(e => ({
- input: i32(e),
- expected: bitcastI32ToVec2F16Comparator(e),
- })),
- f32_inf_nan_to_vec2_f16_inf_nan: () =>
- f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN.map(e => ({
- input: f32(e),
- expected: bitcastF32ToVec2F16Comparator(e),
- })),
- f32_to_vec2_f16: () =>
- f32FiniteRangeForF16Vec2Finite.map(e => ({
- input: f32(e),
- expected: bitcastF32ToVec2F16Comparator(e),
- })),
-
- // vec2<i32>, vec2<u32>, vec2<f32> to vec4<f16>
- vec2_i32_to_vec4_f16_inf_nan: () =>
- slidingSlice(i32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({
- input: toVector(e, i32),
- expected: bitcastVec2I32ToVec4F16Comparator(e),
- })),
- vec2_i32_to_vec4_f16: () =>
- slidingSlice(i32RangeForF16Vec2Finite, 2).map(e => ({
- input: toVector(e, i32),
- expected: bitcastVec2I32ToVec4F16Comparator(e),
- })),
- vec2_u32_to_vec4_f16_inf_nan: () =>
- slidingSlice(u32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({
- input: toVector(e, u32),
- expected: bitcastVec2U32ToVec4F16Comparator(e),
- })),
- vec2_u32_to_vec4_f16: () =>
- slidingSlice(u32RangeForF16Vec2Finite, 2).map(e => ({
- input: toVector(e, u32),
- expected: bitcastVec2U32ToVec4F16Comparator(e),
- })),
- vec2_f32_inf_nan_to_vec4_f16_inf_nan: () =>
- slidingSlice(f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN, 2).map(e => ({
- input: toVector(e, f32),
- expected: bitcastVec2F32ToVec4F16Comparator(e),
- })),
- vec2_f32_to_vec4_f16: () =>
- slidingSlice(f32FiniteRangeForF16Vec2Finite, 2).map(e => ({
- input: toVector(e, f32),
- expected: bitcastVec2F32ToVec4F16Comparator(e),
- })),
-
- // vec2<f16> to i32, u32, f32
- vec2_f16_to_u32: () =>
- f16Vec2FiniteInU16x2.map(e => ({
- input: u16x2ToVec2F16(e),
- expected: bitcastVec2F16ToU32Comparator(e),
- })),
- vec2_f16_inf_nan_to_u32: () =>
- f16Vec2FiniteInfNanInU16x2.map(e => ({
- input: u16x2ToVec2F16(e),
- expected: bitcastVec2F16ToU32Comparator(e),
- })),
- vec2_f16_to_i32: () =>
- f16Vec2FiniteInU16x2.map(e => ({
- input: u16x2ToVec2F16(e),
- expected: bitcastVec2F16ToI32Comparator(e),
- })),
- vec2_f16_inf_nan_to_i32: () =>
- f16Vec2FiniteInfNanInU16x2.map(e => ({
- input: u16x2ToVec2F16(e),
- expected: bitcastVec2F16ToI32Comparator(e),
- })),
- vec2_f16_to_f32_finite: () =>
- f16Vec2FiniteInU16x2
- .filter(u16x2 => isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x2))))
- .map(e => ({
- input: u16x2ToVec2F16(e),
- expected: bitcastVec2F16ToF32Comparator(e),
- })),
- vec2_f16_inf_nan_to_f32: () =>
- f16Vec2FiniteInfNanInU16x2.map(e => ({
- input: u16x2ToVec2F16(e),
- expected: bitcastVec2F16ToF32Comparator(e),
- })),
-
- // vec4<f16> to vec2 of i32, u32, f32
- vec4_f16_to_vec2_u32: () =>
- f16Vec2FiniteInU16x4.map(e => ({
- input: u16x4ToVec4F16(e),
- expected: bitcastVec4F16ToVec2U32Comparator(e),
- })),
- vec4_f16_inf_nan_to_vec2_u32: () =>
- f16Vec2FiniteInfNanInU16x4.map(e => ({
- input: u16x4ToVec4F16(e),
- expected: bitcastVec4F16ToVec2U32Comparator(e),
- })),
- vec4_f16_to_vec2_i32: () =>
- f16Vec2FiniteInU16x4.map(e => ({
- input: u16x4ToVec4F16(e),
- expected: bitcastVec4F16ToVec2I32Comparator(e),
- })),
- vec4_f16_inf_nan_to_vec2_i32: () =>
- f16Vec2FiniteInfNanInU16x4.map(e => ({
- input: u16x4ToVec4F16(e),
- expected: bitcastVec4F16ToVec2I32Comparator(e),
- })),
- vec4_f16_to_vec2_f32_finite: () =>
- f16Vec2FiniteInU16x4
- .filter(
- u16x4 =>
- isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(0, 2)))) &&
- isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(2, 4))))
- )
- .map(e => ({
- input: u16x4ToVec4F16(e),
- expected: bitcastVec4F16ToVec2F32Comparator(e),
- })),
- vec4_f16_inf_nan_to_vec2_f32: () =>
- f16Vec2FiniteInfNanInU16x4.map(e => ({
- input: u16x4ToVec4F16(e),
- expected: bitcastVec4F16ToVec2F32Comparator(e),
- })),
-});
-
/**
* @returns a ShaderBuilder that generates a call to bitcast,
* using appropriate destination type, which optionally can be
@@ -865,7 +71,7 @@ g.test('i32_to_i32')
)
.fn(async t => {
const cases = await d.get('i32_to_i32');
- await run(t, bitcastBuilder('i32', t.params), [TypeI32], TypeI32, t.params, cases);
+ await run(t, bitcastBuilder('i32', t.params), [Type.i32], Type.i32, t.params, cases);
});
g.test('u32_to_u32')
@@ -879,7 +85,7 @@ g.test('u32_to_u32')
)
.fn(async t => {
const cases = await d.get('u32_to_u32');
- await run(t, bitcastBuilder('u32', t.params), [TypeU32], TypeU32, t.params, cases);
+ await run(t, bitcastBuilder('u32', t.params), [Type.u32], Type.u32, t.params, cases);
});
g.test('f32_to_f32')
@@ -896,7 +102,7 @@ g.test('f32_to_f32')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'f32_to_f32' : 'f32_inf_nan_to_f32'
);
- await run(t, bitcastBuilder('f32', t.params), [TypeF32], TypeF32, t.params, cases);
+ await run(t, bitcastBuilder('f32', t.params), [Type.f32], Type.f32, t.params, cases);
});
// To i32 from u32, f32
@@ -911,7 +117,7 @@ g.test('u32_to_i32')
)
.fn(async t => {
const cases = await d.get('u32_to_i32');
- await run(t, bitcastBuilder('i32', t.params), [TypeU32], TypeI32, t.params, cases);
+ await run(t, bitcastBuilder('i32', t.params), [Type.u32], Type.i32, t.params, cases);
});
g.test('f32_to_i32')
@@ -928,7 +134,7 @@ g.test('f32_to_i32')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'f32_to_i32' : 'f32_inf_nan_to_i32'
);
- await run(t, bitcastBuilder('i32', t.params), [TypeF32], TypeI32, t.params, cases);
+ await run(t, bitcastBuilder('i32', t.params), [Type.f32], Type.i32, t.params, cases);
});
// To u32 from i32, f32
@@ -943,7 +149,7 @@ g.test('i32_to_u32')
)
.fn(async t => {
const cases = await d.get('i32_to_u32');
- await run(t, bitcastBuilder('u32', t.params), [TypeI32], TypeU32, t.params, cases);
+ await run(t, bitcastBuilder('u32', t.params), [Type.i32], Type.u32, t.params, cases);
});
g.test('f32_to_u32')
@@ -960,7 +166,7 @@ g.test('f32_to_u32')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'f32_to_u32' : 'f32_inf_nan_to_u32'
);
- await run(t, bitcastBuilder('u32', t.params), [TypeF32], TypeU32, t.params, cases);
+ await run(t, bitcastBuilder('u32', t.params), [Type.f32], Type.u32, t.params, cases);
});
// To f32 from i32, u32
@@ -978,7 +184,7 @@ g.test('i32_to_f32')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'i32_to_f32' : 'i32_to_f32_inf_nan'
);
- await run(t, bitcastBuilder('f32', t.params), [TypeI32], TypeF32, t.params, cases);
+ await run(t, bitcastBuilder('f32', t.params), [Type.i32], Type.f32, t.params, cases);
});
g.test('u32_to_f32')
@@ -995,7 +201,7 @@ g.test('u32_to_f32')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'u32_to_f32' : 'u32_to_f32_inf_nan'
);
- await run(t, bitcastBuilder('f32', t.params), [TypeU32], TypeF32, t.params, cases);
+ await run(t, bitcastBuilder('f32', t.params), [Type.u32], Type.f32, t.params, cases);
});
// 16 bit types
@@ -1020,7 +226,7 @@ g.test('f16_to_f16')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'f16_to_f16' : 'f16_inf_nan_to_f16'
);
- await run(t, bitcastBuilder('f16', t.params), [TypeF16], TypeF16, t.params, cases);
+ await run(t, bitcastBuilder('f16', t.params), [Type.f16], Type.f16, t.params, cases);
});
// f16: 32-bit scalar numeric to vec2<f16>
@@ -1036,14 +242,7 @@ g.test('i32_to_vec2h')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'i32_to_vec2_f16' : 'i32_to_vec2_f16_inf_nan'
);
- await run(
- t,
- bitcastBuilder('vec2<f16>', t.params),
- [TypeI32],
- TypeVec(2, TypeF16),
- t.params,
- cases
- );
+ await run(t, bitcastBuilder('vec2<f16>', t.params), [Type.i32], Type.vec2h, t.params, cases);
});
g.test('u32_to_vec2h')
@@ -1058,14 +257,7 @@ g.test('u32_to_vec2h')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'u32_to_vec2_f16' : 'u32_to_vec2_f16_inf_nan'
);
- await run(
- t,
- bitcastBuilder('vec2<f16>', t.params),
- [TypeU32],
- TypeVec(2, TypeF16),
- t.params,
- cases
- );
+ await run(t, bitcastBuilder('vec2<f16>', t.params), [Type.u32], Type.vec2h, t.params, cases);
});
g.test('f32_to_vec2h')
@@ -1080,14 +272,7 @@ g.test('f32_to_vec2h')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'f32_to_vec2_f16' : 'f32_inf_nan_to_vec2_f16_inf_nan'
);
- await run(
- t,
- bitcastBuilder('vec2<f16>', t.params),
- [TypeF32],
- TypeVec(2, TypeF16),
- t.params,
- cases
- );
+ await run(t, bitcastBuilder('vec2<f16>', t.params), [Type.f32], Type.vec2h, t.params, cases);
});
// f16: vec2<32-bit scalar numeric> to vec4<f16>
@@ -1103,14 +288,7 @@ g.test('vec2i_to_vec4h')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'vec2_i32_to_vec4_f16' : 'vec2_i32_to_vec4_f16_inf_nan'
);
- await run(
- t,
- bitcastBuilder('vec4<f16>', t.params),
- [TypeVec(2, TypeI32)],
- TypeVec(4, TypeF16),
- t.params,
- cases
- );
+ await run(t, bitcastBuilder('vec4<f16>', t.params), [Type.vec2i], Type.vec4h, t.params, cases);
});
g.test('vec2u_to_vec4h')
@@ -1125,14 +303,7 @@ g.test('vec2u_to_vec4h')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'vec2_u32_to_vec4_f16' : 'vec2_u32_to_vec4_f16_inf_nan'
);
- await run(
- t,
- bitcastBuilder('vec4<f16>', t.params),
- [TypeVec(2, TypeU32)],
- TypeVec(4, TypeF16),
- t.params,
- cases
- );
+ await run(t, bitcastBuilder('vec4<f16>', t.params), [Type.vec2u], Type.vec4h, t.params, cases);
});
g.test('vec2f_to_vec4h')
@@ -1149,14 +320,7 @@ g.test('vec2f_to_vec4h')
? 'vec2_f32_to_vec4_f16'
: 'vec2_f32_inf_nan_to_vec4_f16_inf_nan'
);
- await run(
- t,
- bitcastBuilder('vec4<f16>', t.params),
- [TypeVec(2, TypeF32)],
- TypeVec(4, TypeF16),
- t.params,
- cases
- );
+ await run(t, bitcastBuilder('vec4<f16>', t.params), [Type.vec2f], Type.vec4h, t.params, cases);
});
// f16: vec2<f16> to 32-bit scalar numeric
@@ -1172,7 +336,7 @@ g.test('vec2h_to_i32')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'vec2_f16_to_i32' : 'vec2_f16_inf_nan_to_i32'
);
- await run(t, bitcastBuilder('i32', t.params), [TypeVec(2, TypeF16)], TypeI32, t.params, cases);
+ await run(t, bitcastBuilder('i32', t.params), [Type.vec2h], Type.i32, t.params, cases);
});
g.test('vec2h_to_u32')
@@ -1187,7 +351,7 @@ g.test('vec2h_to_u32')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'vec2_f16_to_u32' : 'vec2_f16_inf_nan_to_u32'
);
- await run(t, bitcastBuilder('u32', t.params), [TypeVec(2, TypeF16)], TypeU32, t.params, cases);
+ await run(t, bitcastBuilder('u32', t.params), [Type.vec2h], Type.u32, t.params, cases);
});
g.test('vec2h_to_f32')
@@ -1202,7 +366,7 @@ g.test('vec2h_to_f32')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'vec2_f16_to_f32_finite' : 'vec2_f16_inf_nan_to_f32'
);
- await run(t, bitcastBuilder('f32', t.params), [TypeVec(2, TypeF16)], TypeF32, t.params, cases);
+ await run(t, bitcastBuilder('f32', t.params), [Type.vec2h], Type.f32, t.params, cases);
});
// f16: vec4<f16> to vec2<32-bit scalar numeric>
@@ -1218,14 +382,7 @@ g.test('vec4h_to_vec2i')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'vec4_f16_to_vec2_i32' : 'vec4_f16_inf_nan_to_vec2_i32'
);
- await run(
- t,
- bitcastBuilder('vec2<i32>', t.params),
- [TypeVec(4, TypeF16)],
- TypeVec(2, TypeI32),
- t.params,
- cases
- );
+ await run(t, bitcastBuilder('vec2<i32>', t.params), [Type.vec4h], Type.vec2i, t.params, cases);
});
g.test('vec4h_to_vec2u')
@@ -1240,14 +397,7 @@ g.test('vec4h_to_vec2u')
// Infinities and NaNs are errors in const-eval.
t.params.inputSource === 'const' ? 'vec4_f16_to_vec2_u32' : 'vec4_f16_inf_nan_to_vec2_u32'
);
- await run(
- t,
- bitcastBuilder('vec2<u32>', t.params),
- [TypeVec(4, TypeF16)],
- TypeVec(2, TypeU32),
- t.params,
- cases
- );
+ await run(t, bitcastBuilder('vec2<u32>', t.params), [Type.vec4h], Type.vec2u, t.params, cases);
});
g.test('vec4h_to_vec2f')
@@ -1264,12 +414,230 @@ g.test('vec4h_to_vec2f')
? 'vec4_f16_to_vec2_f32_finite'
: 'vec4_f16_inf_nan_to_vec2_f32'
);
+ await run(t, bitcastBuilder('vec2<f32>', t.params), [Type.vec4h], Type.vec2f, t.params, cases);
+ });
+
+// Abstract Float
+g.test('af_to_f32')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast abstract float to f32 tests`)
+ .params(u =>
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const cases = scalarF32Range().map(u => {
+ const res = FP['f32'].addFlushedIfNeeded([u]).map(f => {
+ return f32(f);
+ });
+ return {
+ input: abstractFloat(u),
+ expected: anyOf(...res),
+ };
+ });
+
+ await run(t, bitcastBuilder('f32', t.params), [Type.abstractFloat], Type.f32, t.params, cases);
+ });
+
+g.test('af_to_i32')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast abstract float to i32 tests`)
+ .params(u =>
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const values = [
+ 0,
+ 1,
+ 10,
+ 256,
+ u32Bits(0b11111111011111111111111111111111).value,
+ u32Bits(0b11111111010000000000000000000000).value,
+ u32Bits(0b11111110110000000000000000000000).value,
+ u32Bits(0b11111101110000000000000000000000).value,
+ u32Bits(0b11111011110000000000000000000000).value,
+ u32Bits(0b11110111110000000000000000000000).value,
+ u32Bits(0b11101111110000000000000000000000).value,
+ u32Bits(0b11011111110000000000000000000000).value,
+ u32Bits(0b10111111110000000000000000000000).value,
+ u32Bits(0b01111111011111111111111111111111).value,
+ u32Bits(0b01111111010000000000000000000000).value,
+ u32Bits(0b01111110110000000000000000000000).value,
+ u32Bits(0b01111101110000000000000000000000).value,
+ u32Bits(0b01111011110000000000000000000000).value,
+ u32Bits(0b01110111110000000000000000000000).value,
+ u32Bits(0b01101111110000000000000000000000).value,
+ u32Bits(0b01011111110000000000000000000000).value,
+ u32Bits(0b00111111110000000000000000000000).value,
+ ];
+
+ const cases = values.map(u => {
+ return {
+ input: abstractFloat(uint32ToFloat32(u)),
+ expected: i32(u),
+ };
+ });
+
+ await run(t, bitcastBuilder('i32', t.params), [Type.abstractFloat], Type.i32, t.params, cases);
+ });
+
+g.test('af_to_u32')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast abstract float to u32 tests`)
+ .params(u =>
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ )
+ .fn(async t => {
+ const values = [
+ 0,
+ 1,
+ 10,
+ 256,
+ u32Bits(0b11111111011111111111111111111111).value,
+ u32Bits(0b11111111010000000000000000000000).value,
+ u32Bits(0b11111110110000000000000000000000).value,
+ u32Bits(0b11111101110000000000000000000000).value,
+ u32Bits(0b11111011110000000000000000000000).value,
+ u32Bits(0b11110111110000000000000000000000).value,
+ u32Bits(0b11101111110000000000000000000000).value,
+ u32Bits(0b11011111110000000000000000000000).value,
+ u32Bits(0b10111111110000000000000000000000).value,
+ u32Bits(0b01111111011111111111111111111111).value,
+ u32Bits(0b01111111010000000000000000000000).value,
+ u32Bits(0b01111110110000000000000000000000).value,
+ u32Bits(0b01111101110000000000000000000000).value,
+ u32Bits(0b01111011110000000000000000000000).value,
+ u32Bits(0b01110111110000000000000000000000).value,
+ u32Bits(0b01101111110000000000000000000000).value,
+ u32Bits(0b01011111110000000000000000000000).value,
+ u32Bits(0b00111111110000000000000000000000).value,
+ ];
+
+ const cases = values.map(u => {
+ return {
+ input: abstractFloat(uint32ToFloat32(u)),
+ expected: u32(u),
+ };
+ });
+
+ await run(t, bitcastBuilder('u32', t.params), [Type.abstractFloat], Type.u32, t.params, cases);
+ });
+
+g.test('af_to_vec2f16')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast abstract float to f16 tests`)
+ .beforeAllSubcases(t => {
+ t.selectDeviceOrSkipTestCase('shader-f16');
+ })
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('af_to_vec2_f16');
+
+ await run(
+ t,
+ bitcastBuilder('vec2<f16>', t.params),
+ [Type.abstractFloat],
+ Type.vec2h,
+ t.params,
+ cases
+ );
+ });
+
+g.test('vec2af_to_vec4f16')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast abstract float to f16 tests`)
+ .beforeAllSubcases(t => {
+ t.selectDeviceOrSkipTestCase('shader-f16');
+ })
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('vec2_af_to_vec4_f16');
+
+ await run(
+ t,
+ bitcastBuilder('vec4<f16>', t.params),
+ [Type.vec(2, Type.abstractFloat)],
+ Type.vec4h,
+ t.params,
+ cases
+ );
+ });
+
+// Abstract Int
+g.test('ai_to_i32')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast abstract int to i32 tests`)
+ .params(u =>
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ .combine('alias', [false, true])
+ )
+ .fn(async t => {
+ const cases = await d.get('ai_to_i32');
+ await run(t, bitcastBuilder('i32', t.params), [Type.abstractInt], Type.i32, t.params, cases);
+ });
+
+g.test('ai_to_u32')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast abstract int to u32 tests`)
+ .params(u =>
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ .combine('alias', [false, true])
+ )
+ .fn(async t => {
+ const cases = await d.get('ai_to_u32');
+ await run(t, bitcastBuilder('u32', t.params), [Type.abstractInt], Type.u32, t.params, cases);
+ });
+
+g.test('ai_to_f32')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast abstract int to f32 tests`)
+ .params(u =>
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ .combine('alias', [false, true])
+ )
+ .fn(async t => {
+ const cases = await d.get('ai_to_f32');
+ await run(t, bitcastBuilder('f32', t.params), [Type.abstractInt], Type.f32, t.params, cases);
+ });
+
+g.test('ai_to_vec2h')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast ai to vec2h tests`)
+ .params(u => u.combine('inputSource', onlyConstInputSource).combine('alias', [false, true]))
+ .beforeAllSubcases(t => {
+ t.selectDeviceOrSkipTestCase('shader-f16');
+ })
+ .fn(async t => {
+ const cases = await d.get('ai_to_vec2_f16');
await run(
t,
- bitcastBuilder('vec2<f32>', t.params),
- [TypeVec(4, TypeF16)],
- TypeVec(2, TypeF32),
+ bitcastBuilder('vec2<f16>', t.params),
+ [Type.abstractInt],
+ Type.vec2h,
t.params,
cases
);
});
+
+g.test('vec2ai_to_vec4h')
+ .specURL('https://www.w3.org/TR/WGSL/#bitcast-builtin')
+ .desc(`bitcast vec2ai to vec4h tests`)
+ .params(u => u.combine('inputSource', onlyConstInputSource).combine('alias', [false, true]))
+ .beforeAllSubcases(t => {
+ t.selectDeviceOrSkipTestCase('shader-f16');
+ })
+ .fn(async t => {
+ const cases = await d.get('vec2_ai_to_vec4_f16');
+ await run(t, bitcastBuilder('vec4<f16>', t.params), [Type.vec2ai], Type.vec4h, t.params, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts
index 282feea703..0afd8c3980 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts
@@ -1,5 +1,6 @@
import {
abstractFloatShaderBuilder,
+ abstractIntShaderBuilder,
basicExpressionBuilder,
basicExpressionWithPredeclarationBuilder,
ShaderBuilder,
@@ -11,10 +12,15 @@ export function builtin(name: string): ShaderBuilder {
}
/* @returns a ShaderBuilder that calls the builtin with the given name that returns AbstractFloats */
-export function abstractBuiltin(name: string): ShaderBuilder {
+export function abstractFloatBuiltin(name: string): ShaderBuilder {
return abstractFloatShaderBuilder(values => `${name}(${values.join(', ')})`);
}
+/* @returns a ShaderBuilder that calls the builtin with the given name that returns AbstractInts */
+export function abstractIntBuiltin(name: string): ShaderBuilder {
+ return abstractIntShaderBuilder(values => `${name}(${values.join(', ')})`);
+}
+
/* @returns a ShaderBuilder that calls the builtin with the given name and has given predeclaration */
export function builtinWithPredeclaration(name: string, predeclaration: string): ShaderBuilder {
return basicExpressionWithPredeclarationBuilder(
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts
new file mode 100644
index 0000000000..c0178d9b83
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts
@@ -0,0 +1,26 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+const kSmallMagnitudeTestValues = [0.1, 0.9, 1.0, 1.1, 1.9, -0.1, -0.9, -1.0, -1.1, -1.9];
+
+// See https://github.com/gpuweb/cts/issues/2766 for details
+const kIssue2766Value = {
+ f32: 0x8000_0000,
+ f16: 0x8000,
+ abstract: 0x8000_0000_0000_0000,
+};
+
+// Cases: [f32|f16]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ [...kSmallMagnitudeTestValues, kIssue2766Value[trait], ...FP[trait].scalarRange()],
+ 'unfiltered',
+ FP[trait].ceilInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('ceil', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts
index 6cdf90986b..842875f094 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'ceil' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn ceil(e: T ) -> T
Returns the ceiling of e. Component-wise when T is a vector.
@@ -10,70 +10,33 @@ Returns the ceiling of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './ceil.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('ceil', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- // Small positive numbers
- 0.1,
- 0.9,
- 1.0,
- 1.1,
- 1.9,
- // Small negative numbers
- -0.1,
- -0.9,
- -1.0,
- -1.1,
- -1.9,
- 0x80000000, // https://github.com/gpuweb/cts/issues/2766
- ...fullF32Range(),
- ],
- 'unfiltered',
- FP.f32.ceilInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- [
- // Small positive numbers
- 0.1,
- 0.9,
- 1.0,
- 1.1,
- 1.9,
- // Small negative numbers
- -0.1,
- -0.9,
- -1.0,
- -1.1,
- -1.9,
- 0x8000, // https://github.com/gpuweb/cts/issues/2766
- ...fullF16Range(),
- ],
- 'unfiltered',
- FP.f16.ceilInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('ceil'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -83,7 +46,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('ceil'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('ceil'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -97,5 +60,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('ceil'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('ceil'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts
new file mode 100644
index 0000000000..909d15e7e7
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts
@@ -0,0 +1,131 @@
+import { kValue } from '../../../../../util/constants.js';
+import { ScalarType, Type } from '../../../../../util/conversion.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { maxBigInt, minBigInt } from '../../../../../util/math.js';
+import { Case } from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+const u32Values = [0, 1, 2, 3, 0x70000000, 0x80000000, kValue.u32.max];
+
+const i32Values = [
+ kValue.i32.negative.min,
+ -3,
+ -2,
+ -1,
+ 0,
+ 1,
+ 2,
+ 3,
+ 0x70000000,
+ kValue.i32.positive.max,
+];
+
+const abstractFloatValues = [
+ kValue.i64.negative.min,
+ -3n,
+ -2n,
+ -1n,
+ 0n,
+ 1n,
+ 2n,
+ 3n,
+ 0x70000000n,
+ kValue.i64.positive.max,
+];
+
+/** @returns a set of clamp test cases from an ascending list of concrete integer values */
+function generateConcreteIntegerTestCases(
+ test_values: Array<number>,
+ type: ScalarType,
+ stage: 'const' | 'non_const'
+): Array<Case> {
+ return test_values.flatMap(low =>
+ test_values.flatMap(high =>
+ stage === 'const' && low > high
+ ? []
+ : test_values.map(e => ({
+ input: [type.create(e), type.create(low), type.create(high)],
+ expected: type.create(Math.min(Math.max(e, low), high)),
+ }))
+ )
+ );
+}
+
+/** @returns a set of clamp test cases from an ascending list of abstract integer values */
+function generateAbstractIntegerTestCases(test_values: Array<bigint>): Array<Case> {
+ return test_values.flatMap(low =>
+ test_values.flatMap(high =>
+ low > high
+ ? []
+ : test_values.map(e => ({
+ input: [
+ Type.abstractInt.create(e),
+ Type.abstractInt.create(low),
+ Type.abstractInt.create(high),
+ ],
+ expected: Type.abstractInt.create(minBigInt(maxBigInt(e, low), high)),
+ }))
+ )
+ );
+}
+
+function generateFloatTestCases(
+ test_values: readonly number[],
+ trait: 'f32' | 'f16' | 'abstract',
+ stage: 'const' | 'non_const'
+): Array<Case> {
+ return test_values.flatMap(low =>
+ test_values.flatMap(high =>
+ stage === 'const' && low > high
+ ? []
+ : test_values.flatMap(e => {
+ const c = FP[trait].makeScalarTripleToIntervalCase(
+ e,
+ low,
+ high,
+ stage === 'const' ? 'finite' : 'unfiltered',
+ ...FP[trait].clampIntervals
+ );
+ return c === undefined ? [] : [c];
+ })
+ )
+ );
+}
+
+// Cases: [f32|f16|abstract]_[non_]const
+// abstract_non_const is empty and unused
+const fp_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return generateFloatTestCases(
+ FP[trait].sparseScalarRange(),
+ trait,
+ nonConst ? 'non_const' : 'const'
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('clamp', {
+ u32_non_const: () => {
+ return generateConcreteIntegerTestCases(u32Values, Type.u32, 'non_const');
+ },
+ u32_const: () => {
+ return generateConcreteIntegerTestCases(u32Values, Type.u32, 'const');
+ },
+ i32_non_const: () => {
+ return generateConcreteIntegerTestCases(i32Values, Type.i32, 'non_const');
+ },
+ i32_const: () => {
+ return generateConcreteIntegerTestCases(i32Values, Type.i32, 'const');
+ },
+ abstract_int: () => {
+ return generateAbstractIntegerTestCases(abstractFloatValues);
+ },
+ ...fp_cases,
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts
index 0113fd656f..0b524bccf0 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts
@@ -1,12 +1,12 @@
export const description = `
Execution tests for the 'clamp' builtin function
-S is AbstractInt, i32, or u32
+S is abstract-int, i32, or u32
T is S or vecN<S>
@const fn clamp(e: T , low: T, high: T) -> T
Returns min(max(e,low),high). Component-wise when T is a vector.
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const clamp(e: T , low: T , high: T) -> T
Returns either min(max(e,low),high), or the median of the three values e, low, high.
@@ -15,117 +15,33 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { kValue } from '../../../../../util/constants.js';
-import {
- ScalarType,
- TypeF32,
- TypeF16,
- TypeI32,
- TypeU32,
- TypeAbstractFloat,
-} from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { sparseF32Range, sparseF16Range, sparseF64Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, Case, onlyConstInputSource, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js';
+import { d } from './clamp.cache.js';
export const g = makeTestGroup(GPUTest);
-const u32Values = [0, 1, 2, 3, 0x70000000, 0x80000000, kValue.u32.max];
-
-const i32Values = [
- kValue.i32.negative.min,
- -3,
- -2,
- -1,
- 0,
- 1,
- 2,
- 3,
- 0x70000000,
- kValue.i32.positive.max,
-];
-
-export const d = makeCaseCache('clamp', {
- u32_non_const: () => {
- return generateIntegerTestCases(u32Values, TypeU32, 'non-const');
- },
- u32_const: () => {
- return generateIntegerTestCases(u32Values, TypeU32, 'const');
- },
- i32_non_const: () => {
- return generateIntegerTestCases(i32Values, TypeI32, 'non-const');
- },
- i32_const: () => {
- return generateIntegerTestCases(i32Values, TypeI32, 'const');
- },
- f32_const: () => {
- return generateFloatTestCases(sparseF32Range(), 'f32', 'const');
- },
- f32_non_const: () => {
- return generateFloatTestCases(sparseF32Range(), 'f32', 'non-const');
- },
- f16_const: () => {
- return generateFloatTestCases(sparseF16Range(), 'f16', 'const');
- },
- f16_non_const: () => {
- return generateFloatTestCases(sparseF16Range(), 'f16', 'non-const');
- },
- abstract: () => {
- return generateFloatTestCases(sparseF64Range(), 'abstract', 'const');
- },
-});
-
-/** @returns a set of clamp test cases from an ascending list of integer values */
-function generateIntegerTestCases(
- test_values: Array<number>,
- type: ScalarType,
- stage: 'const' | 'non-const'
-): Array<Case> {
- return test_values.flatMap(low =>
- test_values.flatMap(high =>
- stage === 'const' && low > high
- ? []
- : test_values.map(e => ({
- input: [type.create(e), type.create(low), type.create(high)],
- expected: type.create(Math.min(Math.max(e, low), high)),
- }))
- )
- );
-}
-
-function generateFloatTestCases(
- test_values: readonly number[],
- trait: 'f32' | 'f16' | 'abstract',
- stage: 'const' | 'non-const'
-): Array<Case> {
- return test_values.flatMap(low =>
- test_values.flatMap(high =>
- stage === 'const' && low > high
- ? []
- : test_values.flatMap(e => {
- const c = FP[trait].makeScalarTripleToIntervalCase(
- e,
- low,
- high,
- stage === 'const' ? 'finite' : 'unfiltered',
- ...FP[trait].clampIntervals
- );
- return c === undefined ? [] : [c];
- })
- )
- );
-}
-
g.test('abstract_int')
.specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
.desc(`abstract int tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_int');
+ await run(
+ t,
+ abstractIntBuiltin('clamp'),
+ [Type.abstractInt, Type.abstractInt, Type.abstractInt],
+ Type.abstractInt,
+ t.params,
+ cases
+ );
+ });
g.test('u32')
.specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
@@ -135,7 +51,7 @@ g.test('u32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
- await run(t, builtin('clamp'), [TypeU32, TypeU32, TypeU32], TypeU32, t.params, cases);
+ await run(t, builtin('clamp'), [Type.u32, Type.u32, Type.u32], Type.u32, t.params, cases);
});
g.test('i32')
@@ -146,7 +62,7 @@ g.test('i32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'i32_const' : 'i32_non_const');
- await run(t, builtin('clamp'), [TypeI32, TypeI32, TypeI32], TypeI32, t.params, cases);
+ await run(t, builtin('clamp'), [Type.i32, Type.i32, Type.i32], Type.i32, t.params, cases);
});
g.test('abstract_float')
@@ -158,12 +74,12 @@ g.test('abstract_float')
.combine('vectorize', [undefined, 2, 3, 4] as const)
)
.fn(async t => {
- const cases = await d.get('abstract');
+ const cases = await d.get('abstract_const');
await run(
t,
- abstractBuiltin('clamp'),
- [TypeAbstractFloat, TypeAbstractFloat, TypeAbstractFloat],
- TypeAbstractFloat,
+ abstractFloatBuiltin('clamp'),
+ [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
t.params,
cases
);
@@ -177,7 +93,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('clamp'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('clamp'), [Type.f32, Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -191,5 +107,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('clamp'), [TypeF16, TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('clamp'), [Type.f16, Type.f16, Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.cache.ts
new file mode 100644
index 0000000000..11c9f50a1f
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.cache.ts
@@ -0,0 +1,23 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ [
+ // Well-defined accuracy range
+ ...linearRange(-Math.PI, Math.PI, 100),
+ ...FP[trait].scalarRange(),
+ ],
+ trait === 'abstract' ? 'finite' : 'unfiltered',
+ // cos has an absolute accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].cosInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('cos', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts
index 723bca2efd..5675f220bf 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'cos' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn cos(e: T ) -> T
Returns the cosine of e. Component-wise when T is a vector.
@@ -9,48 +9,33 @@ Returns the cosine of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range, linearRange } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './cos.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('cos', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- // Well-defined accuracy range
- ...linearRange(-Math.PI, Math.PI, 1000),
- ...fullF32Range(),
- ],
- 'unfiltered',
- FP.f32.cosInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- [
- // Well-defined accuracy range
- ...linearRange(-Math.PI, Math.PI, 1000),
- ...fullF16Range(),
- ],
- 'unfiltered',
- FP.f16.cosInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('cos'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -66,7 +51,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1]
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('cos'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('cos'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -80,5 +65,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('cos'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('cos'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.cache.ts
new file mode 100644
index 0000000000..0c90f178df
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.cache.ts
@@ -0,0 +1,23 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // cosh has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].coshInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('cosh', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts
index 37fb961c98..d043df9527 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'cosh' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn cosh(e: T ) -> T
Returns the hyperbolic cosine of e. Component-wise when T is a vector
@@ -9,38 +9,33 @@ Returns the hyperbolic cosine of e. Component-wise when T is a vector
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './cosh.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('cosh', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'finite', FP.f32.coshInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'unfiltered', FP.f32.coshInterval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'finite', FP.f16.coshInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'unfiltered', FP.f16.coshInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('cosh'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -50,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('cosh'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('cosh'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -64,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('cosh'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('cosh'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts
index cfae4bb6e0..ea0c38ae58 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts
@@ -12,7 +12,7 @@ Also known as "clz" in some languages.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeU32, u32Bits, u32, TypeI32, i32Bits, i32 } from '../../../../../util/conversion.js';
+import { Type, u32Bits, u32, i32Bits, i32 } from '../../../../../util/conversion.js';
import { allInputSources, Config, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -27,7 +27,7 @@ g.test('u32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('countLeadingZeros'), [TypeU32], TypeU32, cfg, [
+ await run(t, builtin('countLeadingZeros'), [Type.u32], Type.u32, cfg, [
// Zero
{ input: u32Bits(0b00000000000000000000000000000000), expected: u32(32) },
@@ -142,7 +142,7 @@ g.test('i32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('countLeadingZeros'), [TypeI32], TypeI32, cfg, [
+ await run(t, builtin('countLeadingZeros'), [Type.i32], Type.i32, cfg, [
// Zero
{ input: i32Bits(0b00000000000000000000000000000000), expected: i32(32) },
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts
index f0be916285..1937e04283 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts
@@ -11,7 +11,7 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeU32, u32Bits, u32, TypeI32, i32Bits, i32 } from '../../../../../util/conversion.js';
+import { Type, u32Bits, u32, i32Bits, i32 } from '../../../../../util/conversion.js';
import { allInputSources, Config, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -26,7 +26,7 @@ g.test('u32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('countOneBits'), [TypeU32], TypeU32, cfg, [
+ await run(t, builtin('countOneBits'), [Type.u32], Type.u32, cfg, [
// Zero
{ input: u32Bits(0b00000000000000000000000000000000), expected: u32(0) },
@@ -141,7 +141,7 @@ g.test('i32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('countOneBits'), [TypeI32], TypeI32, cfg, [
+ await run(t, builtin('countOneBits'), [Type.i32], Type.i32, cfg, [
// Zero
{ input: i32Bits(0b00000000000000000000000000000000), expected: i32(0) },
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts
index d0b3198f49..3392a47810 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts
@@ -12,7 +12,7 @@ Also known as "ctz" in some languages.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js';
+import { i32, i32Bits, Type, u32, u32Bits } from '../../../../../util/conversion.js';
import { allInputSources, Config, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -27,7 +27,7 @@ g.test('u32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('countTrailingZeros'), [TypeU32], TypeU32, cfg, [
+ await run(t, builtin('countTrailingZeros'), [Type.u32], Type.u32, cfg, [
// Zero
{ input: u32Bits(0b00000000000000000000000000000000), expected: u32(32) },
@@ -142,7 +142,7 @@ g.test('i32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('countTrailingZeros'), [TypeI32], TypeI32, cfg, [
+ await run(t, builtin('countTrailingZeros'), [Type.i32], Type.i32, cfg, [
// Zero
{ input: i32Bits(0b00000000000000000000000000000000), expected: i32(32) },
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.cache.ts
new file mode 100644
index 0000000000..396789b4ed
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.cache.ts
@@ -0,0 +1,25 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+// abstract_non_const is empty and not used
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateVectorPairToVectorCases(
+ FP[trait].vectorRange(3),
+ FP[trait].vectorRange(3),
+ nonConst ? 'unfiltered' : 'finite',
+ // cross has an inherited accuracy, so abstract is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].crossInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('cross', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts
index 2b0b3e58ce..cc32537076 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts
@@ -1,77 +1,32 @@
export const description = `
Execution tests for the 'cross' builtin function
-T is AbstractFloat, f32, or f16
+T is abstract-float, f32, or f16
@const fn cross(e1: vec3<T> ,e2: vec3<T>) -> vec3<T>
Returns the cross product of e1 and e2.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeAbstractFloat, TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { sparseVectorF64Range, vectorF16Range, vectorF32Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './cross.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('cross', {
- f32_const: () => {
- return FP.f32.generateVectorPairToVectorCases(
- vectorF32Range(3),
- vectorF32Range(3),
- 'finite',
- FP.f32.crossInterval
- );
- },
- f32_non_const: () => {
- return FP.f32.generateVectorPairToVectorCases(
- vectorF32Range(3),
- vectorF32Range(3),
- 'unfiltered',
- FP.f32.crossInterval
- );
- },
- f16_const: () => {
- return FP.f16.generateVectorPairToVectorCases(
- vectorF16Range(3),
- vectorF16Range(3),
- 'finite',
- FP.f16.crossInterval
- );
- },
- f16_non_const: () => {
- return FP.f16.generateVectorPairToVectorCases(
- vectorF16Range(3),
- vectorF16Range(3),
- 'unfiltered',
- FP.f16.crossInterval
- );
- },
- abstract: () => {
- return FP.abstract.generateVectorPairToVectorCases(
- sparseVectorF64Range(3),
- sparseVectorF64Range(3),
- 'finite',
- FP.abstract.crossInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u => u.combine('inputSource', onlyConstInputSource))
.fn(async t => {
- const cases = await d.get('abstract');
+ const cases = await d.get('abstract_const');
await run(
t,
- abstractBuiltin('cross'),
- [TypeVec(3, TypeAbstractFloat), TypeVec(3, TypeAbstractFloat)],
- TypeVec(3, TypeAbstractFloat),
+ abstractFloatBuiltin('cross'),
+ [Type.vec(3, Type.abstractFloat), Type.vec(3, Type.abstractFloat)],
+ Type.vec(3, Type.abstractFloat),
t.params,
cases
);
@@ -83,14 +38,7 @@ g.test('f32')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(
- t,
- builtin('cross'),
- [TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
- TypeVec(3, TypeF32),
- t.params,
- cases
- );
+ await run(t, builtin('cross'), [Type.vec3f, Type.vec3f], Type.vec3f, t.params, cases);
});
g.test('f16')
@@ -102,12 +50,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(
- t,
- builtin('cross'),
- [TypeVec(3, TypeF16), TypeVec(3, TypeF16)],
- TypeVec(3, TypeF16),
- t.params,
- cases
- );
+ await run(t, builtin('cross'), [Type.vec3h, Type.vec3h], Type.vec3h, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.cache.ts
new file mode 100644
index 0000000000..16abe41f34
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.cache.ts
@@ -0,0 +1,24 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+// abstract_non_const is empty and not used
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // degrees has an inherited accuracy, so abstract is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].degreesInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('degrees', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts
index f82153ffca..e8589c9933 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'degrees' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<T>
@const fn degrees(e1: T ) -> T
Converts radians to degrees, approximating e1 × 180 ÷ π. Component-wise when T is a vector
@@ -9,46 +9,14 @@ Converts radians to degrees, approximating e1 × 180 ÷ π. Component-wise when
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF16Range, fullF32Range, fullF64Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './degrees.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('degrees', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'finite', FP.f32.degreesInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(
- fullF32Range(),
- 'unfiltered',
- FP.f32.degreesInterval
- );
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'finite', FP.f16.degreesInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(
- fullF16Range(),
- 'unfiltered',
- FP.f16.degreesInterval
- );
- },
- abstract: () => {
- return FP.abstract.generateScalarToIntervalCases(
- fullF64Range(),
- 'finite',
- FP.abstract.degreesInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
@@ -58,12 +26,12 @@ g.test('abstract_float')
.combine('vectorize', [undefined, 2, 3, 4] as const)
)
.fn(async t => {
- const cases = await d.get('abstract');
+ const cases = await d.get('abstract_const');
await run(
t,
- abstractBuiltin('degrees'),
- [TypeAbstractFloat],
- TypeAbstractFloat,
+ abstractFloatBuiltin('degrees'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
t.params,
cases
);
@@ -77,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('degrees'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('degrees'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -91,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('degrees'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('degrees'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/derivatives.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/derivatives.cache.ts
new file mode 100644
index 0000000000..ebd414f395
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/derivatives.cache.ts
@@ -0,0 +1,14 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { sparseScalarF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+export const d = makeCaseCache('derivatives', {
+ scalar: () => {
+ return FP.f32.generateScalarPairToIntervalCases(
+ sparseScalarF32Range(),
+ sparseScalarF32Range(),
+ 'unfiltered',
+ FP.f32.subtractionInterval
+ );
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/derivatives.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/derivatives.ts
new file mode 100644
index 0000000000..2af4a48096
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/derivatives.ts
@@ -0,0 +1,215 @@
+import { GPUTest } from '../../../../../gpu_test.js';
+import { Type, Value } from '../../../../../util/conversion.js';
+import { Case } from '../../case.js';
+import { toComparator } from '../../expectation.js';
+import { packScalarsToVector } from '../../expression.js';
+
+/**
+ * Run a test for a derivative builtin function.
+ * @param t the GPUTest
+ * @param cases list of test cases to run
+ * @param builtin the builtin function to test
+ * @param non_uniform_discard if true, one of each pair of invocations will discard
+ * @param vectorize if defined, the vector width to use (2, 3, or 4)
+ */
+export function runDerivativeTest(
+ t: GPUTest,
+ cases: Case[],
+ builtin: string,
+ non_uniform_discard: boolean,
+ vectorize?: number
+) {
+ // If the 'vectorize' config option was provided, pack the cases into vectors.
+ let type: Type = Type.f32;
+ if (vectorize !== undefined) {
+ const packed = packScalarsToVector([type, type], type, cases, vectorize);
+ cases = packed.cases;
+ type = packed.resultType;
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // The two input values for a given case are distributed to two different invocations in a quad.
+ // We will populate a storage buffer with these input values laid out sequentially:
+ // [ case_0_input_1, case_0_input_0, case_1_input_1, case_1_input_0, ...]
+ //
+ // The render pipeline will be launched several times over a viewport size of (2, 2). Each draw
+ // call will execute a single quad (four fragment invocation), which will exercise two test cases.
+ // Each of these draw calls will use a different instance index, which is forwarded to the
+ // fragment shader. Each invocation will determine its index into the storage buffer using its
+ // fragment position and the instance index for that draw call.
+ //
+ // Consider two draw calls that test 4 cases (c_0, c_1, c_2, c_3).
+ //
+ // For derivatives along the 'x' direction, the mapping from fragment position to case input is:
+ // Quad 0: | c_0_i_1 | c_0_i_0 | Quad 1: | c_2_i_1 | c_2_i_0 |
+ // | c_1_i_1 | c_1_i_0 | | c_3_i_1 | c_3_i_0 |
+ //
+ // For derivatives along the 'y' direction, the mapping from fragment position to case input is:
+ // Quad 0: | c_0_i_1 | c_1_i_1 | Quad 1: | c_2_i_1 | c_3_i_1 |
+ // | c_0_i_0 | c_1_i_0 | | c_2_i_0 | c_3_i_0 |
+ //
+ ////////////////////////////////////////////////////////////////
+
+ // Determine the direction of the derivative ('x' or 'y') from the builtin name.
+ const dir = builtin[3];
+
+ // Determine the WGSL type to use in the shader, and the stride in bytes between values.
+ let valueStride = 4;
+ let wgslType = 'f32';
+ if (vectorize) {
+ wgslType = `vec${vectorize}f`;
+ valueStride = vectorize * 4;
+ if (vectorize === 3) {
+ valueStride = 16;
+ }
+ }
+
+ // Define a vertex shader that draws a triangle over the full viewport, and a fragment shader that
+ // calls the derivative builtin with a value loaded from that fragment's index into the storage
+ // buffer (determined using the quad index and fragment position, as described above).
+ const code = `
+struct CaseInfo {
+ @builtin(position) position: vec4f,
+ @location(0) @interpolate(flat) quad_idx: u32,
+}
+
+@vertex
+fn vert(@builtin(vertex_index) vertex_idx: u32,
+ @builtin(instance_index) instance_idx: u32) -> CaseInfo {
+ const kVertices = array(
+ vec2f(-2, -2),
+ vec2f( 2, -2),
+ vec2f( 0, 2),
+ );
+ return CaseInfo(vec4(kVertices[vertex_idx], 0, 1), instance_idx);
+}
+
+@group(0) @binding(0) var<storage, read> inputs : array<${wgslType}>;
+@group(0) @binding(1) var<storage, read_write> outputs : array<${wgslType}>;
+
+@fragment
+fn frag(info : CaseInfo) {
+ let case_idx = u32(info.position.${dir === 'x' ? 'y' : 'x'});
+ let inv_idx = u32(info.position.${dir});
+ let index = info.quad_idx*4 + case_idx*2 + inv_idx;
+ let input = inputs[index];
+ ${non_uniform_discard ? 'if inv_idx == 0 { discard; }' : ''}
+ outputs[index] = ${builtin}(input);
+}
+`;
+
+ // Create the render pipeline.
+ const module = t.device.createShaderModule({ code });
+ const pipeline = t.device.createRenderPipeline({
+ layout: 'auto',
+ vertex: { module },
+ fragment: { module, targets: [{ format: 'rgba8unorm', writeMask: 0 }] },
+ });
+
+ // Create storage buffers to hold the inputs and outputs.
+ const bufferSize = cases.length * 2 * valueStride;
+ const inputBuffer = t.device.createBuffer({
+ size: bufferSize,
+ usage: GPUBufferUsage.STORAGE,
+ mappedAtCreation: true,
+ });
+ const outputBuffer = t.device.createBuffer({
+ size: bufferSize,
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
+ });
+
+ // Populate the input storage buffer with case input values.
+ const valuesData = new Uint8Array(inputBuffer.getMappedRange());
+ for (let i = 0; i < cases.length; i++) {
+ const inputs = cases[i].input as ReadonlyArray<Value>;
+ inputs[0].copyTo(valuesData, (i * 2 + 1) * valueStride);
+ inputs[1].copyTo(valuesData, i * 2 * valueStride);
+ }
+ inputBuffer.unmap();
+
+ // Create a bind group for the storage buffers.
+ const group = t.device.createBindGroup({
+ entries: [
+ { binding: 0, resource: { buffer: inputBuffer } },
+ { binding: 1, resource: { buffer: outputBuffer } },
+ ],
+ layout: pipeline.getBindGroupLayout(0),
+ });
+
+ // Create a texture to use as a color attachment.
+ // We only need this for launching the desired number of fragment invocations.
+ const colorAttachment = t.device.createTexture({
+ size: { width: 2, height: 2 },
+ format: 'rgba8unorm',
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ // Submit the render pass to the device.
+ const encoder = t.device.createCommandEncoder();
+ const pass = encoder.beginRenderPass({
+ colorAttachments: [
+ {
+ view: colorAttachment.createView(),
+ loadOp: 'clear',
+ storeOp: 'discard',
+ },
+ ],
+ });
+ pass.setPipeline(pipeline);
+ pass.setBindGroup(0, group);
+ for (let quad = 0; quad < cases.length / 2; quad++) {
+ pass.draw(3, 1, undefined, quad);
+ }
+ pass.end();
+ t.queue.submit([encoder.finish()]);
+
+ // Check the outputs match the expected results.
+ t.expectGPUBufferValuesPassCheck(
+ outputBuffer,
+ (outputData: Uint8Array) => {
+ for (let i = 0; i < cases.length; i++) {
+ const c = cases[i];
+
+ // Both invocations involved in the derivative should get the same result.
+ for (let d = 0; d < 2; d++) {
+ if (non_uniform_discard && d === 0) {
+ continue;
+ }
+
+ const index = (i * 2 + d) * valueStride;
+ const result = type.read(outputData, index);
+ const cmp = toComparator(c.expected).compare(result);
+ if (!cmp.matched) {
+ // If this is a coarse derivative, the implementation is also allowed to calculate only
+ // one of the two derivatives and return that result to all of the invocations.
+ if (!builtin.endsWith('Fine')) {
+ const c0 = cases[i % 2 === 0 ? i + 1 : i - 1];
+ const cmp0 = toComparator(c0.expected).compare(result);
+ if (!cmp0.matched) {
+ return new Error(`
+ 1st pair: (${(c.input as Value[]).join(', ')})
+ expected: ${cmp.expected}
+
+ 2nd pair: (${(c0.input as Value[]).join(', ')})
+ expected: ${cmp0.expected}
+
+ returned: ${result}`);
+ }
+ } else {
+ return new Error(`
+ inputs: (${(c.input as Value[]).join(', ')})
+ expected: ${cmp.expected}
+
+ returned: ${result}`);
+ }
+ }
+ }
+ }
+ return undefined;
+ },
+ {
+ type: Uint8Array,
+ typedLength: bufferSize,
+ }
+ );
+}
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts
new file mode 100644
index 0000000000..ccd073bce5
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts
@@ -0,0 +1,99 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Accuracy for determinant is only defined for e, where e is an integer and
+// |e| < quadroot(2**21) [~38],
+// due to computational complexity of calculating the general solution for 4x4,
+// so custom matrices are used.
+//
+// Note: For 2x2 and 3x3 the limits are squareroot and cuberoot instead of
+// quadroot, but using the tighter 4x4 limits for all cases for simplicity.
+const kDeterminantValues = [-38, -10, -5, -1, 0, 1, 5, 10, 38];
+
+const kDeterminantMatrixValues = {
+ 2: kDeterminantValues.map((f, idx) => [
+ [idx % 4 === 0 ? f : idx, idx % 4 === 1 ? f : -idx],
+ [idx % 4 === 2 ? f : -idx, idx % 4 === 3 ? f : idx],
+ ]),
+ 3: kDeterminantValues.map((f, idx) => [
+ [idx % 9 === 0 ? f : idx, idx % 9 === 1 ? f : -idx, idx % 9 === 2 ? f : idx],
+ [idx % 9 === 3 ? f : -idx, idx % 9 === 4 ? f : idx, idx % 9 === 5 ? f : -idx],
+ [idx % 9 === 6 ? f : idx, idx % 9 === 7 ? f : -idx, idx % 9 === 8 ? f : idx],
+ ]),
+ 4: kDeterminantValues.map((f, idx) => [
+ [
+ idx % 16 === 0 ? f : idx,
+ idx % 16 === 1 ? f : -idx,
+ idx % 16 === 2 ? f : idx,
+ idx % 16 === 3 ? f : -idx,
+ ],
+ [
+ idx % 16 === 4 ? f : -idx,
+ idx % 16 === 5 ? f : idx,
+ idx % 16 === 6 ? f : -idx,
+ idx % 16 === 7 ? f : idx,
+ ],
+ [
+ idx % 16 === 8 ? f : idx,
+ idx % 16 === 9 ? f : -idx,
+ idx % 16 === 10 ? f : idx,
+ idx % 16 === 11 ? f : -idx,
+ ],
+ [
+ idx % 16 === 12 ? f : -idx,
+ idx % 16 === 13 ? f : idx,
+ idx % 16 === 14 ? f : -idx,
+ idx % 16 === 15 ? f : idx,
+ ],
+ ]),
+};
+
+// Cases: f32_matDxD_[non_]const
+const f32_cases = ([2, 3, 4] as const)
+ .flatMap(dim =>
+ ([true, false] as const).map(nonConst => ({
+ [`f32_mat${dim}x${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ return FP.f32.generateMatrixToScalarCases(
+ kDeterminantMatrixValues[dim],
+ nonConst ? 'unfiltered' : 'finite',
+ FP.f32.determinantInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+// Cases: f16_matDxD_[non_]const
+const f16_cases = ([2, 3, 4] as const)
+ .flatMap(dim =>
+ ([true, false] as const).map(nonConst => ({
+ [`f16_mat${dim}x${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ return FP.f16.generateMatrixToScalarCases(
+ kDeterminantMatrixValues[dim],
+ nonConst ? 'unfiltered' : 'finite',
+ FP.f16.determinantInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+// Cases: abstract_matDxD
+const abstract_cases = ([2, 3, 4] as const)
+ .map(dim => ({
+ [`abstract_mat${dim}x${dim}`]: () => {
+ return FP.abstract.generateMatrixToScalarCases(
+ kDeterminantMatrixValues[dim],
+ 'finite',
+ // determinant has an inherited accuracy, so abstract is only expected to be as accurate as f32
+ FP.f32.determinantInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('determinant', {
+ ...f32_cases,
+ ...f16_cases,
+ ...abstract_cases,
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts
index f08f4f0b6b..638af80aca 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts
@@ -1,109 +1,37 @@
export const description = `
Execution tests for the 'determinant' builtin function
-T is AbstractFloat, f32, or f16
+T is abstract-float, f32, or f16
@const determinant(e: matCxC<T> ) -> T
Returns the determinant of e.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16, TypeMat } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './determinant.cache.js';
export const g = makeTestGroup(GPUTest);
-// Accuracy for determinant is only defined for e, where e is an integer and
-// |e| < quadroot(2**21) [~38],
-// due to computational complexity of calculating the general solution for 4x4,
-// so custom matrices are used.
-//
-// Note: For 2x2 and 3x3 the limits are squareroot and cuberoot instead of
-// quadroot, but using the tighter 4x4 limits for all cases for simplicity.
-const kDeterminantValues = [-38, -10, -5, -1, 0, 1, 5, 10, 38];
-
-const kDeterminantMatrixValues = {
- 2: kDeterminantValues.map((f, idx) => [
- [idx % 4 === 0 ? f : idx, idx % 4 === 1 ? f : -idx],
- [idx % 4 === 2 ? f : -idx, idx % 4 === 3 ? f : idx],
- ]),
- 3: kDeterminantValues.map((f, idx) => [
- [idx % 9 === 0 ? f : idx, idx % 9 === 1 ? f : -idx, idx % 9 === 2 ? f : idx],
- [idx % 9 === 3 ? f : -idx, idx % 9 === 4 ? f : idx, idx % 9 === 5 ? f : -idx],
- [idx % 9 === 6 ? f : idx, idx % 9 === 7 ? f : -idx, idx % 9 === 8 ? f : idx],
- ]),
- 4: kDeterminantValues.map((f, idx) => [
- [
- idx % 16 === 0 ? f : idx,
- idx % 16 === 1 ? f : -idx,
- idx % 16 === 2 ? f : idx,
- idx % 16 === 3 ? f : -idx,
- ],
- [
- idx % 16 === 4 ? f : -idx,
- idx % 16 === 5 ? f : idx,
- idx % 16 === 6 ? f : -idx,
- idx % 16 === 7 ? f : idx,
- ],
- [
- idx % 16 === 8 ? f : idx,
- idx % 16 === 9 ? f : -idx,
- idx % 16 === 10 ? f : idx,
- idx % 16 === 11 ? f : -idx,
- ],
- [
- idx % 16 === 12 ? f : -idx,
- idx % 16 === 13 ? f : idx,
- idx % 16 === 14 ? f : -idx,
- idx % 16 === 15 ? f : idx,
- ],
- ]),
-};
-
-// Cases: f32_matDxD_[non_]const
-const f32_cases = ([2, 3, 4] as const)
- .flatMap(dim =>
- ([true, false] as const).map(nonConst => ({
- [`f32_mat${dim}x${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f32.generateMatrixToScalarCases(
- kDeterminantMatrixValues[dim],
- nonConst ? 'unfiltered' : 'finite',
- FP.f32.determinantInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: f16_matDxD_[non_]const
-const f16_cases = ([2, 3, 4] as const)
- .flatMap(dim =>
- ([true, false] as const).map(nonConst => ({
- [`f16_mat${dim}x${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f16.generateMatrixToScalarCases(
- kDeterminantMatrixValues[dim],
- nonConst ? 'unfiltered' : 'finite',
- FP.f16.determinantInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-export const d = makeCaseCache('determinant', {
- ...f32_cases,
- ...f16_cases,
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
.desc(`abstract float tests`)
- .params(u => u.combine('inputSource', allInputSources).combine('dimension', [2, 3, 4] as const))
- .unimplemented();
+ .params(u => u.combine('inputSource', onlyConstInputSource).combine('dim', [2, 3, 4] as const))
+ .fn(async t => {
+ const dim = t.params.dim;
+ const cases = await d.get(`abstract_mat${dim}x${dim}`);
+ await run(
+ t,
+ abstractFloatBuiltin('determinant'),
+ [Type.mat(dim, dim, Type.abstractFloat)],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
@@ -116,7 +44,7 @@ g.test('f32')
? `f32_mat${dim}x${dim}_const`
: `f32_mat${dim}x${dim}_non_const`
);
- await run(t, builtin('determinant'), [TypeMat(dim, dim, TypeF32)], TypeF32, t.params, cases);
+ await run(t, builtin('determinant'), [Type.mat(dim, dim, Type.f32)], Type.f32, t.params, cases);
});
g.test('f16')
@@ -133,5 +61,5 @@ g.test('f16')
? `f16_mat${dim}x${dim}_const`
: `f16_mat${dim}x${dim}_non_const`
);
- await run(t, builtin('determinant'), [TypeMat(dim, dim, TypeF16)], TypeF16, t.params, cases);
+ await run(t, builtin('determinant'), [Type.mat(dim, dim, Type.f16)], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.cache.ts
new file mode 100644
index 0000000000..0b37190cf7
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.cache.ts
@@ -0,0 +1,49 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const scalar_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarPairToIntervalCases(
+ FP[trait].scalarRange(),
+ FP[trait].scalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // distance has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].distanceInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+// Cases: [f32|f16|abstract]_vecN_[non_]const
+const vec_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(dim =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateVectorPairToIntervalCases(
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseVectorRange(dim),
+ nonConst ? 'unfiltered' : 'finite',
+ // distance has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].distanceInterval
+ );
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('distance', {
+ ...scalar_cases,
+ ...vec_cases,
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts
index 13cddf6403..5f03c49319 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'distance' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn distance(e1: T ,e2: T ) -> f32
Returns the distance between e1 and e2 (e.g. length(e1-e2)).
@@ -10,97 +10,77 @@ Returns the distance between e1 and e2 (e.g. length(e1-e2)).
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import {
- fullF32Range,
- fullF16Range,
- sparseVectorF32Range,
- sparseVectorF16Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
-
-import { builtin } from './builtin.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
+
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './distance.cache.js';
export const g = makeTestGroup(GPUTest);
-// Cases: f32_vecN_[non_]const
-const f32_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f32_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f32.generateVectorPairToIntervalCases(
- sparseVectorF32Range(n),
- sparseVectorF32Range(n),
- nonConst ? 'unfiltered' : 'finite',
- FP.f32.distanceInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: f16_vecN_[non_]const
-const f16_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f16_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f16.generateVectorPairToIntervalCases(
- sparseVectorF16Range(n),
- sparseVectorF16Range(n),
- nonConst ? 'unfiltered' : 'finite',
- FP.f16.distanceInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-export const d = makeCaseCache('distance', {
- f32_const: () => {
- return FP.f32.generateScalarPairToIntervalCases(
- fullF32Range(),
- fullF32Range(),
- 'finite',
- FP.f32.distanceInterval
- );
- },
- f32_non_const: () => {
- return FP.f32.generateScalarPairToIntervalCases(
- fullF32Range(),
- fullF32Range(),
- 'unfiltered',
- FP.f32.distanceInterval
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(`abstract float tests`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('distance'),
+ [Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
);
- },
- ...f32_vec_cases,
- f16_const: () => {
- return FP.f16.generateScalarPairToIntervalCases(
- fullF16Range(),
- fullF16Range(),
- 'finite',
- FP.f16.distanceInterval
+ });
+
+g.test('abstract_float_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec2s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec2_const');
+ await run(
+ t,
+ abstractFloatBuiltin('distance'),
+ [Type.vec2af, Type.vec2af],
+ Type.abstractFloat,
+ t.params,
+ cases
);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarPairToIntervalCases(
- fullF16Range(),
- fullF16Range(),
- 'unfiltered',
- FP.f16.distanceInterval
+ });
+
+g.test('abstract_float_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec3s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec3_const');
+ await run(
+ t,
+ abstractFloatBuiltin('distance'),
+ [Type.vec3af, Type.vec3af],
+ Type.abstractFloat,
+ t.params,
+ cases
);
- },
- ...f16_vec_cases,
-});
+ });
-g.test('abstract_float')
- .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
- .desc(`abstract float tests`)
- .params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
- )
- .unimplemented();
+g.test('abstract_float_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec4s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec4_const');
+ await run(
+ t,
+ abstractFloatBuiltin('distance'),
+ [Type.vec4af, Type.vec4af],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
@@ -108,7 +88,7 @@ g.test('f32')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('distance'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('distance'), [Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f32_vec2')
@@ -119,14 +99,7 @@ g.test('f32_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
);
- await run(
- t,
- builtin('distance'),
- [TypeVec(2, TypeF32), TypeVec(2, TypeF32)],
- TypeF32,
- t.params,
- cases
- );
+ await run(t, builtin('distance'), [Type.vec2f, Type.vec2f], Type.f32, t.params, cases);
});
g.test('f32_vec3')
@@ -137,14 +110,7 @@ g.test('f32_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
);
- await run(
- t,
- builtin('distance'),
- [TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
- TypeF32,
- t.params,
- cases
- );
+ await run(t, builtin('distance'), [Type.vec3f, Type.vec3f], Type.f32, t.params, cases);
});
g.test('f32_vec4')
@@ -155,14 +121,7 @@ g.test('f32_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
);
- await run(
- t,
- builtin('distance'),
- [TypeVec(4, TypeF32), TypeVec(4, TypeF32)],
- TypeF32,
- t.params,
- cases
- );
+ await run(t, builtin('distance'), [Type.vec4f, Type.vec4f], Type.f32, t.params, cases);
});
g.test('f16')
@@ -174,7 +133,7 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('distance'), [TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('distance'), [Type.f16, Type.f16], Type.f16, t.params, cases);
});
g.test('f16_vec2')
@@ -188,14 +147,7 @@ g.test('f16_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const'
);
- await run(
- t,
- builtin('distance'),
- [TypeVec(2, TypeF16), TypeVec(2, TypeF16)],
- TypeF16,
- t.params,
- cases
- );
+ await run(t, builtin('distance'), [Type.vec2h, Type.vec2h], Type.f16, t.params, cases);
});
g.test('f16_vec3')
@@ -209,14 +161,7 @@ g.test('f16_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const'
);
- await run(
- t,
- builtin('distance'),
- [TypeVec(3, TypeF16), TypeVec(3, TypeF16)],
- TypeF16,
- t.params,
- cases
- );
+ await run(t, builtin('distance'), [Type.vec3h, Type.vec3h], Type.f16, t.params, cases);
});
g.test('f16_vec4')
@@ -230,12 +175,5 @@ g.test('f16_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const'
);
- await run(
- t,
- builtin('distance'),
- [TypeVec(4, TypeF16), TypeVec(4, TypeF16)],
- TypeF16,
- t.params,
- cases
- );
+ await run(t, builtin('distance'), [Type.vec4h, Type.vec4h], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.cache.ts
new file mode 100644
index 0000000000..6d1f22def1
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.cache.ts
@@ -0,0 +1,118 @@
+import { ROArrayArray } from '../../../../../../common/util/types.js';
+import { assert } from '../../../../../../common/util/util.js';
+import { kValue } from '../../../../../util/constants.js';
+import { FP } from '../../../../../util/floating_point.js';
+import {
+ calculatePermutations,
+ sparseVectorI32Range,
+ sparseVectorI64Range,
+ sparseVectorU32Range,
+ vectorI32Range,
+ vectorI64Range,
+ vectorU32Range,
+} from '../../../../../util/math.js';
+import {
+ generateVectorVectorToI32Cases,
+ generateVectorVectorToI64Cases,
+ generateVectorVectorToU32Cases,
+} from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+function ai_dot(x: bigint[], y: bigint[]): bigint | undefined {
+ assert(x.length === y.length, 'Cannot calculate dot for vectors of different lengths');
+ const multiplications = x.map((_, idx) => x[idx] * y[idx]);
+ if (multiplications.some(kValue.i64.isOOB)) return undefined;
+
+ const result = multiplications.reduce((prev, curr) => prev + curr);
+ if (kValue.i64.isOOB(result)) return undefined;
+
+ // The spec does not state the ordering of summation, so all the
+ // permutations are calculated and the intermediate results checked for
+ // going OOB. vec2 does not need permutations, since a + b === b + a.
+ // All the end results should be the same regardless of the order if the
+ // intermediate additions stay inbounds.
+ if (x.length !== 2) {
+ let wentOOB: boolean = false;
+ const permutations: ROArrayArray<bigint> = calculatePermutations(multiplications);
+ permutations.forEach(p => {
+ if (!wentOOB) {
+ p.reduce((prev, curr) => {
+ const next = prev + curr;
+ if (kValue.i64.isOOB(next)) {
+ wentOOB = true;
+ }
+ return next;
+ });
+ }
+ });
+
+ if (wentOOB) return undefined;
+ }
+
+ return !kValue.i64.isOOB(result) ? result : undefined;
+}
+
+function ci_dot(x: number[], y: number[]): number | undefined {
+ assert(x.length === y.length, 'Cannot calculate dot for vectors of different lengths');
+ return x.reduce((prev, _, idx) => prev + Math.imul(x[idx], y[idx]), 0);
+}
+
+// Cases: [f32|f16|abstract]_vecN_[non_]const
+const float_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(N =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait === 'abstract' ? 'abstract_float' : trait}_vec${N}_${
+ nonConst ? 'non_const' : 'const'
+ }`]: () => {
+ // Emit an empty array for not const abstract float, since they will never be run
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ // vec3 and vec4 require calculating all possible permutations, so their runtime is much
+ // longer per test, so only using sparse vectors for them.
+ return FP[trait].generateVectorPairToIntervalCases(
+ N === 2 ? FP[trait].vectorRange(2) : FP[trait].sparseVectorRange(N),
+ N === 2 ? FP[trait].vectorRange(2) : FP[trait].sparseVectorRange(N),
+ nonConst ? 'unfiltered' : 'finite',
+ // dot has an inherited accuracy, so abstract is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].dotInterval
+ );
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+const cases = {
+ ...float_cases,
+ abstract_int_vec2: () => {
+ return generateVectorVectorToI64Cases(vectorI64Range(2), vectorI64Range(2), ai_dot);
+ },
+ abstract_int_vec3: () => {
+ return generateVectorVectorToI64Cases(sparseVectorI64Range(3), sparseVectorI64Range(3), ai_dot);
+ },
+ abstract_int_vec4: () => {
+ return generateVectorVectorToI64Cases(sparseVectorI64Range(4), sparseVectorI64Range(4), ai_dot);
+ },
+ i32_vec2: () => {
+ return generateVectorVectorToI32Cases(vectorI32Range(2), vectorI32Range(2), ci_dot);
+ },
+ i32_vec3: () => {
+ return generateVectorVectorToI32Cases(sparseVectorI32Range(3), sparseVectorI32Range(3), ci_dot);
+ },
+ i32_vec4: () => {
+ return generateVectorVectorToI32Cases(sparseVectorI32Range(4), sparseVectorI32Range(4), ci_dot);
+ },
+ u32_vec2: () => {
+ return generateVectorVectorToU32Cases(vectorU32Range(2), vectorU32Range(2), ci_dot);
+ },
+ u32_vec3: () => {
+ return generateVectorVectorToU32Cases(sparseVectorU32Range(3), sparseVectorU32Range(3), ci_dot);
+ },
+ u32_vec4: () => {
+ return generateVectorVectorToU32Cases(sparseVectorU32Range(4), sparseVectorU32Range(4), ci_dot);
+ },
+};
+
+export const d = makeCaseCache('dot', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts
index 2726546183..188409bf21 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts
@@ -1,87 +1,182 @@
export const description = `
Execution tests for the 'dot' builtin function
-T is AbstractInt, AbstractFloat, i32, u32, f32, or f16
+T is Type.abstractInt, Type.abstractFloat, i32, u32, f32, or f16
@const fn dot(e1: vecN<T>,e2: vecN<T>) -> T
Returns the dot product of e1 and e2.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { sparseVectorF32Range, vectorF32Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js';
+import { d } from './dot.cache.js';
export const g = makeTestGroup(GPUTest);
-// Cases: [f32|f16]_vecN_[non_]const
-const cases = (['f32', 'f16'] as const)
- .flatMap(trait =>
- ([2, 3, 4] as const).flatMap(N =>
- ([true, false] as const).map(nonConst => ({
- [`${trait}_vec${N}_${nonConst ? 'non_const' : 'const'}`]: () => {
- // vec3 and vec4 require calculating all possible permutations, so their runtime is much
- // longer per test, so only using sparse vectors for them.
- return FP[trait].generateVectorPairToIntervalCases(
- N === 2 ? vectorF32Range(2) : sparseVectorF32Range(N),
- N === 2 ? vectorF32Range(2) : sparseVectorF32Range(N),
- nonConst ? 'unfiltered' : 'finite',
- FP[trait].dotInterval
- );
- },
- }))
- )
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-export const d = makeCaseCache('dot', cases);
-
-g.test('abstract_int')
- .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
- .desc(`abstract int tests`)
+g.test('abstract_int_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`abstract integer tests using vec2s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_int_vec2');
+ await run(
+ t,
+ abstractIntBuiltin('dot'),
+ [Type.vec(2, Type.abstractInt), Type.vec(2, Type.abstractInt)],
+ Type.abstractInt,
+ t.params,
+ cases
+ );
+ });
+
+g.test('abstract_int_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`abstract integer tests using vec3s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_int_vec3');
+ await run(
+ t,
+ abstractIntBuiltin('dot'),
+ [Type.vec(3, Type.abstractInt), Type.vec(3, Type.abstractInt)],
+ Type.abstractInt,
+ t.params,
+ cases
+ );
+ });
+
+g.test('abstract_int_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`abstract integer tests using vec4s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_int_vec4');
+ await run(
+ t,
+ abstractIntBuiltin('dot'),
+ [Type.vec(4, Type.abstractInt), Type.vec(4, Type.abstractInt)],
+ Type.abstractInt,
+ t.params,
+ cases
+ );
+ });
+
+g.test('i32_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`i32 tests using vec2s`)
.params(u => u.combine('inputSource', allInputSources))
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('i32_vec2');
+ await run(t, builtin('dot'), [Type.vec2i, Type.vec2i], Type.i32, t.params, cases);
+ });
-g.test('i32')
+g.test('i32_vec3')
.specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
- .desc(`i32 tests`)
+ .desc(`i32 tests using vec3s`)
.params(u => u.combine('inputSource', allInputSources))
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('i32_vec3');
+ await run(t, builtin('dot'), [Type.vec3i, Type.vec3i], Type.i32, t.params, cases);
+ });
-g.test('u32')
+g.test('i32_vec4')
.specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
- .desc(`u32 tests`)
+ .desc(`i32 tests using vec4s`)
.params(u => u.combine('inputSource', allInputSources))
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('i32_vec4');
+ await run(t, builtin('dot'), [Type.vec4i, Type.vec4i], Type.i32, t.params, cases);
+ });
-g.test('abstract_float')
+g.test('u32_vec2')
.specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
- .desc(`abstract float test`)
+ .desc(`u32 tests using vec2s`)
.params(u => u.combine('inputSource', allInputSources))
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('u32_vec2');
+ await run(t, builtin('dot'), [Type.vec2u, Type.vec2u], Type.u32, t.params, cases);
+ });
-g.test('f32_vec2')
+g.test('u32_vec3')
.specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
- .desc(`f32 tests using vec2s`)
+ .desc(`u32 tests using vec3s`)
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
- const cases = await d.get(
- t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
+ const cases = await d.get('u32_vec3');
+ await run(t, builtin('dot'), [Type.vec3u, Type.vec3u], Type.u32, t.params, cases);
+ });
+
+g.test('u32_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`u32 tests using vec4s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get('u32_vec4');
+ await run(t, builtin('dot'), [Type.vec4u, Type.vec4u], Type.u32, t.params, cases);
+ });
+
+g.test('abstract_float_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`abstract float tests using vec2s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_float_vec2_const');
+ await run(
+ t,
+ abstractFloatBuiltin('dot'),
+ [Type.vec(2, Type.abstractFloat), Type.vec(2, Type.abstractFloat)],
+ Type.abstractFloat,
+ t.params,
+ cases
);
+ });
+
+g.test('abstract_float_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`abstract float tests using vec3s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_float_vec3_const');
await run(
t,
- builtin('dot'),
- [TypeVec(2, TypeF32), TypeVec(2, TypeF32)],
- TypeF32,
+ abstractFloatBuiltin('dot'),
+ [Type.vec(3, Type.abstractFloat), Type.vec(3, Type.abstractFloat)],
+ Type.abstractFloat,
t.params,
cases
);
});
+g.test('abstract_float_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`abstract float tests using vec4s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_float_vec4_const');
+ await run(
+ t,
+ abstractFloatBuiltin('dot'),
+ [Type.vec(4, Type.abstractFloat), Type.vec(4, Type.abstractFloat)],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
+
+g.test('f32_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
+ .desc(`f32 tests using vec2s`)
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cases = await d.get(
+ t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
+ );
+ await run(t, builtin('dot'), [Type.vec2f, Type.vec2f], Type.f32, t.params, cases);
+ });
+
g.test('f32_vec3')
.specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions')
.desc(`f32 tests using vec3s`)
@@ -90,14 +185,7 @@ g.test('f32_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
);
- await run(
- t,
- builtin('dot'),
- [TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
- TypeF32,
- t.params,
- cases
- );
+ await run(t, builtin('dot'), [Type.vec3f, Type.vec3f], Type.f32, t.params, cases);
});
g.test('f32_vec4')
@@ -108,14 +196,7 @@ g.test('f32_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
);
- await run(
- t,
- builtin('dot'),
- [TypeVec(4, TypeF32), TypeVec(4, TypeF32)],
- TypeF32,
- t.params,
- cases
- );
+ await run(t, builtin('dot'), [Type.vec4f, Type.vec4f], Type.f32, t.params, cases);
});
g.test('f16_vec2')
@@ -129,14 +210,7 @@ g.test('f16_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const'
);
- await run(
- t,
- builtin('dot'),
- [TypeVec(2, TypeF16), TypeVec(2, TypeF16)],
- TypeF16,
- t.params,
- cases
- );
+ await run(t, builtin('dot'), [Type.vec2h, Type.vec2h], Type.f16, t.params, cases);
});
g.test('f16_vec3')
@@ -150,14 +224,7 @@ g.test('f16_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const'
);
- await run(
- t,
- builtin('dot'),
- [TypeVec(3, TypeF16), TypeVec(3, TypeF16)],
- TypeF16,
- t.params,
- cases
- );
+ await run(t, builtin('dot'), [Type.vec3h, Type.vec3h], Type.f16, t.params, cases);
});
g.test('f16_vec4')
@@ -171,12 +238,5 @@ g.test('f16_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const'
);
- await run(
- t,
- builtin('dot'),
- [TypeVec(4, TypeF16), TypeVec(4, TypeF16)],
- TypeF16,
- t.params,
- cases
- );
+ await run(t, builtin('dot'), [Type.vec4h, Type.vec4h], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts
new file mode 100644
index 0000000000..de537c473e
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts
@@ -0,0 +1,74 @@
+export const description = `
+Execution tests for the 'dot4I8Packed' builtin function
+
+@const fn dot4I8Packed(e1: u32 ,e2: u32) -> i32
+e1 and e2 are interpreted as vectors with four 8-bit signed integer components. Return the signed
+integer dot product of these two vectors. Each component is sign-extended to i32 before performing
+the multiply, and then the add operations are done in WGSL i32 with wrapping behaviour.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { Type, i32, u32 } from '../../../../../util/conversion.js';
+import { Case } from '../../case.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('basic')
+ .specURL('https://www.w3.org/TR/WGSL/#dot4I8Packed-builtin')
+ .desc(
+ `
+@const fn dot4I8Packed(e1: u32, e2: u32) -> i32
+ `
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const dot4I8Packed = (e1: number, e2: number) => {
+ let result = 0;
+ for (let i = 0; i < 4; ++i) {
+ let e1_i = (e1 >> (i * 8)) & 0xff;
+ if (e1_i >= 128) {
+ e1_i -= 256;
+ }
+ let e2_i = (e2 >> (i * 8)) & 0xff;
+ if (e2_i >= 128) {
+ e2_i -= 256;
+ }
+ result += e1_i * e2_i;
+ }
+ return result;
+ };
+
+ const testInputs = [
+ // dot({0, 0, 0, 0}, {0, 0, 0, 0})
+ [0, 0],
+ // dot({127, 127, 127, 127}, {127, 127, 127, 127})
+ [0x7f7f7f7f, 0x7f7f7f7f],
+ // dot({-128, -128, -128, -128}, {-128, -128, -128, -128})
+ [0x80808080, 0x80808080],
+ // dot({127, 127, 127, 127}, {-128, -128, -128, -128})
+ [0x7f7f7f7f, 0x80808080],
+ // dot({1, 2, 3, 4}, {5, 6, 7, 8})
+ [0x01020304, 0x05060708],
+ // dot({1, 2, 3, 4}, {-1, -2, -3, -4})
+ [0x01020304, 0xfffefdfc],
+ // dot({-5, -6, -7, -8}, {5, 6, 7, 8})
+ [0xfbfaf9f8, 0x05060708],
+ // dot({-9, -10, -11, -12}, {-13, -14, -15, -16})
+ [0xf7f6f5f4, 0xf3f2f1f0],
+ ] as const;
+
+ const makeCase = (x: number, y: number): Case => {
+ return { input: [u32(x), u32(y)], expected: i32(dot4I8Packed(x, y)) };
+ };
+ const cases: Array<Case> = testInputs.flatMap(v => {
+ return [makeCase(...(v as [number, number]))];
+ });
+
+ await run(t, builtin('dot4I8Packed'), [Type.u32, Type.u32], Type.i32, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts
new file mode 100644
index 0000000000..a12a3d0123
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts
@@ -0,0 +1,59 @@
+export const description = `
+Execution tests for the 'dot4U8Packed' builtin function
+
+@const fn dot4U8Packed(e1: u32 ,e2: u32) -> u32
+e1 and e2 are interpreted as vectors with four 8-bit unsigned integer components. Return the
+unsigned integer dot product of these two vectors.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { Type, u32 } from '../../../../../util/conversion.js';
+import { Case } from '../../case.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('basic')
+ .specURL('https://www.w3.org/TR/WGSL/#dot4U8Packed-builtin')
+ .desc(
+ `
+@const fn dot4U8Packed(e1: u32, e2: u32) -> u32
+ `
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const dot4U8Packed = (e1: number, e2: number) => {
+ let result = 0;
+ for (let i = 0; i < 4; ++i) {
+ const e1_i = (e1 >> (i * 8)) & 0xff;
+ const e2_i = (e2 >> (i * 8)) & 0xff;
+ result += e1_i * e2_i;
+ }
+ return result;
+ };
+
+ const testInputs = [
+ // dot({0, 0, 0, 0}, {0, 0, 0, 0})
+ [0, 0],
+ // dot({255u, 255u, 255u, 255u}, {255u, 255u, 255u, 255u})
+ [0xffffffff, 0xffffffff],
+ // dot({1u, 2u, 3u, 4u}, {5u, 6u, 7u, 8u})
+ [0x01020304, 0x05060708],
+ // dot({120u, 90u, 60u, 30u}, {50u, 100u, 150u, 200u})
+ [0x785a3c1e, 0x326496c8],
+ ] as const;
+
+ const makeCase = (x: number, y: number): Case => {
+ return { input: [u32(x), u32(y)], expected: u32(dot4U8Packed(x, y)) };
+ };
+ const cases: Array<Case> = testInputs.flatMap(v => {
+ return [makeCase(...(v as [number, number]))];
+ });
+
+ await run(t, builtin('dot4U8Packed'), [Type.u32, Type.u32], Type.u32, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts
index 287a51c699..d922603a9f 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts
@@ -10,14 +10,22 @@ The result is the same as either dpdxFine(e) or dpdxCoarse(e).
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { allInputSources } from '../../expression.js';
+
+import { d } from './derivatives.cache.js';
+import { runDerivativeTest } from './derivatives.js';
export const g = makeTestGroup(GPUTest);
+const builtin = 'dpdx';
+
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
- .desc(`f32 tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ .combine('non_uniform_discard', [false, true])
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('scalar');
+ runDerivativeTest(t, cases, builtin, t.params.non_uniform_discard, t.params.vectorize);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts
index 67a75bb010..488f7e5440 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts
@@ -9,14 +9,22 @@ This may result in fewer unique positions that dpdxFine(e).
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { allInputSources } from '../../expression.js';
+
+import { d } from './derivatives.cache.js';
+import { runDerivativeTest } from './derivatives.js';
export const g = makeTestGroup(GPUTest);
+const builtin = 'dpdxCoarse';
+
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
- .desc(`f32 tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ .combine('non_uniform_discard', [false, true])
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('scalar');
+ runDerivativeTest(t, cases, builtin, t.params.non_uniform_discard, t.params.vectorize);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts
index 91d65b990b..180aec2ec4 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts
@@ -8,14 +8,22 @@ Returns the partial derivative of e with respect to window x coordinates.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { allInputSources } from '../../expression.js';
+
+import { d } from './derivatives.cache.js';
+import { runDerivativeTest } from './derivatives.js';
export const g = makeTestGroup(GPUTest);
+const builtin = 'dpdxFine';
+
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
- .desc(`f32 tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ .combine('non_uniform_discard', [false, true])
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('scalar');
+ runDerivativeTest(t, cases, builtin, t.params.non_uniform_discard, t.params.vectorize);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts
index 0cd9cafdb9..94df913d5c 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts
@@ -9,14 +9,22 @@ The result is the same as either dpdyFine(e) or dpdyCoarse(e).
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { allInputSources } from '../../expression.js';
+
+import { d } from './derivatives.cache.js';
+import { runDerivativeTest } from './derivatives.js';
export const g = makeTestGroup(GPUTest);
+const builtin = 'dpdy';
+
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
- .desc(`f32 tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ .combine('non_uniform_discard', [false, true])
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('scalar');
+ runDerivativeTest(t, cases, builtin, t.params.non_uniform_discard, t.params.vectorize);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts
index f06869fdc2..2974475a6c 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts
@@ -9,14 +9,22 @@ This may result in fewer unique positions that dpdyFine(e).
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { allInputSources } from '../../expression.js';
+
+import { d } from './derivatives.cache.js';
+import { runDerivativeTest } from './derivatives.js';
export const g = makeTestGroup(GPUTest);
+const builtin = 'dpdyCoarse';
+
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
- .desc(`f32 test`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ .combine('non_uniform_discard', [false, true])
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('scalar');
+ runDerivativeTest(t, cases, builtin, t.params.non_uniform_discard, t.params.vectorize);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts
index e09761de95..5772024cc6 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts
@@ -8,14 +8,22 @@ Returns the partial derivative of e with respect to window y coordinates.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { allInputSources } from '../../expression.js';
+
+import { d } from './derivatives.cache.js';
+import { runDerivativeTest } from './derivatives.js';
export const g = makeTestGroup(GPUTest);
+const builtin = 'dpdyFine';
+
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions')
- .desc(`f32 tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
+ .combine('non_uniform_discard', [false, true])
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('scalar');
+ runDerivativeTest(t, cases, builtin, t.params.non_uniform_discard, t.params.vectorize);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.cache.ts
new file mode 100644
index 0000000000..5ecc8b2d97
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.cache.ts
@@ -0,0 +1,44 @@
+import { kValue } from '../../../../../util/constants.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { biasedRange, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// floor(ln(max f32 value)) = 88, so exp(88) will be within range of a f32, but exp(89) will not
+// floor(ln(max f64 value)) = 709, so exp(709) can be handled by the testing framework, but exp(710) will misbehave
+const f32_inputs = [
+ 0, // Returns 1 by definition
+ -89, // Returns subnormal value
+ kValue.f32.negative.min, // Closest to returning 0 as possible
+ ...biasedRange(kValue.f32.negative.max, -88, 100),
+ ...biasedRange(kValue.f32.positive.min, 88, 100),
+ ...linearRange(89, 709, 10), // Overflows f32, but not f64
+];
+
+// floor(ln(max f16 value)) = 11, so exp(11) will be within range of a f16, but exp(12) will not
+const f16_inputs = [
+ 0, // Returns 1 by definition
+ -12, // Returns subnormal value
+ kValue.f16.negative.min, // Closest to returning 0 as possible
+ ...biasedRange(kValue.f16.negative.max, -11, 100),
+ ...biasedRange(kValue.f16.positive.min, 11, 100),
+ ...linearRange(12, 709, 10), // Overflows f16, but not f64
+];
+
+export const d = makeCaseCache('exp', {
+ f32_const: () => {
+ return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.expInterval);
+ },
+ f32_non_const: () => {
+ return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.expInterval);
+ },
+ f16_const: () => {
+ return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.expInterval);
+ },
+ f16_non_const: () => {
+ return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.expInterval);
+ },
+ abstract: () => {
+ // exp has an ulp accuracy, so is only expected to be as accurate as f32
+ return FP.abstract.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.expInterval);
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts
index 8b1ced3cab..e6bf65fe4f 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'exp' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn exp(e1: T ) -> T
Returns the natural exponentiation of e1 (e.g. e^e1). Component-wise when T is a vector.
@@ -9,60 +9,33 @@ Returns the natural exponentiation of e1 (e.g. e^e1). Component-wise when T is a
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { kValue } from '../../../../../util/constants.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { biasedRange, linearRange } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './exp.cache.js';
export const g = makeTestGroup(GPUTest);
-// floor(ln(max f32 value)) = 88, so exp(88) will be within range of a f32, but exp(89) will not
-// floor(ln(max f64 value)) = 709, so exp(709) can be handled by the testing framework, but exp(710) will misbehave
-const f32_inputs = [
- 0, // Returns 1 by definition
- -89, // Returns subnormal value
- kValue.f32.negative.min, // Closest to returning 0 as possible
- ...biasedRange(kValue.f32.negative.max, -88, 100),
- ...biasedRange(kValue.f32.positive.min, 88, 100),
- ...linearRange(89, 709, 10), // Overflows f32, but not f64
-];
-
-// floor(ln(max f16 value)) = 11, so exp(11) will be within range of a f16, but exp(12) will not
-const f16_inputs = [
- 0, // Returns 1 by definition
- -12, // Returns subnormal value
- kValue.f16.negative.min, // Closest to returning 0 as possible
- ...biasedRange(kValue.f16.negative.max, -11, 100),
- ...biasedRange(kValue.f16.positive.min, 11, 100),
- ...linearRange(12, 709, 10), // Overflows f16, but not f64
-];
-
-export const d = makeCaseCache('exp', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.expInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.expInterval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.expInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.expInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('exp'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -72,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('exp'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('exp'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -86,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('exp'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('exp'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.cache.ts
new file mode 100644
index 0000000000..e2d0f1c16c
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.cache.ts
@@ -0,0 +1,44 @@
+import { kValue } from '../../../../../util/constants.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { biasedRange, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// floor(log2(max f32 value)) = 127, so exp2(127) will be within range of a f32, but exp2(128) will not
+// floor(ln(max f64 value)) = 1023, so exp2(1023) can be handled by the testing framework, but exp2(1024) will misbehave
+const f32_inputs = [
+ 0, // Returns 1 by definition
+ -128, // Returns subnormal value
+ kValue.f32.negative.min, // Closest to returning 0 as possible
+ ...biasedRange(kValue.f32.negative.max, -127, 100),
+ ...biasedRange(kValue.f32.positive.min, 127, 100),
+ ...linearRange(128, 1023, 10), // Overflows f32, but not f64
+];
+
+// floor(log2(max f16 value)) = 15, so exp2(15) will be within range of a f16, but exp2(15) will not
+const f16_inputs = [
+ 0, // Returns 1 by definition
+ -16, // Returns subnormal value
+ kValue.f16.negative.min, // Closest to returning 0 as possible
+ ...biasedRange(kValue.f16.negative.max, -15, 100),
+ ...biasedRange(kValue.f16.positive.min, 15, 100),
+ ...linearRange(16, 1023, 10), // Overflows f16, but not f64
+];
+
+export const d = makeCaseCache('exp2', {
+ f32_const: () => {
+ return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.exp2Interval);
+ },
+ f32_non_const: () => {
+ return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.exp2Interval);
+ },
+ f16_const: () => {
+ return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.exp2Interval);
+ },
+ f16_non_const: () => {
+ return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.exp2Interval);
+ },
+ abstract: () => {
+ // exp2 has an ulp accuracy, so is only expected to be as accurate as f32
+ return FP.abstract.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.exp2Interval);
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts
index 67e123cb30..f47d2e5066 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'exp2' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn exp2(e: T ) -> T
Returns 2 raised to the power e (e.g. 2^e). Component-wise when T is a vector.
@@ -9,60 +9,33 @@ Returns 2 raised to the power e (e.g. 2^e). Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { kValue } from '../../../../../util/constants.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { biasedRange, linearRange } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './exp2.cache.js';
export const g = makeTestGroup(GPUTest);
-// floor(log2(max f32 value)) = 127, so exp2(127) will be within range of a f32, but exp2(128) will not
-// floor(ln(max f64 value)) = 1023, so exp2(1023) can be handled by the testing framework, but exp2(1024) will misbehave
-const f32_inputs = [
- 0, // Returns 1 by definition
- -128, // Returns subnormal value
- kValue.f32.negative.min, // Closest to returning 0 as possible
- ...biasedRange(kValue.f32.negative.max, -127, 100),
- ...biasedRange(kValue.f32.positive.min, 127, 100),
- ...linearRange(128, 1023, 10), // Overflows f32, but not f64
-];
-
-// floor(log2(max f16 value)) = 15, so exp2(15) will be within range of a f16, but exp2(15) will not
-const f16_inputs = [
- 0, // Returns 1 by definition
- -16, // Returns subnormal value
- kValue.f16.negative.min, // Closest to returning 0 as possible
- ...biasedRange(kValue.f16.negative.max, -15, 100),
- ...biasedRange(kValue.f16.positive.min, 15, 100),
- ...linearRange(16, 1023, 10), // Overflows f16, but not f64
-];
-
-export const d = makeCaseCache('exp2', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.exp2Interval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.exp2Interval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.exp2Interval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.exp2Interval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('exp2'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -72,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('exp2'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('exp2'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -86,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('exp2'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('exp2'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts
index d535bf5d74..ef04b661bd 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts
@@ -33,17 +33,7 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import {
- i32Bits,
- TypeI32,
- u32,
- TypeU32,
- u32Bits,
- vec2,
- vec3,
- vec4,
- TypeVec,
-} from '../../../../../util/conversion.js';
+import { i32Bits, Type, u32, u32Bits, vec2, vec3, vec4 } from '../../../../../util/conversion.js';
import { allInputSources, Config, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -57,7 +47,7 @@ g.test('u32')
.fn(async t => {
const cfg: Config = t.params;
- const T = t.params.width === 1 ? TypeU32 : TypeVec(t.params.width, TypeU32);
+ const T = t.params.width === 1 ? Type.u32 : Type.vec(t.params.width, Type.u32);
const V = (x: number, y?: number, z?: number, w?: number) => {
y = y === undefined ? x : y;
@@ -193,7 +183,7 @@ g.test('u32')
);
}
- await run(t, builtin('extractBits'), [T, TypeU32, TypeU32], T, cfg, cases);
+ await run(t, builtin('extractBits'), [T, Type.u32, Type.u32], T, cfg, cases);
});
g.test('i32')
@@ -203,7 +193,7 @@ g.test('i32')
.fn(async t => {
const cfg: Config = t.params;
- const T = t.params.width === 1 ? TypeI32 : TypeVec(t.params.width, TypeI32);
+ const T = t.params.width === 1 ? Type.i32 : Type.vec(t.params.width, Type.i32);
const V = (x: number, y?: number, z?: number, w?: number) => {
y = y === undefined ? x : y;
@@ -333,5 +323,5 @@ g.test('i32')
);
}
- await run(t, builtin('extractBits'), [T, TypeU32, TypeU32], T, cfg, cases);
+ await run(t, builtin('extractBits'), [T, Type.u32, Type.u32], T, cfg, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.cache.ts
new file mode 100644
index 0000000000..17a5d0fd8f
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.cache.ts
@@ -0,0 +1,125 @@
+import { ROArrayArray } from '../../../../../../common/util/types.js';
+import { anyOf } from '../../../../../util/compare.js';
+import { toVector } from '../../../../../util/conversion.js';
+import { FP, FPKind, FPVector } from '../../../../../util/floating_point.js';
+import { cartesianProduct } from '../../../../../util/math.js';
+import { Case, selectNCases } from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { IntervalFilter } from '../../interval_filter.js';
+
+// Using a bespoke implementation of make*Case and generate*Cases here
+// since faceForwardIntervals is the only builtin with the API signature
+// (vec, vec, vec) -> vec
+//
+// Additionally faceForward has significant complexities around it due to the
+// fact that `dot` is calculated in its operation, but the result of dot isn't
+// used to calculate the builtin's result.
+
+/**
+ * @returns a Case for `faceForward`
+ * @param argumentKind what kind of floating point numbers being operated on
+ * @param parameterKind what kind of floating point operation should be performed,
+ * should be the same as argumentKind, except for abstract
+ * @param x the `x` param for the case
+ * @param y the `y` param for the case
+ * @param z the `z` param for the case
+ * @param check what interval checking to apply
+ * */
+function makeCase(
+ argumentKind: FPKind,
+ parameterKind: FPKind,
+ x: readonly number[],
+ y: readonly number[],
+ z: readonly number[],
+ check: IntervalFilter
+): Case | undefined {
+ const fp = FP[argumentKind];
+ x = x.map(fp.quantize);
+ y = y.map(fp.quantize);
+ z = z.map(fp.quantize);
+
+ const results = FP[parameterKind].faceForwardIntervals(x, y, z);
+ if (check === 'finite' && results.some(r => r === undefined)) {
+ return undefined;
+ }
+
+ // Stripping the undefined results, since undefined is used to signal that an OOB
+ // could occur within the calculation that isn't reflected in the result
+ // intervals.
+ const define_results = results.filter((r): r is FPVector => r !== undefined);
+
+ return {
+ input: [
+ toVector(x, fp.scalarBuilder),
+ toVector(y, fp.scalarBuilder),
+ toVector(z, fp.scalarBuilder),
+ ],
+ expected: anyOf(...define_results),
+ };
+}
+
+/**
+ * @returns an array of Cases for `faceForward`
+ * @param argumentKind what kind of floating point numbers being operated on
+ * @param parameterKind what kind of floating point operation should be performed,
+ * should be the same as argumentKind, except for abstract
+ * @param xs array of inputs to try for the `x` param
+ * @param ys array of inputs to try for the `y` param
+ * @param zs array of inputs to try for the `z` param
+ * @param check what interval checking to apply
+ */
+function generateCases(
+ argumentKind: FPKind,
+ parameterKind: FPKind,
+ xs: ROArrayArray<number>,
+ ys: ROArrayArray<number>,
+ zs: ROArrayArray<number>,
+ check: IntervalFilter
+): Case[] {
+ // Cannot use `cartesianProduct` here due to heterogeneous param types
+ return cartesianProduct(xs, ys, zs)
+ .map(e => makeCase(argumentKind, parameterKind, e[0], e[1], e[2], check))
+ .filter((c): c is Case => c !== undefined);
+}
+
+// Cases: [f32|f16|abstract]_vecN_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(dim =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ if (trait !== 'abstract') {
+ return generateCases(
+ trait,
+ trait,
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseVectorRange(dim),
+ nonConst ? 'unfiltered' : 'finite'
+ );
+ } else {
+ // Restricting the number of cases, because a vector of abstract floats needs to be returned, which is costly.
+ return selectNCases(
+ 'faceForward',
+ 20,
+ generateCases(
+ trait,
+ // faceForward has an inherited accuracy, so is only expected to be as accurate as f32
+ 'f32',
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseVectorRange(dim),
+ nonConst ? 'unfiltered' : 'finite'
+ )
+ );
+ }
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('faceForward', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts
index 6b6794fb9f..096abe034f 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts
@@ -1,142 +1,68 @@
export const description = `
Execution tests for the 'faceForward' builtin function
-T is vecN<AbstractFloat>, vecN<f32>, or vecN<f16>
+T is vecN<Type.abstractFloat>, vecN<f32>, or vecN<f16>
@const fn faceForward(e1: T ,e2: T ,e3: T ) -> T
Returns e1 if dot(e2,e3) is negative, and -e1 otherwise.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
-import { ROArrayArray } from '../../../../../../common/util/types.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { anyOf } from '../../../../../util/compare.js';
-import { toVector, TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js';
-import { FP, FPKind, FPVector } from '../../../../../util/floating_point.js';
-import {
- cartesianProduct,
- sparseVectorF32Range,
- sparseVectorF16Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, Case, IntervalFilter, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './faceForward.cache.js';
export const g = makeTestGroup(GPUTest);
-// Using a bespoke implementation of make*Case and generate*Cases here
-// since faceForwardIntervals is the only builtin with the API signature
-// (vec, vec, vec) -> vec
-//
-// Additionally faceForward has significant complexities around it due to the
-// fact that `dot` is calculated in it s operation, but the result of dot isn't
-// used to calculate the builtin's result.
-
-/**
- * @returns a Case for `faceForward`
- * @param kind what kind of floating point numbers being operated on
- * @param x the `x` param for the case
- * @param y the `y` param for the case
- * @param z the `z` param for the case
- * @param check what interval checking to apply
- * */
-function makeCase(
- kind: FPKind,
- x: readonly number[],
- y: readonly number[],
- z: readonly number[],
- check: IntervalFilter
-): Case | undefined {
- const fp = FP[kind];
- x = x.map(fp.quantize);
- y = y.map(fp.quantize);
- z = z.map(fp.quantize);
-
- const results = FP[kind].faceForwardIntervals(x, y, z);
- if (check === 'finite' && results.some(r => r === undefined)) {
- return undefined;
- }
-
- // Stripping the undefined results, since undefined is used to signal that an OOB
- // could occur within the calculation that isn't reflected in the result
- // intervals.
- const define_results = results.filter((r): r is FPVector => r !== undefined);
-
- return {
- input: [
- toVector(x, fp.scalarBuilder),
- toVector(y, fp.scalarBuilder),
- toVector(z, fp.scalarBuilder),
- ],
- expected: anyOf(...define_results),
- };
-}
-
-/**
- * @returns an array of Cases for `faceForward`
- * @param kind what kind of floating point numbers being operated on
- * @param xs array of inputs to try for the `x` param
- * @param ys array of inputs to try for the `y` param
- * @param zs array of inputs to try for the `z` param
- * @param check what interval checking to apply
- */
-function generateCases(
- kind: FPKind,
- xs: ROArrayArray<number>,
- ys: ROArrayArray<number>,
- zs: ROArrayArray<number>,
- check: IntervalFilter
-): Case[] {
- // Cannot use `cartesianProduct` here due to heterogeneous param types
- return cartesianProduct(xs, ys, zs)
- .map(e => makeCase(kind, e[0], e[1], e[2], check))
- .filter((c): c is Case => c !== undefined);
-}
-
-// Cases: f32_vecN_[non_]const
-const f32_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f32_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return generateCases(
- 'f32',
- sparseVectorF32Range(n),
- sparseVectorF32Range(n),
- sparseVectorF32Range(n),
- nonConst ? 'unfiltered' : 'finite'
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: f16_vecN_[non_]const
-const f16_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f16_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return generateCases(
- 'f16',
- sparseVectorF16Range(n),
- sparseVectorF16Range(n),
- sparseVectorF16Range(n),
- nonConst ? 'unfiltered' : 'finite'
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
+g.test('abstract_float_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec2s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec2_const');
+ await run(
+ t,
+ abstractFloatBuiltin('faceForward'),
+ [Type.vec2af, Type.vec2af, Type.vec2af],
+ Type.vec2af,
+ t.params,
+ cases
+ );
+ });
-export const d = makeCaseCache('faceForward', {
- ...f32_vec_cases,
- ...f16_vec_cases,
-});
+g.test('abstract_float_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec3s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec3_const');
+ await run(
+ t,
+ abstractFloatBuiltin('faceForward'),
+ [Type.vec3af, Type.vec3af, Type.vec3af],
+ Type.vec3af,
+ t.params,
+ cases
+ );
+ });
-g.test('abstract_float')
- .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
- .desc(`abstract float tests`)
- .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
- .unimplemented();
+g.test('abstract_float_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec4s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec4_const');
+ await run(
+ t,
+ abstractFloatBuiltin('faceForward'),
+ [Type.vec4af, Type.vec4af, Type.vec4af],
+ Type.vec4af,
+ t.params,
+ cases
+ );
+ });
g.test('f32_vec2')
.specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
@@ -149,8 +75,8 @@ g.test('f32_vec2')
await run(
t,
builtin('faceForward'),
- [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeVec(2, TypeF32)],
- TypeVec(2, TypeF32),
+ [Type.vec2f, Type.vec2f, Type.vec2f],
+ Type.vec2f,
t.params,
cases
);
@@ -167,8 +93,8 @@ g.test('f32_vec3')
await run(
t,
builtin('faceForward'),
- [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
- TypeVec(3, TypeF32),
+ [Type.vec3f, Type.vec3f, Type.vec3f],
+ Type.vec3f,
t.params,
cases
);
@@ -185,8 +111,8 @@ g.test('f32_vec4')
await run(
t,
builtin('faceForward'),
- [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeVec(4, TypeF32)],
- TypeVec(4, TypeF32),
+ [Type.vec4f, Type.vec4f, Type.vec4f],
+ Type.vec4f,
t.params,
cases
);
@@ -206,8 +132,8 @@ g.test('f16_vec2')
await run(
t,
builtin('faceForward'),
- [TypeVec(2, TypeF16), TypeVec(2, TypeF16), TypeVec(2, TypeF16)],
- TypeVec(2, TypeF16),
+ [Type.vec2h, Type.vec2h, Type.vec2h],
+ Type.vec2h,
t.params,
cases
);
@@ -227,8 +153,8 @@ g.test('f16_vec3')
await run(
t,
builtin('faceForward'),
- [TypeVec(3, TypeF16), TypeVec(3, TypeF16), TypeVec(3, TypeF16)],
- TypeVec(3, TypeF16),
+ [Type.vec3h, Type.vec3h, Type.vec3h],
+ Type.vec3h,
t.params,
cases
);
@@ -248,8 +174,8 @@ g.test('f16_vec4')
await run(
t,
builtin('faceForward'),
- [TypeVec(4, TypeF16), TypeVec(4, TypeF16), TypeVec(4, TypeF16)],
- TypeVec(4, TypeF16),
+ [Type.vec4h, Type.vec4h, Type.vec4h],
+ Type.vec4h,
t.params,
cases
);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts
index 26216563cd..9248b1e2bf 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts
@@ -16,7 +16,7 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js';
+import { i32, i32Bits, Type, u32, u32Bits } from '../../../../../util/conversion.js';
import { allInputSources, Config, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -31,7 +31,7 @@ g.test('u32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('firstLeadingBit'), [TypeU32], TypeU32, cfg, [
+ await run(t, builtin('firstLeadingBit'), [Type.u32], Type.u32, cfg, [
// Zero
{ input: u32Bits(0b00000000000000000000000000000000), expected: u32(-1) },
@@ -146,7 +146,7 @@ g.test('i32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('firstLeadingBit'), [TypeI32], TypeI32, cfg, [
+ await run(t, builtin('firstLeadingBit'), [Type.i32], Type.i32, cfg, [
// Zero
{ input: i32Bits(0b00000000000000000000000000000000), expected: i32(-1) },
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts
index 5c65f59d28..a8dd27ee87 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts
@@ -12,7 +12,7 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js';
+import { i32, i32Bits, Type, u32, u32Bits } from '../../../../../util/conversion.js';
import { allInputSources, Config, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -27,7 +27,7 @@ g.test('u32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('firstTrailingBit'), [TypeU32], TypeU32, cfg, [
+ await run(t, builtin('firstTrailingBit'), [Type.u32], Type.u32, cfg, [
// Zero
{ input: u32Bits(0b00000000000000000000000000000000), expected: u32(-1) },
@@ -142,7 +142,7 @@ g.test('i32')
)
.fn(async t => {
const cfg: Config = t.params;
- await run(t, builtin('firstTrailingBit'), [TypeI32], TypeI32, cfg, [
+ await run(t, builtin('firstTrailingBit'), [Type.i32], Type.i32, cfg, [
// Zero
{ input: i32Bits(0b00000000000000000000000000000000), expected: i32(-1) },
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.cache.ts
new file mode 100644
index 0000000000..0af966cfd2
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.cache.ts
@@ -0,0 +1,26 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+const kSmallMagnitudeTestValues = [0.1, 0.9, 1.0, 1.1, 1.9, -0.1, -0.9, -1.0, -1.1, -1.9];
+
+// See https://github.com/gpuweb/cts/issues/2766 for details
+const kIssue2766Value = {
+ abstract: 0x8000_0000_0000_0000,
+ f32: 0x8000_0000,
+ f16: 0x8000,
+};
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ [...kSmallMagnitudeTestValues, kIssue2766Value[trait], ...FP[trait].scalarRange()],
+ 'unfiltered',
+ FP[trait].floorInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('floor', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts
index 873a6772c3..26cffe5d10 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'floor' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn floor(e: T ) -> T
Returns the floor of e. Component-wise when T is a vector.
@@ -9,54 +9,14 @@ Returns the floor of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16, TypeAbstractFloat } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range, fullF64Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './floor.cache.js';
export const g = makeTestGroup(GPUTest);
-const kSmallMagnitudeTestValues = [0.1, 0.9, 1.0, 1.1, 1.9, -0.1, -0.9, -1.0, -1.1, -1.9];
-
-export const d = makeCaseCache('floor', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- ...kSmallMagnitudeTestValues,
- ...fullF32Range(),
- 0x8000_0000, // https://github.com/gpuweb/cts/issues/2766
- ],
- 'unfiltered',
- FP.f32.floorInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- [
- ...kSmallMagnitudeTestValues,
- ...fullF16Range(),
- 0x8000, // https://github.com/gpuweb/cts/issues/2766
- ],
- 'unfiltered',
- FP.f16.floorInterval
- );
- },
- abstract: () => {
- return FP.abstract.generateScalarToIntervalCases(
- [
- ...kSmallMagnitudeTestValues,
- ...fullF64Range(),
- 0x8000_0000_0000_0000, // https://github.com/gpuweb/cts/issues/2766
- ],
- 'unfiltered',
- FP.abstract.floorInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
@@ -67,7 +27,14 @@ g.test('abstract_float')
)
.fn(async t => {
const cases = await d.get('abstract');
- await run(t, abstractBuiltin('floor'), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases);
+ await run(
+ t,
+ abstractFloatBuiltin('floor'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
});
g.test('f32')
@@ -78,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('floor'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('floor'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -92,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('floor'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('floor'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.cache.ts
new file mode 100644
index 0000000000..20a61ce837
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.cache.ts
@@ -0,0 +1,26 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+// abstract_non_const is empty and not used
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarTripleToIntervalCases(
+ FP[trait].sparseScalarRange(),
+ FP[trait].sparseScalarRange(),
+ FP[trait].sparseScalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // fma has an inherited accuracy, so abstract is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].fmaInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('fma', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts
index 701f9d7ca9..620792d420 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'fma' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn fma(e1: T ,e2: T ,e3: T ) -> T
Returns e1 * e2 + e3. Component-wise when T is a vector.
@@ -9,64 +9,14 @@ Returns e1 * e2 + e3. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16, TypeAbstractFloat } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { sparseF32Range, sparseF16Range, sparseF64Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './fma.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('fma', {
- f32_const: () => {
- return FP.f32.generateScalarTripleToIntervalCases(
- sparseF32Range(),
- sparseF32Range(),
- sparseF32Range(),
- 'finite',
- FP.f32.fmaInterval
- );
- },
- f32_non_const: () => {
- return FP.f32.generateScalarTripleToIntervalCases(
- sparseF32Range(),
- sparseF32Range(),
- sparseF32Range(),
- 'unfiltered',
- FP.f32.fmaInterval
- );
- },
- f16_const: () => {
- return FP.f16.generateScalarTripleToIntervalCases(
- sparseF16Range(),
- sparseF16Range(),
- sparseF16Range(),
- 'finite',
- FP.f16.fmaInterval
- );
- },
- f16_non_const: () => {
- return FP.f16.generateScalarTripleToIntervalCases(
- sparseF16Range(),
- sparseF16Range(),
- sparseF16Range(),
- 'unfiltered',
- FP.f16.fmaInterval
- );
- },
- abstract: () => {
- return FP.abstract.generateScalarTripleToIntervalCases(
- sparseF64Range(),
- sparseF64Range(),
- sparseF64Range(),
- 'finite',
- FP.abstract.fmaInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
@@ -76,12 +26,12 @@ g.test('abstract_float')
.combine('vectorize', [undefined, 2, 3, 4] as const)
)
.fn(async t => {
- const cases = await d.get('abstract');
+ const cases = await d.get('abstract_const');
await run(
t,
- abstractBuiltin('fma'),
- [TypeAbstractFloat, TypeAbstractFloat, TypeAbstractFloat],
- TypeAbstractFloat,
+ abstractFloatBuiltin('fma'),
+ [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
t.params,
cases
);
@@ -95,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('fma'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('fma'), [Type.f32, Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -109,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('fma'), [TypeF16, TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('fma'), [Type.f16, Type.f16, Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.cache.ts
new file mode 100644
index 0000000000..5d0933554b
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.cache.ts
@@ -0,0 +1,50 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+const kCommonValues = [
+ 0.5, // 0.5 -> 0.5
+ 0.9, // ~0.9 -> ~0.9
+ 1, // 1 -> 0
+ 2, // 2 -> 0
+ 1.11, // ~1.11 -> ~0.11
+ -0.1, // ~-0.1 -> ~0.9
+ -0.5, // -0.5 -> 0.5
+ -0.9, // ~-0.9 -> ~0.1
+ -1, // -1 -> 0
+ -2, // -2 -> 0
+ -1.11, // ~-1.11 -> ~0.89
+];
+
+const kTraitSpecificValues = {
+ f32: [
+ 10.0001, // ~10.0001 -> ~0.0001
+ -10.0001, // -10.0001 -> ~0.9999
+ 0x8000_0000, // https://github.com/gpuweb/cts/issues/2766
+ ],
+ f16: [
+ 10.0078125, // 10.0078125 -> 0.0078125
+ -10.0078125, // -10.0078125 -> 0.9921875
+ 658.5, // 658.5 -> 0.5
+ 0x8000, // https://github.com/gpuweb/cts/issues/2766
+ ],
+ abstract: [
+ 10.0001, // ~10.0001 -> ~0.0001
+ -10.0001, // -10.0001 -> ~0.9999
+ 0x8000_0000, // https://github.com/gpuweb/cts/issues/2766
+ ],
+};
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ [...kCommonValues, ...kTraitSpecificValues[trait], ...FP[trait].scalarRange()],
+ trait === 'abstract' ? 'finite' : 'unfiltered',
+ FP[trait].fractInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('fract', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts
index 44ea31fde2..f1f7279c0b 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'fract' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn fract(e: T ) -> T
Returns the fractional part of e, computed as e - floor(e).
@@ -10,72 +10,33 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './fract.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('fract', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- 0.5, // 0.5 -> 0.5
- 0.9, // ~0.9 -> ~0.9
- 1, // 1 -> 0
- 2, // 2 -> 0
- 1.11, // ~1.11 -> ~0.11
- 10.0001, // ~10.0001 -> ~0.0001
- -0.1, // ~-0.1 -> ~0.9
- -0.5, // -0.5 -> 0.5
- -0.9, // ~-0.9 -> ~0.1
- -1, // -1 -> 0
- -2, // -2 -> 0
- -1.11, // ~-1.11 -> ~0.89
- -10.0001, // -10.0001 -> ~0.9999
- 0x80000000, // https://github.com/gpuweb/cts/issues/2766
- ...fullF32Range(),
- ],
- 'unfiltered',
- FP.f32.fractInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- [
- 0.5, // 0.5 -> 0.5
- 0.9, // ~0.9 -> ~0.9
- 1, // 1 -> 0
- 2, // 2 -> 0
- 1.11, // ~1.11 -> ~0.11
- 10.0078125, // 10.0078125 -> 0.0078125
- -0.1, // ~-0.1 -> ~0.9
- -0.5, // -0.5 -> 0.5
- -0.9, // ~-0.9 -> ~0.1
- -1, // -1 -> 0
- -2, // -2 -> 0
- -1.11, // ~-1.11 -> ~0.89
- -10.0078125, // -10.0078125 -> 0.9921875
- 658.5, // 658.5 -> 0.5
- ...fullF16Range(),
- ],
- 'unfiltered',
- FP.f16.fractInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('fract'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -85,7 +46,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('fract'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('fract'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -99,5 +60,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('fract'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('fract'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts
new file mode 100644
index 0000000000..2211e2adc9
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts
@@ -0,0 +1,103 @@
+import { skipUndefined } from '../../../../../util/compare.js';
+import {
+ ScalarValue,
+ VectorValue,
+ i32,
+ toVector,
+ abstractInt,
+} from '../../../../../util/conversion.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { frexp } from '../../../../../util/math.js';
+import { Case } from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+/* @returns a fract Case for a given scalar or vector input */
+function makeCaseFract(v: number | readonly number[], trait: 'f32' | 'f16' | 'abstract'): Case {
+ const fp = FP[trait];
+ let toInput: (n: readonly number[]) => ScalarValue | VectorValue;
+ let toOutput: (n: readonly number[]) => ScalarValue | VectorValue;
+ if (v instanceof Array) {
+ // Input is vector
+ toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder);
+ toOutput = (n: readonly number[]) => toVector(n, fp.scalarBuilder);
+ } else {
+ // Input is scalar, also wrap it in an array.
+ v = [v];
+ toInput = (n: readonly number[]) => fp.scalarBuilder(n[0]);
+ toOutput = (n: readonly number[]) => fp.scalarBuilder(n[0]);
+ }
+
+ v = v.map(fp.quantize);
+ if (v.some(e => e !== 0 && fp.isSubnormal(e))) {
+ return { input: toInput(v), expected: skipUndefined(undefined) };
+ }
+
+ const fs = v.map(e => {
+ return frexp(e, trait !== 'abstract' ? trait : 'f64').fract;
+ });
+
+ return { input: toInput(v), expected: toOutput(fs) };
+}
+
+/* @returns an exp Case for a given scalar or vector input */
+function makeCaseExp(v: number | readonly number[], trait: 'f32' | 'f16' | 'abstract'): Case {
+ const fp = FP[trait];
+ let toInput: (n: readonly number[]) => ScalarValue | VectorValue;
+ let toOutput: (n: readonly number[]) => ScalarValue | VectorValue;
+ if (v instanceof Array) {
+ // Input is vector
+ toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder);
+ toOutput = (n: readonly number[]) =>
+ toVector(n, trait !== 'abstract' ? i32 : (n: number) => abstractInt(BigInt(n)));
+ } else {
+ // Input is scalar, also wrap it in an array.
+ v = [v];
+ toInput = (n: readonly number[]) => fp.scalarBuilder(n[0]);
+ toOutput = (n: readonly number[]) =>
+ trait !== 'abstract' ? i32(n[0]) : abstractInt(BigInt(n[0]));
+ }
+
+ v = v.map(fp.quantize);
+ if (v.some(e => e !== 0 && fp.isSubnormal(e))) {
+ return { input: toInput(v), expected: skipUndefined(undefined) };
+ }
+
+ const fs = v.map(e => {
+ return frexp(e, trait !== 'abstract' ? trait : 'f64').exp;
+ });
+
+ return { input: toInput(v), expected: toOutput(fs) };
+}
+
+// Cases: [f32|f16]_vecN_[exp|whole]
+const vec_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(dim =>
+ (['exp', 'fract'] as const).map(portion => ({
+ [`${trait}_vec${dim}_${portion}`]: () => {
+ return FP[trait]
+ .vectorRange(dim)
+ .map(v => (portion === 'exp' ? makeCaseExp(v, trait) : makeCaseFract(v, trait)));
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+// Cases: [f32|f16]_[exp|whole]
+const scalar_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ (['exp', 'fract'] as const).map(portion => ({
+ [`${trait}_${portion}`]: () => {
+ return FP[trait]
+ .scalarRange()
+ .map(v => (portion === 'exp' ? makeCaseExp(v, trait) : makeCaseFract(v, trait)));
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('frexp', {
+ ...scalar_cases,
+ ...vec_cases,
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts
index ffe672b08c..8f07f3990d 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts
@@ -15,34 +15,19 @@ The magnitude of the significand is in the range of [0.5, 1.0) or 0.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { skipUndefined } from '../../../../../util/compare.js';
-import {
- i32,
- Scalar,
- toVector,
- TypeF32,
- TypeF16,
- TypeI32,
- TypeVec,
- Vector,
-} from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import {
- frexp,
- fullF16Range,
- fullF32Range,
- vectorF16Range,
- vectorF32Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import {
+ ShaderBuilder,
allInputSources,
basicExpressionBuilder,
- Case,
run,
- ShaderBuilder,
+ abstractFloatShaderBuilder,
+ abstractIntShaderBuilder,
+ onlyConstInputSource,
} from '../../expression.js';
+import { d } from './frexp.cache.js';
+
export const g = makeTestGroup(GPUTest);
/* @returns an ShaderBuilder that evaluates frexp and returns .fract from the result structure */
@@ -55,112 +40,159 @@ function expBuilder(): ShaderBuilder {
return basicExpressionBuilder(value => `frexp(${value}).exp`);
}
-/* @returns a fract Case for a given scalar or vector input */
-function makeVectorCaseFract(v: number | readonly number[], trait: 'f32' | 'f16'): Case {
- const fp = FP[trait];
- let toInput: (n: readonly number[]) => Scalar | Vector;
- let toOutput: (n: readonly number[]) => Scalar | Vector;
- if (v instanceof Array) {
- // Input is vector
- toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder);
- toOutput = (n: readonly number[]) => toVector(n, fp.scalarBuilder);
- } else {
- // Input is scalar, also wrap it in an array.
- v = [v];
- toInput = (n: readonly number[]) => fp.scalarBuilder(n[0]);
- toOutput = (n: readonly number[]) => fp.scalarBuilder(n[0]);
- }
-
- v = v.map(fp.quantize);
- if (v.some(e => e !== 0 && fp.isSubnormal(e))) {
- return { input: toInput(v), expected: skipUndefined(undefined) };
- }
-
- const fs = v.map(e => {
- return frexp(e, trait).fract;
+/* @returns an ShaderBuilder that evaluates frexp and returns .fract from the result structure, for abstract inputs */
+function abstractFractBuilder(): ShaderBuilder {
+ return abstractFloatShaderBuilder(value => `frexp(${value}).fract`);
+}
+
+/* @returns an ShaderBuilder that evaluates frexp and returns .exp from the result structure, for abstract inputs */
+function abstractExpBuilder(): ShaderBuilder {
+ return abstractIntShaderBuilder(value => `frexp(${value}).exp`);
+}
+
+g.test('abstract_float_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is AbstractFloat
+
+struct __frexp_result_abstract {
+ fract : AbstractFloat, // fract part
+ exp : AbstractInt // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_fract');
+ await run(t, abstractFractBuilder(), [Type.abstractFloat], Type.abstractFloat, t.params, cases);
+ });
+
+g.test('abstract_float_exp')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is AbstractFloat
+
+struct __frexp_result_abstract {
+ fract : AbstractFloat, // fract part
+ exp : AbstractInt // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_exp');
+ await run(t, abstractExpBuilder(), [Type.abstractFloat], Type.abstractInt, t.params, cases);
+ });
+
+g.test('abstract_float_vec2_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec2<AbstractFloat>
+
+struct __frexp_result_vec2_abstract {
+ fract : vec2<AbstractFloat>, // fract part
+ exp : vec2<AbstractInt> // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec2_fract');
+ await run(t, abstractFractBuilder(), [Type.vec2af], Type.vec2af, t.params, cases);
+ });
+
+g.test('abstract_float_vec2_exp')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec2<AbstractFloat>
+
+struct __frexp_result_vec2_abstract {
+ fract : vec2<AbstractFloat>, // fractional part
+ exp : vec2<AbstractInt> // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec2_exp');
+ await run(t, abstractExpBuilder(), [Type.vec2af], Type.vec2ai, t.params, cases);
});
- return { input: toInput(v), expected: toOutput(fs) };
+g.test('abstract_float_vec3_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec3<AbstractFloat>
+
+struct __frexp_result_vec3_abstract {
+ fract : vec3<AbstractFloat>, // fractional part
+ exp : vec3<AbstractInt> // exponent part
}
+`
+ )
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec3_fract');
+ await run(t, abstractFractBuilder(), [Type.vec3af], Type.vec3af, t.params, cases);
+ });
-/* @returns an exp Case for a given scalar or vector input */
-function makeVectorCaseExp(v: number | readonly number[], trait: 'f32' | 'f16'): Case {
- const fp = FP[trait];
- let toInput: (n: readonly number[]) => Scalar | Vector;
- let toOutput: (n: readonly number[]) => Scalar | Vector;
- if (v instanceof Array) {
- // Input is vector
- toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder);
- toOutput = (n: readonly number[]) => toVector(n, i32);
- } else {
- // Input is scalar, also wrap it in an array.
- v = [v];
- toInput = (n: readonly number[]) => fp.scalarBuilder(n[0]);
- toOutput = (n: readonly number[]) => i32(n[0]);
- }
-
- v = v.map(fp.quantize);
- if (v.some(e => e !== 0 && fp.isSubnormal(e))) {
- return { input: toInput(v), expected: skipUndefined(undefined) };
- }
-
- const fs = v.map(e => {
- return frexp(e, trait).exp;
+g.test('abstract_float_vec3_exp')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec3<AbstractFloat>
+
+struct __frexp_result_vec3_abstract {
+ fract : vec3<AbstractFloat>, // fractional part
+ exp : vec3<AbstractInt> // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec3_exp');
+ await run(t, abstractExpBuilder(), [Type.vec3af], Type.vec3ai, t.params, cases);
});
- return { input: toInput(v), expected: toOutput(fs) };
+g.test('abstract_float_vec4_fract')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec4<AbstractFloat>
+
+struct __frexp_result_vec4_abstract {
+ fract : vec4<AbstractFloat>, // fractional part
+ exp : vec4<AbstractInt> // exponent part
}
+`
+ )
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec4_fract');
+ await run(t, abstractFractBuilder(), [Type.vec4af], Type.vec4af, t.params, cases);
+ });
+
+g.test('abstract_float_vec4_exp')
+ .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
+ .desc(
+ `
+T is vec4<AbstractFloat>
-export const d = makeCaseCache('frexp', {
- f32_fract: () => {
- return fullF32Range().map(v => makeVectorCaseFract(v, 'f32'));
- },
- f32_exp: () => {
- return fullF32Range().map(v => makeVectorCaseExp(v, 'f32'));
- },
- f32_vec2_fract: () => {
- return vectorF32Range(2).map(v => makeVectorCaseFract(v, 'f32'));
- },
- f32_vec2_exp: () => {
- return vectorF32Range(2).map(v => makeVectorCaseExp(v, 'f32'));
- },
- f32_vec3_fract: () => {
- return vectorF32Range(3).map(v => makeVectorCaseFract(v, 'f32'));
- },
- f32_vec3_exp: () => {
- return vectorF32Range(3).map(v => makeVectorCaseExp(v, 'f32'));
- },
- f32_vec4_fract: () => {
- return vectorF32Range(4).map(v => makeVectorCaseFract(v, 'f32'));
- },
- f32_vec4_exp: () => {
- return vectorF32Range(4).map(v => makeVectorCaseExp(v, 'f32'));
- },
- f16_fract: () => {
- return fullF16Range().map(v => makeVectorCaseFract(v, 'f16'));
- },
- f16_exp: () => {
- return fullF16Range().map(v => makeVectorCaseExp(v, 'f16'));
- },
- f16_vec2_fract: () => {
- return vectorF16Range(2).map(v => makeVectorCaseFract(v, 'f16'));
- },
- f16_vec2_exp: () => {
- return vectorF16Range(2).map(v => makeVectorCaseExp(v, 'f16'));
- },
- f16_vec3_fract: () => {
- return vectorF16Range(3).map(v => makeVectorCaseFract(v, 'f16'));
- },
- f16_vec3_exp: () => {
- return vectorF16Range(3).map(v => makeVectorCaseExp(v, 'f16'));
- },
- f16_vec4_fract: () => {
- return vectorF16Range(4).map(v => makeVectorCaseFract(v, 'f16'));
- },
- f16_vec4_exp: () => {
- return vectorF16Range(4).map(v => makeVectorCaseExp(v, 'f16'));
- },
-});
+struct __frexp_result_vec4_abstract {
+ fract : vec4<AbstractFloat>, // fractional part
+ exp : vec4<AbstractInt> // exponent part
+}
+`
+ )
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec4_exp');
+ await run(t, abstractExpBuilder(), [Type.vec4af], Type.vec4ai, t.params, cases);
+ });
g.test('f32_fract')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -177,7 +209,7 @@ struct __frexp_result_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_fract');
- await run(t, fractBuilder(), [TypeF32], TypeF32, t.params, cases);
+ await run(t, fractBuilder(), [Type.f32], Type.f32, t.params, cases);
});
g.test('f32_exp')
@@ -195,7 +227,7 @@ struct __frexp_result_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_exp');
- await run(t, expBuilder(), [TypeF32], TypeI32, t.params, cases);
+ await run(t, expBuilder(), [Type.f32], Type.i32, t.params, cases);
});
g.test('f32_vec2_fract')
@@ -213,7 +245,7 @@ struct __frexp_result_vec2_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec2_fract');
- await run(t, fractBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec2f], Type.vec2f, t.params, cases);
});
g.test('f32_vec2_exp')
@@ -231,7 +263,7 @@ struct __frexp_result_vec2_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec2_exp');
- await run(t, expBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeI32), t.params, cases);
+ await run(t, expBuilder(), [Type.vec2f], Type.vec2i, t.params, cases);
});
g.test('f32_vec3_fract')
@@ -249,7 +281,7 @@ struct __frexp_result_vec3_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec3_fract');
- await run(t, fractBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec3f], Type.vec3f, t.params, cases);
});
g.test('f32_vec3_exp')
@@ -267,7 +299,7 @@ struct __frexp_result_vec3_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec3_exp');
- await run(t, expBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeI32), t.params, cases);
+ await run(t, expBuilder(), [Type.vec3f], Type.vec3i, t.params, cases);
});
g.test('f32_vec4_fract')
@@ -285,7 +317,7 @@ struct __frexp_result_vec4_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec4_fract');
- await run(t, fractBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec4f], Type.vec4f, t.params, cases);
});
g.test('f32_vec4_exp')
@@ -303,7 +335,7 @@ struct __frexp_result_vec4_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec4_exp');
- await run(t, expBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeI32), t.params, cases);
+ await run(t, expBuilder(), [Type.vec4f], Type.vec4i, t.params, cases);
});
g.test('f16_fract')
@@ -324,7 +356,7 @@ struct __frexp_result_f16 {
})
.fn(async t => {
const cases = await d.get('f16_fract');
- await run(t, fractBuilder(), [TypeF16], TypeF16, t.params, cases);
+ await run(t, fractBuilder(), [Type.f16], Type.f16, t.params, cases);
});
g.test('f16_exp')
@@ -345,7 +377,7 @@ struct __frexp_result_f16 {
})
.fn(async t => {
const cases = await d.get('f16_exp');
- await run(t, expBuilder(), [TypeF16], TypeI32, t.params, cases);
+ await run(t, expBuilder(), [Type.f16], Type.i32, t.params, cases);
});
g.test('f16_vec2_fract')
@@ -366,7 +398,7 @@ struct __frexp_result_vec2_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec2_fract');
- await run(t, fractBuilder(), [TypeVec(2, TypeF16)], TypeVec(2, TypeF16), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec2h], Type.vec2h, t.params, cases);
});
g.test('f16_vec2_exp')
@@ -387,7 +419,7 @@ struct __frexp_result_vec2_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec2_exp');
- await run(t, expBuilder(), [TypeVec(2, TypeF16)], TypeVec(2, TypeI32), t.params, cases);
+ await run(t, expBuilder(), [Type.vec2h], Type.vec2i, t.params, cases);
});
g.test('f16_vec3_fract')
@@ -408,7 +440,7 @@ struct __frexp_result_vec3_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec3_fract');
- await run(t, fractBuilder(), [TypeVec(3, TypeF16)], TypeVec(3, TypeF16), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec3h], Type.vec3h, t.params, cases);
});
g.test('f16_vec3_exp')
@@ -429,7 +461,7 @@ struct __frexp_result_vec3_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec3_exp');
- await run(t, expBuilder(), [TypeVec(3, TypeF16)], TypeVec(3, TypeI32), t.params, cases);
+ await run(t, expBuilder(), [Type.vec3h], Type.vec3i, t.params, cases);
});
g.test('f16_vec4_fract')
@@ -450,7 +482,7 @@ struct __frexp_result_vec4_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec4_fract');
- await run(t, fractBuilder(), [TypeVec(4, TypeF16)], TypeVec(4, TypeF16), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec4h], Type.vec4h, t.params, cases);
});
g.test('f16_vec4_exp')
@@ -471,5 +503,5 @@ struct __frexp_result_vec4_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec4_exp');
- await run(t, expBuilder(), [TypeVec(4, TypeF16)], TypeVec(4, TypeI32), t.params, cases);
+ await run(t, expBuilder(), [Type.vec4h], Type.vec4i, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts
index 1068e76252..b3eb65781d 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts
@@ -18,17 +18,7 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import {
- i32Bits,
- TypeI32,
- u32,
- TypeU32,
- u32Bits,
- vec2,
- vec3,
- vec4,
- TypeVec,
-} from '../../../../../util/conversion.js';
+import { i32Bits, Type, u32, u32Bits, vec2, vec3, vec4 } from '../../../../../util/conversion.js';
import { allInputSources, Config, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -46,8 +36,8 @@ g.test('integer')
)
.fn(async t => {
const cfg: Config = t.params;
- const scalarType = t.params.signed ? TypeI32 : TypeU32;
- const T = t.params.width === 1 ? scalarType : TypeVec(t.params.width, scalarType);
+ const scalarType = t.params.signed ? Type.i32 : Type.u32;
+ const T = t.params.width === 1 ? scalarType : Type.vec(t.params.width, scalarType);
const V = (x: number, y?: number, z?: number, w?: number) => {
y = y === undefined ? x : y;
@@ -382,5 +372,5 @@ g.test('integer')
);
}
- await run(t, builtin('insertBits'), [T, T, TypeU32, TypeU32], T, cfg, cases);
+ await run(t, builtin('insertBits'), [T, T, Type.u32, Type.u32], T, cfg, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.cache.ts
new file mode 100644
index 0000000000..e50d6d4866
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.cache.ts
@@ -0,0 +1,44 @@
+import { kValue } from '../../../../../util/constants.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { biasedRange, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+export const d = makeCaseCache('inverseSqrt', {
+ f32: () => {
+ return FP.f32.generateScalarToIntervalCases(
+ [
+ // 0 < x <= 1 linearly spread
+ ...linearRange(kValue.f32.positive.min, 1, 100),
+ // 1 <= x < 2^32, biased towards 1
+ ...biasedRange(1, 2 ** 32, 1000),
+ ],
+ 'unfiltered',
+ FP.f32.inverseSqrtInterval
+ );
+ },
+ f16: () => {
+ return FP.f16.generateScalarToIntervalCases(
+ [
+ // 0 < x <= 1 linearly spread
+ ...linearRange(kValue.f16.positive.min, 1, 100),
+ // 1 <= x < 2^15, biased towards 1
+ ...biasedRange(1, 2 ** 15, 1000),
+ ],
+ 'unfiltered',
+ FP.f16.inverseSqrtInterval
+ );
+ },
+ abstract: () => {
+ return FP.abstract.generateScalarToIntervalCases(
+ [
+ // 0 < x <= 1 linearly spread
+ ...linearRange(kValue.f64.positive.min, 1, 100),
+ // 1 <= x < 2^64, biased towards 1, only using 100 steps, because af tests are more expensive per case
+ ...biasedRange(1, 2 ** 64, 100),
+ ],
+ 'finite',
+ // inverseSqrt has an ulp accuracy, so is only expected to be as accurate as f32
+ FP.f32.inverseSqrtInterval
+ );
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts
index 3e83816387..954718de6c 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'inverseSqrt' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn inverseSqrt(e: T ) -> T
Returns the reciprocal of sqrt(e). Component-wise when T is a vector.
@@ -9,51 +9,33 @@ Returns the reciprocal of sqrt(e). Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { kValue } from '../../../../../util/constants.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { biasedRange, linearRange } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './inversesqrt.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('inverseSqrt', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- // 0 < x <= 1 linearly spread
- ...linearRange(kValue.f32.positive.min, 1, 100),
- // 1 <= x < 2^32, biased towards 1
- ...biasedRange(1, 2 ** 32, 1000),
- ],
- 'unfiltered',
- FP.f32.inverseSqrtInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- [
- // 0 < x <= 1 linearly spread
- ...linearRange(kValue.f16.positive.min, 1, 100),
- // 1 <= x < 2^15, biased towards 1
- ...biasedRange(1, 2 ** 15, 1000),
- ],
- 'unfiltered',
- FP.f16.inverseSqrtInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('inverseSqrt'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -63,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('inverseSqrt'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('inverseSqrt'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -77,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('inverseSqrt'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('inverseSqrt'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.cache.ts
new file mode 100644
index 0000000000..cf9194319b
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.cache.ts
@@ -0,0 +1,61 @@
+import { assert } from '../../../../../../common/util/util.js';
+import { anyOf } from '../../../../../util/compare.js';
+import { abstractInt, i32 } from '../../../../../util/conversion.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { biasedRange, quantizeToI32, sparseI32Range } from '../../../../../util/math.js';
+import { Case } from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// ldexpInterval's return interval doesn't cover the flush-to-zero cases when e2 + bias <= 0, thus
+// special examination is required.
+// See the comment block on ldexpInterval for more details
+// e2 is an integer (i32) while e1 is float.
+const makeCase = (trait: 'f32' | 'f16' | 'abstract', e1: number, e2: number): Case => {
+ const FPTrait = FP[trait];
+ e1 = FPTrait.quantize(e1);
+ // e2 should be in i32 range for the convenience.
+ assert(-2147483648 <= e2 && e2 <= 2147483647, 'e2 should be in i32 range');
+ e2 = quantizeToI32(e2);
+
+ const expected = FPTrait.ldexpInterval(e1, e2);
+
+ const e2_scalar = trait === 'abstract' ? abstractInt(BigInt(e2)) : i32(e2);
+ // Result may be zero if e2 + bias <= 0
+ if (e2 + FPTrait.constants().bias <= 0) {
+ return {
+ input: [FPTrait.scalarBuilder(e1), e2_scalar],
+ expected: anyOf(expected, FPTrait.constants().zeroInterval),
+ };
+ }
+
+ return { input: [FPTrait.scalarBuilder(e1), e2_scalar], expected };
+};
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (nonConst) {
+ if (trait === 'abstract') {
+ return [];
+ }
+ return FP[trait]
+ .sparseScalarRange()
+ .flatMap(e1 => sparseI32Range().map(e2 => makeCase(trait, e1, e2)));
+ }
+ const bias = FP[trait].constants().bias;
+ // const
+ return FP[trait]
+ .sparseScalarRange()
+ .flatMap(e1 =>
+ biasedRange(-bias - 10, bias + 1, 10).flatMap(e2 =>
+ FP[trait].isFinite(e1 * 2 ** quantizeToI32(e2)) ? makeCase(trait, e1, e2) : []
+ )
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('ldexp', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts
index 3829867752..d6cebfef6d 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts
@@ -1,10 +1,10 @@
export const description = `
Execution tests for the 'ldexp' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
-K is AbstractInt, i32
+K is Type.abstractInt, i32
I is K or vecN<K>, where
I is a scalar if T is a scalar, or a vector when T is a vector
@@ -13,77 +13,15 @@ Returns e1 * 2^e2. Component-wise when T is a vector.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
-import { assert } from '../../../../../../common/util/util.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { anyOf } from '../../../../../util/compare.js';
-import { i32, TypeF32, TypeF16, TypeI32 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import {
- biasedRange,
- quantizeToI32,
- sparseF32Range,
- sparseI32Range,
- sparseF16Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, Case, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './ldexp.cache.js';
export const g = makeTestGroup(GPUTest);
-const bias = {
- f32: 127,
- f16: 15,
-} as const;
-
-// ldexpInterval's return interval doesn't cover the flush-to-zero cases when e2 + bias <= 0, thus
-// special examination is required.
-// See the comment block on ldexpInterval for more details
-// e2 is an integer (i32) while e1 is float.
-const makeCase = (trait: 'f32' | 'f16', e1: number, e2: number): Case => {
- const FPTrait = FP[trait];
- e1 = FPTrait.quantize(e1);
- // e2 should be in i32 range for the convinience.
- assert(-2147483648 <= e2 && e2 <= 2147483647, 'e2 should be in i32 range');
- e2 = quantizeToI32(e2);
-
- const expected = FPTrait.ldexpInterval(e1, e2);
-
- // Result may be zero if e2 + bias <= 0
- if (e2 + bias[trait] <= 0) {
- return {
- input: [FPTrait.scalarBuilder(e1), i32(e2)],
- expected: anyOf(expected, FPTrait.constants().zeroInterval),
- };
- }
-
- return { input: [FPTrait.scalarBuilder(e1), i32(e2)], expected };
-};
-
-export const d = makeCaseCache('ldexp', {
- f32_non_const: () => {
- return sparseF32Range().flatMap(e1 => sparseI32Range().map(e2 => makeCase('f32', e1, e2)));
- },
- f32_const: () => {
- return sparseF32Range().flatMap(e1 =>
- biasedRange(-bias.f32 - 10, bias.f32 + 1, 10).flatMap(e2 =>
- FP.f32.isFinite(e1 * 2 ** quantizeToI32(e2)) ? makeCase('f32', e1, e2) : []
- )
- );
- },
- f16_non_const: () => {
- return sparseF16Range().flatMap(e1 => sparseI32Range().map(e2 => makeCase('f16', e1, e2)));
- },
- f16_const: () => {
- return sparseF16Range().flatMap(e1 =>
- biasedRange(-bias.f16 - 10, bias.f16 + 1, 10).flatMap(e2 =>
- FP.f16.isFinite(e1 * 2 ** quantizeToI32(e2)) ? makeCase('f16', e1, e2) : []
- )
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(
@@ -91,9 +29,21 @@ g.test('abstract_float')
`
)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('ldexp'),
+ [Type.abstractFloat, Type.abstractInt],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -103,7 +53,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('ldexp'), [TypeF32, TypeI32], TypeF32, t.params, cases);
+ await run(t, builtin('ldexp'), [Type.f32, Type.i32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -117,5 +67,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('ldexp'), [TypeF16, TypeI32], TypeF16, t.params, cases);
+ await run(t, builtin('ldexp'), [Type.f16, Type.i32], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.cache.ts
new file mode 100644
index 0000000000..e7a2ee22ba
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.cache.ts
@@ -0,0 +1,42 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const scalar_cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ trait !== 'abstract' ? 'unfiltered' : 'finite',
+ // length has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].lengthInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+// Cases: [f32|f16|abstract]_vecN_[non_]const
+const vec_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(dim =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateVectorToIntervalCases(
+ FP[trait].vectorRange(dim),
+ nonConst ? 'unfiltered' : 'finite',
+ // length has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].lengthInterval
+ );
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('length', {
+ ...scalar_cases,
+ ...vec_cases,
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts
index 85c1f85169..735c8468b7 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'length' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn length(e: T ) -> f32
Returns the length of e (e.g. abs(e) if T is a scalar, or sqrt(e[0]^2 + e[1]^2 + ...) if T is a vector).
@@ -9,77 +9,77 @@ Returns the length of e (e.g. abs(e) if T is a scalar, or sqrt(e[0]^2 + e[1]^2 +
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import {
- fullF32Range,
- fullF16Range,
- vectorF32Range,
- vectorF16Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
-
-import { builtin } from './builtin.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
+
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './length.cache.js';
export const g = makeTestGroup(GPUTest);
-// Cases: f32_vecN_[non_]const
-const f32_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f32_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f32.generateVectorToIntervalCases(
- vectorF32Range(n),
- nonConst ? 'unfiltered' : 'finite',
- FP.f32.lengthInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: f16_vecN_[non_]const
-const f16_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f16_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f16.generateVectorToIntervalCases(
- vectorF16Range(n),
- nonConst ? 'unfiltered' : 'finite',
- FP.f16.lengthInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-export const d = makeCaseCache('length', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- fullF32Range(),
- 'unfiltered',
- FP.f32.lengthInterval
+g.test('abstract_float')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract_float tests`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('length'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
+
+g.test('abstract_float_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec2s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec2_const');
+ await run(
+ t,
+ abstractFloatBuiltin('length'),
+ [Type.vec2af],
+ Type.abstractFloat,
+ t.params,
+ cases
);
- },
- ...f32_vec_cases,
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- fullF16Range(),
- 'unfiltered',
- FP.f16.lengthInterval
+ });
+
+g.test('abstract_float_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract_float tests using vec3s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec3_const');
+ await run(
+ t,
+ abstractFloatBuiltin('length'),
+ [Type.vec3af],
+ Type.abstractFloat,
+ t.params,
+ cases
);
- },
- ...f16_vec_cases,
-});
+ });
-g.test('abstract_float')
+g.test('abstract_float_vec4')
.specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
- .desc(`abstract float tests`)
- .params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
- )
- .unimplemented();
+ .desc(`abstract_float tests using vec4s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec4_const');
+ await run(
+ t,
+ abstractFloatBuiltin('length'),
+ [Type.vec4af],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
@@ -87,7 +87,7 @@ g.test('f32')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('length'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('length'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f32_vec2')
@@ -98,7 +98,7 @@ g.test('f32_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
);
- await run(t, builtin('length'), [TypeVec(2, TypeF32)], TypeF32, t.params, cases);
+ await run(t, builtin('length'), [Type.vec2f], Type.f32, t.params, cases);
});
g.test('f32_vec3')
@@ -109,7 +109,7 @@ g.test('f32_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
);
- await run(t, builtin('length'), [TypeVec(3, TypeF32)], TypeF32, t.params, cases);
+ await run(t, builtin('length'), [Type.vec3f], Type.f32, t.params, cases);
});
g.test('f32_vec4')
@@ -120,7 +120,7 @@ g.test('f32_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
);
- await run(t, builtin('length'), [TypeVec(4, TypeF32)], TypeF32, t.params, cases);
+ await run(t, builtin('length'), [Type.vec4f], Type.f32, t.params, cases);
});
g.test('f16')
@@ -132,7 +132,7 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('length'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('length'), [Type.f16], Type.f16, t.params, cases);
});
g.test('f16_vec2')
@@ -146,7 +146,7 @@ g.test('f16_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const'
);
- await run(t, builtin('length'), [TypeVec(2, TypeF16)], TypeF16, t.params, cases);
+ await run(t, builtin('length'), [Type.vec2h], Type.f16, t.params, cases);
});
g.test('f16_vec3')
@@ -160,7 +160,7 @@ g.test('f16_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const'
);
- await run(t, builtin('length'), [TypeVec(3, TypeF16)], TypeF16, t.params, cases);
+ await run(t, builtin('length'), [Type.vec3h], Type.f16, t.params, cases);
});
g.test('f16_vec4')
@@ -174,5 +174,5 @@ g.test('f16_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const'
);
- await run(t, builtin('length'), [TypeVec(4, TypeF16)], TypeF16, t.params, cases);
+ await run(t, builtin('length'), [Type.vec4h], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.cache.ts
new file mode 100644
index 0000000000..76b8bfa1af
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.cache.ts
@@ -0,0 +1,30 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { biasedRange, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// log's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] }
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ [
+ ...linearRange(FP[trait].constants().positive.min, 0.5, 20),
+ ...linearRange(0.5, 2.0, 20),
+ ...biasedRange(2.0, 2 ** 32, 1000),
+ ...FP[trait].scalarRange(),
+ ],
+ nonConst ? 'unfiltered' : 'finite',
+ // log has an absolute or ulp accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].logInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('log', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts
index ac60e2b1bc..99b4a6983e 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'log' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn log(e: T ) -> T
Returns the natural logarithm of e. Component-wise when T is a vector.
@@ -9,53 +9,33 @@ Returns the natural logarithm of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { kValue } from '../../../../../util/constants.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { biasedRange, fullF32Range, fullF16Range, linearRange } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './log.cache.js';
export const g = makeTestGroup(GPUTest);
-// log's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] }
-const f32_inputs = [
- ...linearRange(kValue.f32.positive.min, 0.5, 20),
- ...linearRange(0.5, 2.0, 20),
- ...biasedRange(2.0, 2 ** 32, 1000),
- ...fullF32Range(),
-];
-const f16_inputs = [
- ...linearRange(kValue.f16.positive.min, 0.5, 20),
- ...linearRange(0.5, 2.0, 20),
- ...biasedRange(2.0, 2 ** 32, 1000),
- ...fullF16Range(),
-];
-
-export const d = makeCaseCache('log', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.logInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.logInterval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.logInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.logInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('log'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -71,7 +51,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1]
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('log'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('log'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -85,5 +65,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('log'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('log'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.cache.ts
new file mode 100644
index 0000000000..d7781f328d
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.cache.ts
@@ -0,0 +1,30 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { biasedRange, linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// log2's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] }
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ [
+ ...linearRange(FP[trait].constants().positive.min, 0.5, 20),
+ ...linearRange(0.5, 2.0, 20),
+ ...biasedRange(2.0, 2 ** 32, 1000),
+ ...FP[trait].scalarRange(),
+ ],
+ nonConst ? 'unfiltered' : 'finite',
+ // log2 has an absolute or ulp accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].log2Interval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('log2', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts
index 37931579b9..dc623a6784 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'log2' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn log2(e: T ) -> T
Returns the base-2 logarithm of e. Component-wise when T is a vector.
@@ -9,53 +9,33 @@ Returns the base-2 logarithm of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { kValue } from '../../../../../util/constants.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { biasedRange, fullF32Range, fullF16Range, linearRange } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './log2.cache.js';
export const g = makeTestGroup(GPUTest);
-// log2's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] }
-const f32_inputs = [
- ...linearRange(kValue.f32.positive.min, 0.5, 20),
- ...linearRange(0.5, 2.0, 20),
- ...biasedRange(2.0, 2 ** 32, 1000),
- ...fullF32Range(),
-];
-const f16_inputs = [
- ...linearRange(kValue.f16.positive.min, 0.5, 20),
- ...linearRange(0.5, 2.0, 20),
- ...biasedRange(2.0, 2 ** 32, 1000),
- ...fullF16Range(),
-];
-
-export const d = makeCaseCache('log2', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.log2Interval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.log2Interval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.log2Interval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.log2Interval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('log2'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -71,7 +51,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1]
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('log2'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('log2'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -85,5 +65,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('log2'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('log2'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.cache.ts
new file mode 100644
index 0000000000..72def03dab
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.cache.ts
@@ -0,0 +1,18 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarPairToIntervalCases(
+ FP[trait].sparseScalarRange(),
+ FP[trait].sparseScalarRange(),
+ 'unfiltered',
+ FP[trait].maxInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('max', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts
index 6654b4951c..ee7cb0d674 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts
@@ -1,12 +1,12 @@
export const description = `
Execution tests for the 'max' builtin function
-S is AbstractInt, i32, or u32
+S is abstract-int, i32, or u32
T is S or vecN<S>
@const fn max(e1: T ,e2: T) -> T
Returns e2 if e1 is less than e2, and e1 otherwise. Component-wise when T is a vector.
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is vecN<S>
@const fn max(e1: T ,e2: T) -> T
Returns e2 if e1 is less than e2, and e1 otherwise.
@@ -18,72 +18,50 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import {
- i32,
- TypeF32,
- TypeF16,
- TypeI32,
- TypeU32,
- u32,
- TypeAbstractFloat,
-} from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range, sparseF64Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, Case, onlyConstInputSource, run } from '../../expression.js';
-
-import { abstractBuiltin, builtin } from './builtin.js';
+import { Type, i32, u32, abstractInt } from '../../../../../util/conversion.js';
+import { maxBigInt } from '../../../../../util/math.js';
+import { Case } from '../../case.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
+
+import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js';
+import { d } from './max.cache.js';
/** Generate set of max test cases from list of interesting values */
-function generateTestCases(
- values: Array<number>,
- makeCase: (x: number, y: number) => Case
-): Array<Case> {
- const cases = new Array<Case>();
- values.forEach(e => {
- values.forEach(f => {
- cases.push(makeCase(e, f));
+function generateTestCases<Type>(values: Type[], makeCase: (x: Type, y: Type) => Case): Case[] {
+ return values.flatMap(e => {
+ return values.map(f => {
+ return makeCase(e, f);
});
});
- return cases;
}
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('max', {
- f32: () => {
- return FP.f32.generateScalarPairToIntervalCases(
- fullF32Range(),
- fullF32Range(),
- 'unfiltered',
- FP.f32.maxInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarPairToIntervalCases(
- fullF16Range(),
- fullF16Range(),
- 'unfiltered',
- FP.f16.maxInterval
- );
- },
- abstract: () => {
- return FP.abstract.generateScalarPairToIntervalCases(
- sparseF64Range(),
- sparseF64Range(),
- 'unfiltered',
- FP.abstract.maxInterval
- );
- },
-});
-
g.test('abstract_int')
.specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
.desc(`abstract int tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const makeCase = (x: bigint, y: bigint): Case => {
+ return { input: [abstractInt(x), abstractInt(y)], expected: abstractInt(maxBigInt(x, y)) };
+ };
+
+ const test_values = [-0x70000000n, -2n, -1n, 0n, 1n, 2n, 0x70000000n];
+ const cases = generateTestCases(test_values, makeCase);
+
+ await run(
+ t,
+ abstractIntBuiltin('max'),
+ [Type.abstractInt, Type.abstractInt],
+ Type.abstractInt,
+ t.params,
+ cases
+ );
+ });
g.test('u32')
.specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
@@ -96,10 +74,10 @@ g.test('u32')
return { input: [u32(x), u32(y)], expected: u32(Math.max(x, y)) };
};
- const test_values: Array<number> = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff];
+ const test_values: number[] = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff];
const cases = generateTestCases(test_values, makeCase);
- await run(t, builtin('max'), [TypeU32, TypeU32], TypeU32, t.params, cases);
+ await run(t, builtin('max'), [Type.u32, Type.u32], Type.u32, t.params, cases);
});
g.test('i32')
@@ -113,10 +91,10 @@ g.test('i32')
return { input: [i32(x), i32(y)], expected: i32(Math.max(x, y)) };
};
- const test_values: Array<number> = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000];
+ const test_values: number[] = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000];
const cases = generateTestCases(test_values, makeCase);
- await run(t, builtin('max'), [TypeI32, TypeI32], TypeI32, t.params, cases);
+ await run(t, builtin('max'), [Type.i32, Type.i32], Type.i32, t.params, cases);
});
g.test('abstract_float')
@@ -131,9 +109,9 @@ g.test('abstract_float')
const cases = await d.get('abstract');
await run(
t,
- abstractBuiltin('max'),
- [TypeAbstractFloat, TypeAbstractFloat],
- TypeAbstractFloat,
+ abstractFloatBuiltin('max'),
+ [Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
t.params,
cases
);
@@ -147,7 +125,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('max'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('max'), [Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -161,5 +139,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('max'), [TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('max'), [Type.f16, Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.cache.ts
new file mode 100644
index 0000000000..cddca325f0
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.cache.ts
@@ -0,0 +1,18 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarPairToIntervalCases(
+ FP[trait].sparseScalarRange(),
+ FP[trait].sparseScalarRange(),
+ 'unfiltered',
+ FP[trait].minInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('min', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts
index 6c05319546..ac63641399 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts
@@ -1,12 +1,12 @@
export const description = `
Execution tests for the 'min' builtin function
-S is AbstractInt, i32, or u32
+S is abstract-int, i32, or u32
T is S or vecN<S>
@const fn min(e1: T ,e2: T) -> T
Returns e1 if e1 is less than e2, and e2 otherwise. Component-wise when T is a vector.
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn min(e1: T ,e2: T) -> T
Returns e2 if e2 is less than e1, and e1 otherwise.
@@ -17,72 +17,50 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import {
- i32,
- TypeF32,
- TypeF16,
- TypeI32,
- TypeU32,
- u32,
- TypeAbstractFloat,
-} from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range, sparseF64Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, Case, onlyConstInputSource, run } from '../../expression.js';
-
-import { abstractBuiltin, builtin } from './builtin.js';
+import { Type, i32, u32, abstractInt } from '../../../../../util/conversion.js';
+import { minBigInt } from '../../../../../util/math.js';
+import { Case } from '../../case.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-export const g = makeTestGroup(GPUTest);
+import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js';
+import { d } from './min.cache.js';
-export const d = makeCaseCache('min', {
- f32: () => {
- return FP.f32.generateScalarPairToIntervalCases(
- fullF32Range(),
- fullF32Range(),
- 'unfiltered',
- FP.f32.minInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarPairToIntervalCases(
- fullF16Range(),
- fullF16Range(),
- 'unfiltered',
- FP.f16.minInterval
- );
- },
- abstract: () => {
- return FP.abstract.generateScalarPairToIntervalCases(
- sparseF64Range(),
- sparseF64Range(),
- 'unfiltered',
- FP.abstract.minInterval
- );
- },
-});
+export const g = makeTestGroup(GPUTest);
/** Generate set of min test cases from list of interesting values */
-function generateTestCases(
- values: Array<number>,
- makeCase: (x: number, y: number) => Case
-): Array<Case> {
- const cases = new Array<Case>();
- values.forEach(e => {
- values.forEach(f => {
- cases.push(makeCase(e, f));
+function generateTestCases<Type>(values: Type[], makeCase: (x: Type, y: Type) => Case): Case[] {
+ return values.flatMap(e => {
+ return values.map(f => {
+ return makeCase(e, f);
});
});
- return cases;
}
g.test('abstract_int')
.specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
.desc(`abstract int tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const makeCase = (x: bigint, y: bigint): Case => {
+ return { input: [abstractInt(x), abstractInt(y)], expected: abstractInt(minBigInt(x, y)) };
+ };
+
+ const test_values = [-0x70000000n, -2n, -1n, 0n, 1n, 2n, 0x70000000n];
+ const cases = generateTestCases(test_values, makeCase);
+
+ await run(
+ t,
+ abstractIntBuiltin('min'),
+ [Type.abstractInt, Type.abstractInt],
+ Type.abstractInt,
+ t.params,
+ cases
+ );
+ });
g.test('u32')
.specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions')
@@ -95,10 +73,10 @@ g.test('u32')
return { input: [u32(x), u32(y)], expected: u32(Math.min(x, y)) };
};
- const test_values: Array<number> = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff];
+ const test_values: number[] = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff];
const cases = generateTestCases(test_values, makeCase);
- await run(t, builtin('min'), [TypeU32, TypeU32], TypeU32, t.params, cases);
+ await run(t, builtin('min'), [Type.u32, Type.u32], Type.u32, t.params, cases);
});
g.test('i32')
@@ -112,10 +90,10 @@ g.test('i32')
return { input: [i32(x), i32(y)], expected: i32(Math.min(x, y)) };
};
- const test_values: Array<number> = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000];
+ const test_values: number[] = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000];
const cases = generateTestCases(test_values, makeCase);
- await run(t, builtin('min'), [TypeI32, TypeI32], TypeI32, t.params, cases);
+ await run(t, builtin('min'), [Type.i32, Type.i32], Type.i32, t.params, cases);
});
g.test('abstract_float')
@@ -130,9 +108,9 @@ g.test('abstract_float')
const cases = await d.get('abstract');
await run(
t,
- abstractBuiltin('min'),
- [TypeAbstractFloat, TypeAbstractFloat],
- TypeAbstractFloat,
+ abstractFloatBuiltin('min'),
+ [Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
t.params,
cases
);
@@ -146,7 +124,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('min'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('min'), [Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -160,5 +138,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('min'), [TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('min'), [Type.f16, Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.cache.ts
new file mode 100644
index 0000000000..be221297b0
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.cache.ts
@@ -0,0 +1,56 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { selectNCases } from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+// abstract_non_const is empty and unused
+const scalar_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ const cases = FP[trait].generateScalarTripleToIntervalCases(
+ FP[trait].sparseScalarRange(),
+ FP[trait].sparseScalarRange(),
+ FP[trait].sparseScalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // mix has an inherited accuracy, so abstract is only expected to be as accurate as f32
+ ...FP[trait !== 'abstract' ? trait : 'f32'].mixIntervals
+ );
+ return selectNCases('mix_scalar', trait === 'abstract' ? 50 : cases.length, cases);
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+// Cases: [f32|f16]_vecN_scalar_[non_]const
+// abstract_vecN_non_const is empty and unused
+const vec_scalar_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(dim =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ const cases = FP[trait].generateVectorPairScalarToVectorComponentWiseCase(
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseScalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // mix has an inherited accuracy, so abstract is only expected to be as accurate as f32
+ ...FP[trait !== 'abstract' ? trait : 'f32'].mixIntervals
+ );
+ return selectNCases('mix_vector', trait === 'abstract' ? 50 : cases.length, cases);
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('mix', {
+ ...scalar_cases,
+ ...vec_scalar_cases,
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts
index 95e9f6b310..0005ab5c0e 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts
@@ -1,12 +1,12 @@
export const description = `
Execution tests for the 'mix' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn mix(e1: T, e2: T, e3: T) -> T
Returns the linear blend of e1 and e2 (e.g. e1*(1-e3)+e2*e3). Component-wise when T is a vector.
-T is AbstractFloat, f32, or f16
+T is abstract-float, f32, or f16
T2 is vecN<T>
@const fn mix(e1: T2, e2: T2, e3: T) -> T2
Returns the component-wise linear blend of e1 and e2, using scalar blending factor e3 for each component.
@@ -16,121 +16,81 @@ Same as mix(e1,e2,T2(e3)).
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeVec, TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import {
- sparseF32Range,
- sparseF16Range,
- sparseVectorF32Range,
- sparseVectorF16Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './mix.cache.js';
export const g = makeTestGroup(GPUTest);
-// Cases: f32_vecN_scalar_[non_]const
-const f32_vec_scalar_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f32_vec${n}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f32.generateVectorPairScalarToVectorComponentWiseCase(
- sparseVectorF32Range(n),
- sparseVectorF32Range(n),
- sparseF32Range(),
- nonConst ? 'unfiltered' : 'finite',
- ...FP.f32.mixIntervals
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: f16_vecN_scalar_[non_]const
-const f16_vec_scalar_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f16_vec${n}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f16.generateVectorPairScalarToVectorComponentWiseCase(
- sparseVectorF16Range(n),
- sparseVectorF16Range(n),
- sparseF16Range(),
- nonConst ? 'unfiltered' : 'finite',
- ...FP.f16.mixIntervals
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-export const d = makeCaseCache('mix', {
- f32_const: () => {
- return FP.f32.generateScalarTripleToIntervalCases(
- sparseF32Range(),
- sparseF32Range(),
- sparseF32Range(),
- 'finite',
- ...FP.f32.mixIntervals
- );
- },
- f32_non_const: () => {
- return FP.f32.generateScalarTripleToIntervalCases(
- sparseF32Range(),
- sparseF32Range(),
- sparseF32Range(),
- 'unfiltered',
- ...FP.f32.mixIntervals
- );
- },
- ...f32_vec_scalar_cases,
- f16_const: () => {
- return FP.f16.generateScalarTripleToIntervalCases(
- sparseF16Range(),
- sparseF16Range(),
- sparseF16Range(),
- 'finite',
- ...FP.f16.mixIntervals
- );
- },
- f16_non_const: () => {
- return FP.f16.generateScalarTripleToIntervalCases(
- sparseF16Range(),
- sparseF16Range(),
- sparseF16Range(),
- 'unfiltered',
- ...FP.f16.mixIntervals
- );
- },
- ...f16_vec_scalar_cases,
-});
-
g.test('abstract_float_matching')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract_float test with matching third param`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('mix'),
+ [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('abstract_float_nonmatching_vec2')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract_float tests with two vec2<abstract_float> params and scalar third param`)
- .params(u => u.combine('inputSource', allInputSources))
- .unimplemented();
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec2_scalar_const');
+ await run(
+ t,
+ abstractFloatBuiltin('mix'),
+ [Type.vec(2, Type.abstractFloat), Type.vec(2, Type.abstractFloat), Type.abstractFloat],
+ Type.vec(2, Type.abstractFloat),
+ t.params,
+ cases
+ );
+ });
g.test('abstract_float_nonmatching_vec3')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract_float tests with two vec3<abstract_float> params and scalar third param`)
- .params(u => u.combine('inputSource', allInputSources))
- .unimplemented();
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec3_scalar_const');
+ await run(
+ t,
+ abstractFloatBuiltin('mix'),
+ [Type.vec(3, Type.abstractFloat), Type.vec(3, Type.abstractFloat), Type.abstractFloat],
+ Type.vec(3, Type.abstractFloat),
+ t.params,
+ cases
+ );
+ });
g.test('abstract_float_nonmatching_vec4')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract_float tests with two vec4<abstract_float> params and scalar third param`)
- .params(u => u.combine('inputSource', allInputSources))
- .unimplemented();
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec4_scalar_const');
+ await run(
+ t,
+ abstractFloatBuiltin('mix'),
+ [Type.vec(4, Type.abstractFloat), Type.vec(4, Type.abstractFloat), Type.abstractFloat],
+ Type.vec(4, Type.abstractFloat),
+ t.params,
+ cases
+ );
+ });
g.test('f32_matching')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -140,7 +100,7 @@ g.test('f32_matching')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('mix'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('mix'), [Type.f32, Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f32_nonmatching_vec2')
@@ -151,14 +111,7 @@ g.test('f32_nonmatching_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec2_scalar_const' : 'f32_vec2_scalar_non_const'
);
- await run(
- t,
- builtin('mix'),
- [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeF32],
- TypeVec(2, TypeF32),
- t.params,
- cases
- );
+ await run(t, builtin('mix'), [Type.vec2f, Type.vec2f, Type.f32], Type.vec2f, t.params, cases);
});
g.test('f32_nonmatching_vec3')
@@ -169,14 +122,7 @@ g.test('f32_nonmatching_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec3_scalar_const' : 'f32_vec3_scalar_non_const'
);
- await run(
- t,
- builtin('mix'),
- [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeF32],
- TypeVec(3, TypeF32),
- t.params,
- cases
- );
+ await run(t, builtin('mix'), [Type.vec3f, Type.vec3f, Type.f32], Type.vec3f, t.params, cases);
});
g.test('f32_nonmatching_vec4')
@@ -187,14 +133,7 @@ g.test('f32_nonmatching_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec4_scalar_const' : 'f32_vec4_scalar_non_const'
);
- await run(
- t,
- builtin('mix'),
- [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeF32],
- TypeVec(4, TypeF32),
- t.params,
- cases
- );
+ await run(t, builtin('mix'), [Type.vec4f, Type.vec4f, Type.f32], Type.vec4f, t.params, cases);
});
g.test('f16_matching')
@@ -208,7 +147,7 @@ g.test('f16_matching')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('mix'), [TypeF16, TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('mix'), [Type.f16, Type.f16, Type.f16], Type.f16, t.params, cases);
});
g.test('f16_nonmatching_vec2')
@@ -222,14 +161,7 @@ g.test('f16_nonmatching_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec2_scalar_const' : 'f16_vec2_scalar_non_const'
);
- await run(
- t,
- builtin('mix'),
- [TypeVec(2, TypeF16), TypeVec(2, TypeF16), TypeF16],
- TypeVec(2, TypeF16),
- t.params,
- cases
- );
+ await run(t, builtin('mix'), [Type.vec2h, Type.vec2h, Type.f16], Type.vec2h, t.params, cases);
});
g.test('f16_nonmatching_vec3')
@@ -243,14 +175,7 @@ g.test('f16_nonmatching_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec3_scalar_const' : 'f16_vec3_scalar_non_const'
);
- await run(
- t,
- builtin('mix'),
- [TypeVec(3, TypeF16), TypeVec(3, TypeF16), TypeF16],
- TypeVec(3, TypeF16),
- t.params,
- cases
- );
+ await run(t, builtin('mix'), [Type.vec3h, Type.vec3h, Type.f16], Type.vec3h, t.params, cases);
});
g.test('f16_nonmatching_vec4')
@@ -264,12 +189,5 @@ g.test('f16_nonmatching_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec4_scalar_const' : 'f16_vec4_scalar_non_const'
);
- await run(
- t,
- builtin('mix'),
- [TypeVec(4, TypeF16), TypeVec(4, TypeF16), TypeF16],
- TypeVec(4, TypeF16),
- t.params,
- cases
- );
+ await run(t, builtin('mix'), [Type.vec4h, Type.vec4h, Type.f16], Type.vec4h, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.cache.ts
new file mode 100644
index 0000000000..1a76de56bb
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.cache.ts
@@ -0,0 +1,75 @@
+import { toVector } from '../../../../../util/conversion.js';
+import { FP, FPKind } from '../../../../../util/floating_point.js';
+import { Case } from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+/** @returns a fract Case for a scalar vector input */
+function makeScalarCaseFract(kind: FPKind, n: number): Case {
+ const fp = FP[kind];
+ n = fp.quantize(n);
+ const result = fp.modfInterval(n).fract;
+
+ return { input: fp.scalarBuilder(n), expected: result };
+}
+
+/** @returns a whole Case for a scalar vector input */
+function makeScalarCaseWhole(kind: FPKind, n: number): Case {
+ const fp = FP[kind];
+ n = fp.quantize(n);
+ const result = fp.modfInterval(n).whole;
+
+ return { input: fp.scalarBuilder(n), expected: result };
+}
+
+/** @returns a fract Case for a given vector input */
+function makeVectorCaseFract(kind: FPKind, v: readonly number[]): Case {
+ const fp = FP[kind];
+ v = v.map(fp.quantize);
+ const fs = v.map(e => {
+ return fp.modfInterval(e).fract;
+ });
+
+ return { input: toVector(v, fp.scalarBuilder), expected: fs };
+}
+
+/** @returns a whole Case for a given vector input */
+function makeVectorCaseWhole(kind: FPKind, v: readonly number[]): Case {
+ const fp = FP[kind];
+ v = v.map(fp.quantize);
+ const ws = v.map(e => {
+ return fp.modfInterval(e).whole;
+ });
+
+ return { input: toVector(v, fp.scalarBuilder), expected: ws };
+}
+
+// Cases: [f32|f16|abstract]_[fract|whole]
+const scalar_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(kind =>
+ (['whole', 'fract'] as const).map(portion => ({
+ [`${kind}_${portion}`]: () => {
+ const makeCase = portion === 'whole' ? makeScalarCaseWhole : makeScalarCaseFract;
+ return FP[kind].scalarRange().map(makeCase.bind(null, kind));
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+// Cases: [f32|f16|abstract]_vecN_[fract|whole]
+const vec_cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(kind =>
+ ([2, 3, 4] as const).flatMap(n =>
+ (['whole', 'fract'] as const).map(portion => ({
+ [`${kind}_vec${n}_${portion}`]: () => {
+ const makeCase = portion === 'whole' ? makeVectorCaseWhole : makeVectorCaseFract;
+ return FP[kind].vectorRange(n).map(makeCase.bind(null, kind));
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('modf', {
+ ...scalar_cases,
+ ...vec_cases,
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts
index 1a3d8a2850..6c988008f8 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts
@@ -1,13 +1,13 @@
export const description = `
Execution tests for the 'modf' builtin function
-T is f32 or f16 or AbstractFloat
+T is f32 or f16 or Type.abstractFloat
@const fn modf(e:T) -> result_struct
Splits |e| into fractional and whole number parts.
The whole part is (|e| % 1.0), and the fractional part is |e| minus the whole part.
Returns the result_struct for the given type.
-S is f32 or f16 or AbstractFloat
+S is f32 or f16 or Type.abstractFloat
T is vecN<S>
@const fn modf(e:T) -> result_struct
Splits the components of |e| into fractional and whole number parts.
@@ -18,33 +18,18 @@ Returns the result_struct for the given type.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import {
- toVector,
- TypeAbstractFloat,
- TypeF16,
- TypeF32,
- TypeVec,
-} from '../../../../../util/conversion.js';
-import { FP, FPKind } from '../../../../../util/floating_point.js';
-import {
- fullF16Range,
- fullF32Range,
- fullF64Range,
- vectorF16Range,
- vectorF32Range,
- vectorF64Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import {
abstractFloatShaderBuilder,
allInputSources,
basicExpressionBuilder,
- Case,
onlyConstInputSource,
run,
ShaderBuilder,
} from '../../expression.js';
+import { d } from './modf.cache.js';
+
export const g = makeTestGroup(GPUTest);
/** @returns an ShaderBuilder that evaluates modf and returns .whole from the result structure */
@@ -67,101 +52,6 @@ function abstractFractBuilder(): ShaderBuilder {
return abstractFloatShaderBuilder(value => `modf(${value}).fract`);
}
-/** @returns a fract Case for a scalar vector input */
-function makeScalarCaseFract(kind: FPKind, n: number): Case {
- const fp = FP[kind];
- n = fp.quantize(n);
- const result = fp.modfInterval(n).fract;
-
- return { input: fp.scalarBuilder(n), expected: result };
-}
-
-/** @returns a whole Case for a scalar vector input */
-function makeScalarCaseWhole(kind: FPKind, n: number): Case {
- const fp = FP[kind];
- n = fp.quantize(n);
- const result = fp.modfInterval(n).whole;
-
- return { input: fp.scalarBuilder(n), expected: result };
-}
-
-/** @returns a fract Case for a given vector input */
-function makeVectorCaseFract(kind: FPKind, v: readonly number[]): Case {
- const fp = FP[kind];
- v = v.map(fp.quantize);
- const fs = v.map(e => {
- return fp.modfInterval(e).fract;
- });
-
- return { input: toVector(v, fp.scalarBuilder), expected: fs };
-}
-
-/** @returns a whole Case for a given vector input */
-function makeVectorCaseWhole(kind: FPKind, v: readonly number[]): Case {
- const fp = FP[kind];
- v = v.map(fp.quantize);
- const ws = v.map(e => {
- return fp.modfInterval(e).whole;
- });
-
- return { input: toVector(v, fp.scalarBuilder), expected: ws };
-}
-
-const scalar_range = {
- f32: fullF32Range(),
- f16: fullF16Range(),
- abstract: fullF64Range(),
-};
-
-const vector_range = {
- f32: {
- 2: vectorF32Range(2),
- 3: vectorF32Range(3),
- 4: vectorF32Range(4),
- },
- f16: {
- 2: vectorF16Range(2),
- 3: vectorF16Range(3),
- 4: vectorF16Range(4),
- },
- abstract: {
- 2: vectorF64Range(2),
- 3: vectorF64Range(3),
- 4: vectorF64Range(4),
- },
-};
-
-// Cases: [f32|f16|abstract]_[fract|whole]
-const scalar_cases = (['f32', 'f16', 'abstract'] as const)
- .flatMap(kind =>
- (['whole', 'fract'] as const).map(portion => ({
- [`${kind}_${portion}`]: () => {
- const makeCase = portion === 'whole' ? makeScalarCaseWhole : makeScalarCaseFract;
- return scalar_range[kind].map(makeCase.bind(null, kind));
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: [f32|f16|abstract]_vecN_[fract|whole]
-const vec_cases = (['f32', 'f16', 'abstract'] as const)
- .flatMap(kind =>
- ([2, 3, 4] as const).flatMap(n =>
- (['whole', 'fract'] as const).map(portion => ({
- [`${kind}_vec${n}_${portion}`]: () => {
- const makeCase = portion === 'whole' ? makeVectorCaseWhole : makeVectorCaseFract;
- return vector_range[kind][n].map(makeCase.bind(null, kind));
- },
- }))
- )
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-export const d = makeCaseCache('modf', {
- ...scalar_cases,
- ...vec_cases,
-});
-
g.test('f32_fract')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(
@@ -177,7 +67,7 @@ struct __modf_result_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_fract');
- await run(t, fractBuilder(), [TypeF32], TypeF32, t.params, cases);
+ await run(t, fractBuilder(), [Type.f32], Type.f32, t.params, cases);
});
g.test('f32_whole')
@@ -195,7 +85,7 @@ struct __modf_result_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_whole');
- await run(t, wholeBuilder(), [TypeF32], TypeF32, t.params, cases);
+ await run(t, wholeBuilder(), [Type.f32], Type.f32, t.params, cases);
});
g.test('f32_vec2_fract')
@@ -213,7 +103,7 @@ struct __modf_result_vec2_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec2_fract');
- await run(t, fractBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec2f], Type.vec2f, t.params, cases);
});
g.test('f32_vec2_whole')
@@ -231,7 +121,7 @@ struct __modf_result_vec2_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec2_whole');
- await run(t, wholeBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases);
+ await run(t, wholeBuilder(), [Type.vec2f], Type.vec2f, t.params, cases);
});
g.test('f32_vec3_fract')
@@ -249,7 +139,7 @@ struct __modf_result_vec3_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec3_fract');
- await run(t, fractBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec3f], Type.vec3f, t.params, cases);
});
g.test('f32_vec3_whole')
@@ -267,7 +157,7 @@ struct __modf_result_vec3_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec3_whole');
- await run(t, wholeBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases);
+ await run(t, wholeBuilder(), [Type.vec3f], Type.vec3f, t.params, cases);
});
g.test('f32_vec4_fract')
@@ -285,7 +175,7 @@ struct __modf_result_vec4_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec4_fract');
- await run(t, fractBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec4f], Type.vec4f, t.params, cases);
});
g.test('f32_vec4_whole')
@@ -303,7 +193,7 @@ struct __modf_result_vec4_f32 {
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get('f32_vec4_whole');
- await run(t, wholeBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases);
+ await run(t, wholeBuilder(), [Type.vec4f], Type.vec4f, t.params, cases);
});
g.test('f16_fract')
@@ -324,7 +214,7 @@ struct __modf_result_f16 {
})
.fn(async t => {
const cases = await d.get('f16_fract');
- await run(t, fractBuilder(), [TypeF16], TypeF16, t.params, cases);
+ await run(t, fractBuilder(), [Type.f16], Type.f16, t.params, cases);
});
g.test('f16_whole')
@@ -345,7 +235,7 @@ struct __modf_result_f16 {
})
.fn(async t => {
const cases = await d.get('f16_whole');
- await run(t, wholeBuilder(), [TypeF16], TypeF16, t.params, cases);
+ await run(t, wholeBuilder(), [Type.f16], Type.f16, t.params, cases);
});
g.test('f16_vec2_fract')
@@ -366,7 +256,7 @@ struct __modf_result_vec2_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec2_fract');
- await run(t, fractBuilder(), [TypeVec(2, TypeF16)], TypeVec(2, TypeF16), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec2h], Type.vec2h, t.params, cases);
});
g.test('f16_vec2_whole')
@@ -387,7 +277,7 @@ struct __modf_result_vec2_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec2_whole');
- await run(t, wholeBuilder(), [TypeVec(2, TypeF16)], TypeVec(2, TypeF16), t.params, cases);
+ await run(t, wholeBuilder(), [Type.vec2h], Type.vec2h, t.params, cases);
});
g.test('f16_vec3_fract')
@@ -408,7 +298,7 @@ struct __modf_result_vec3_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec3_fract');
- await run(t, fractBuilder(), [TypeVec(3, TypeF16)], TypeVec(3, TypeF16), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec3h], Type.vec3h, t.params, cases);
});
g.test('f16_vec3_whole')
@@ -429,7 +319,7 @@ struct __modf_result_vec3_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec3_whole');
- await run(t, wholeBuilder(), [TypeVec(3, TypeF16)], TypeVec(3, TypeF16), t.params, cases);
+ await run(t, wholeBuilder(), [Type.vec3h], Type.vec3h, t.params, cases);
});
g.test('f16_vec4_fract')
@@ -450,7 +340,7 @@ struct __modf_result_vec4_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec4_fract');
- await run(t, fractBuilder(), [TypeVec(4, TypeF16)], TypeVec(4, TypeF16), t.params, cases);
+ await run(t, fractBuilder(), [Type.vec4h], Type.vec4h, t.params, cases);
});
g.test('f16_vec4_whole')
@@ -471,43 +361,43 @@ struct __modf_result_vec4_f16 {
})
.fn(async t => {
const cases = await d.get('f16_vec4_whole');
- await run(t, wholeBuilder(), [TypeVec(4, TypeF16)], TypeVec(4, TypeF16), t.params, cases);
+ await run(t, wholeBuilder(), [Type.vec4h], Type.vec4h, t.params, cases);
});
g.test('abstract_fract')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(
`
-T is AbstractFloat
+T is abstract-float
struct __modf_result_abstract {
- fract : AbstractFloat, // fractional part
- whole : AbstractFloat // whole part
+ fract : Type.abstractFloat, // fractional part
+ whole : Type.abstractFloat // whole part
}
`
)
.params(u => u.combine('inputSource', onlyConstInputSource))
.fn(async t => {
const cases = await d.get('abstract_fract');
- await run(t, abstractFractBuilder(), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases);
+ await run(t, abstractFractBuilder(), [Type.abstractFloat], Type.abstractFloat, t.params, cases);
});
g.test('abstract_whole')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(
`
-T is AbstractFloat
+T is abstract-float
struct __modf_result_abstract {
- fract : AbstractFloat, // fractional part
- whole : AbstractFloat // whole part
+ fract : Type.abstractFloat, // fractional part
+ whole : Type.abstractFloat // whole part
}
`
)
.params(u => u.combine('inputSource', onlyConstInputSource))
.fn(async t => {
const cases = await d.get('abstract_whole');
- await run(t, abstractWholeBuilder(), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases);
+ await run(t, abstractWholeBuilder(), [Type.abstractFloat], Type.abstractFloat, t.params, cases);
});
g.test('abstract_vec2_fract')
@@ -528,8 +418,8 @@ struct __modf_result_vec2_abstract {
await run(
t,
abstractFractBuilder(),
- [TypeVec(2, TypeAbstractFloat)],
- TypeVec(2, TypeAbstractFloat),
+ [Type.vec(2, Type.abstractFloat)],
+ Type.vec(2, Type.abstractFloat),
t.params,
cases
);
@@ -553,8 +443,8 @@ struct __modf_result_vec2_abstract {
await run(
t,
abstractWholeBuilder(),
- [TypeVec(2, TypeAbstractFloat)],
- TypeVec(2, TypeAbstractFloat),
+ [Type.vec(2, Type.abstractFloat)],
+ Type.vec(2, Type.abstractFloat),
t.params,
cases
);
@@ -578,8 +468,8 @@ struct __modf_result_vec3_abstract {
await run(
t,
abstractFractBuilder(),
- [TypeVec(3, TypeAbstractFloat)],
- TypeVec(3, TypeAbstractFloat),
+ [Type.vec(3, Type.abstractFloat)],
+ Type.vec(3, Type.abstractFloat),
t.params,
cases
);
@@ -603,8 +493,8 @@ struct __modf_result_vec3_abstract {
await run(
t,
abstractWholeBuilder(),
- [TypeVec(3, TypeAbstractFloat)],
- TypeVec(3, TypeAbstractFloat),
+ [Type.vec(3, Type.abstractFloat)],
+ Type.vec(3, Type.abstractFloat),
t.params,
cases
);
@@ -628,8 +518,8 @@ struct __modf_result_vec4_abstract {
await run(
t,
abstractFractBuilder(),
- [TypeVec(4, TypeAbstractFloat)],
- TypeVec(4, TypeAbstractFloat),
+ [Type.vec(4, Type.abstractFloat)],
+ Type.vec(4, Type.abstractFloat),
t.params,
cases
);
@@ -653,8 +543,8 @@ struct __modf_result_vec4_abstract {
await run(
t,
abstractWholeBuilder(),
- [TypeVec(4, TypeAbstractFloat)],
- TypeVec(4, TypeAbstractFloat),
+ [Type.vec(4, Type.abstractFloat)],
+ Type.vec(4, Type.abstractFloat),
t.params,
cases
);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.cache.ts
new file mode 100644
index 0000000000..7da5a43c22
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.cache.ts
@@ -0,0 +1,25 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_vecN_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(dim =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateVectorToVectorCases(
+ FP[trait].vectorRange(dim),
+ nonConst ? 'unfiltered' : 'finite',
+ // normalize has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].normalizeInterval
+ );
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('normalize', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts
index 615617b448..06be8a125e 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts
@@ -1,65 +1,47 @@
export const description = `
Execution tests for the 'normalize' builtin function
-T is AbstractFloat, f32, or f16
+T is abstract-float, f32, or f16
@const fn normalize(e: vecN<T> ) -> vecN<T>
Returns a unit vector in the same direction as e.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { vectorF32Range, vectorF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './normalize.cache.js';
export const g = makeTestGroup(GPUTest);
-// Cases: f32_vecN_[non_]const
-const f32_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f32_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f32.generateVectorToVectorCases(
- vectorF32Range(n),
- nonConst ? 'unfiltered' : 'finite',
- FP.f32.normalizeInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: f16_vecN_[non_]const
-const f16_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f16_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f16.generateVectorToVectorCases(
- vectorF16Range(n),
- nonConst ? 'unfiltered' : 'finite',
- FP.f16.normalizeInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
+g.test('abstract_float_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec2s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec2_const');
+ await run(t, abstractFloatBuiltin('normalize'), [Type.vec2af], Type.vec2af, t.params, cases);
+ });
-export const d = makeCaseCache('normalize', {
- ...f32_vec_cases,
- ...f16_vec_cases,
-});
+g.test('abstract_float_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec3s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec3_const');
+ await run(t, abstractFloatBuiltin('normalize'), [Type.vec3af], Type.vec3af, t.params, cases);
+ });
-g.test('abstract_float')
- .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
- .desc(`abstract float tests`)
- .params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
- )
- .unimplemented();
+g.test('abstract_float_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec4s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec4_const');
+ await run(t, abstractFloatBuiltin('normalize'), [Type.vec4af], Type.vec4af, t.params, cases);
+ });
g.test('f32_vec2')
.specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
@@ -69,7 +51,7 @@ g.test('f32_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
);
- await run(t, builtin('normalize'), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases);
+ await run(t, builtin('normalize'), [Type.vec2f], Type.vec2f, t.params, cases);
});
g.test('f32_vec3')
@@ -80,7 +62,7 @@ g.test('f32_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
);
- await run(t, builtin('normalize'), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases);
+ await run(t, builtin('normalize'), [Type.vec3f], Type.vec3f, t.params, cases);
});
g.test('f32_vec4')
@@ -91,7 +73,7 @@ g.test('f32_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
);
- await run(t, builtin('normalize'), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases);
+ await run(t, builtin('normalize'), [Type.vec4f], Type.vec4f, t.params, cases);
});
g.test('f16_vec2')
@@ -105,7 +87,7 @@ g.test('f16_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const'
);
- await run(t, builtin('normalize'), [TypeVec(2, TypeF16)], TypeVec(2, TypeF16), t.params, cases);
+ await run(t, builtin('normalize'), [Type.vec2h], Type.vec2h, t.params, cases);
});
g.test('f16_vec3')
@@ -119,7 +101,7 @@ g.test('f16_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const'
);
- await run(t, builtin('normalize'), [TypeVec(3, TypeF16)], TypeVec(3, TypeF16), t.params, cases);
+ await run(t, builtin('normalize'), [Type.vec3h], Type.vec3h, t.params, cases);
});
g.test('f16_vec4')
@@ -133,5 +115,5 @@ g.test('f16_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const'
);
- await run(t, builtin('normalize'), [TypeVec(4, TypeF16)], TypeVec(4, TypeF16), t.params, cases);
+ await run(t, builtin('normalize'), [Type.vec4h], Type.vec4h, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.cache.ts
new file mode 100644
index 0000000000..9cd7824cd9
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.cache.ts
@@ -0,0 +1,55 @@
+import { anyOf, skipUndefined } from '../../../../../util/compare.js';
+import { f32, pack2x16float, u32, vec2 } from '../../../../../util/conversion.js';
+import { cartesianProduct, quantizeToF32, scalarF32Range } from '../../../../../util/math.js';
+import { Case } from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// pack2x16float has somewhat unusual behaviour, specifically around how it is
+// supposed to behave when values go OOB and when they are considered to have
+// gone OOB, so has its own bespoke implementation.
+
+/**
+ * @returns a Case for `pack2x16float`
+ * @param param0 first param for the case
+ * @param param1 second param for the case
+ * @param filter_undefined should inputs that cause an undefined expectation be
+ * filtered out, needed for const-eval
+ */
+function makeCase(param0: number, param1: number, filter_undefined: boolean): Case | undefined {
+ param0 = quantizeToF32(param0);
+ param1 = quantizeToF32(param1);
+
+ const results = pack2x16float(param0, param1);
+ if (filter_undefined && results.some(r => r === undefined)) {
+ return undefined;
+ }
+
+ return {
+ input: [vec2(f32(param0), f32(param1))],
+ expected: anyOf(
+ ...results.map(r => (r === undefined ? skipUndefined(undefined) : skipUndefined(u32(r))))
+ ),
+ };
+}
+
+/**
+ * @returns an array of Cases for `pack2x16float`
+ * @param param0s array of inputs to try for the first param
+ * @param param1s array of inputs to try for the second param
+ * @param filter_undefined should inputs that cause an undefined expectation be
+ * filtered out, needed for const-eval
+ */
+function generateCases(param0s: number[], param1s: number[], filter_undefined: boolean): Case[] {
+ return cartesianProduct(param0s, param1s)
+ .map(e => makeCase(e[0], e[1], filter_undefined))
+ .filter((c): c is Case => c !== undefined);
+}
+
+export const d = makeCaseCache('pack2x16float', {
+ f32_const: () => {
+ return generateCases(scalarF32Range(), scalarF32Range(), true);
+ },
+ f32_non_const: () => {
+ return generateCases(scalarF32Range(), scalarF32Range(), false);
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts
index 790e54720c..5ba6993427 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts
@@ -6,74 +6,14 @@ which is then placed in bits 16 × i through 16 × i + 15 of the result.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { anyOf, skipUndefined } from '../../../../../util/compare.js';
-import {
- f32,
- pack2x16float,
- TypeF32,
- TypeU32,
- TypeVec,
- u32,
- vec2,
-} from '../../../../../util/conversion.js';
-import { cartesianProduct, fullF32Range, quantizeToF32 } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, Case, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
+import { d } from './pack2x16float.cache.js';
export const g = makeTestGroup(GPUTest);
-// pack2x16float has somewhat unusual behaviour, specifically around how it is
-// supposed to behave when values go OOB and when they are considered to have
-// gone OOB, so has its own bespoke implementation.
-
-/**
- * @returns a Case for `pack2x16float`
- * @param param0 first param for the case
- * @param param1 second param for the case
- * @param filter_undefined should inputs that cause an undefined expectation be
- * filtered out, needed for const-eval
- */
-function makeCase(param0: number, param1: number, filter_undefined: boolean): Case | undefined {
- param0 = quantizeToF32(param0);
- param1 = quantizeToF32(param1);
-
- const results = pack2x16float(param0, param1);
- if (filter_undefined && results.some(r => r === undefined)) {
- return undefined;
- }
-
- return {
- input: [vec2(f32(param0), f32(param1))],
- expected: anyOf(
- ...results.map(r => (r === undefined ? skipUndefined(undefined) : skipUndefined(u32(r))))
- ),
- };
-}
-
-/**
- * @returns an array of Cases for `pack2x16float`
- * @param param0s array of inputs to try for the first param
- * @param param1s array of inputs to try for the second param
- * @param filter_undefined should inputs that cause an undefined expectation be
- * filtered out, needed for const-eval
- */
-function generateCases(param0s: number[], param1s: number[], filter_undefined: boolean): Case[] {
- return cartesianProduct(param0s, param1s)
- .map(e => makeCase(e[0], e[1], filter_undefined))
- .filter((c): c is Case => c !== undefined);
-}
-
-export const d = makeCaseCache('pack2x16float', {
- f32_const: () => {
- return generateCases(fullF32Range(), fullF32Range(), true);
- },
- f32_non_const: () => {
- return generateCases(fullF32Range(), fullF32Range(), false);
- },
-});
-
g.test('pack')
.specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions')
.desc(
@@ -84,5 +24,5 @@ g.test('pack')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('pack2x16float'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases);
+ await run(t, builtin('pack2x16float'), [Type.vec2f], Type.u32, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts
index 54bb21f6c6..1bcca2f73f 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts
@@ -8,17 +8,10 @@ bits 16 × i through 16 × i + 15 of the result.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
import { kValue } from '../../../../../util/constants.js';
-import {
- f32,
- pack2x16snorm,
- TypeF32,
- TypeU32,
- TypeVec,
- u32,
- vec2,
-} from '../../../../../util/conversion.js';
+import { f32, pack2x16snorm, u32, vec2, Type } from '../../../../../util/conversion.js';
import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js';
-import { allInputSources, Case, run } from '../../expression.js';
+import { Case } from '../../case.js';
+import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -51,5 +44,5 @@ g.test('pack')
];
});
- await run(t, builtin('pack2x16snorm'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases);
+ await run(t, builtin('pack2x16snorm'), [Type.vec2f], Type.u32, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts
index a875a9c7e1..334d106482 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts
@@ -8,17 +8,10 @@ bits 16 × i through 16 × i + 15 of the result.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
import { kValue } from '../../../../../util/constants.js';
-import {
- f32,
- pack2x16unorm,
- TypeF32,
- TypeU32,
- TypeVec,
- u32,
- vec2,
-} from '../../../../../util/conversion.js';
+import { f32, pack2x16unorm, u32, vec2, Type } from '../../../../../util/conversion.js';
import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js';
-import { allInputSources, Case, run } from '../../expression.js';
+import { Case } from '../../case.js';
+import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -51,5 +44,5 @@ g.test('pack')
];
});
- await run(t, builtin('pack2x16unorm'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases);
+ await run(t, builtin('pack2x16unorm'), [Type.vec2f], Type.u32, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts
index de0463e9fc..fbe362ea45 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts
@@ -8,18 +8,10 @@ bits 8 × i through 8 × i + 7 of the result.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
import { kValue } from '../../../../../util/constants.js';
-import {
- f32,
- pack4x8snorm,
- Scalar,
- TypeF32,
- TypeU32,
- TypeVec,
- u32,
- vec4,
-} from '../../../../../util/conversion.js';
+import { f32, pack4x8snorm, ScalarValue, u32, vec4, Type } from '../../../../../util/conversion.js';
import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js';
-import { allInputSources, Case, run } from '../../expression.js';
+import { Case } from '../../case.js';
+import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -35,7 +27,12 @@ g.test('pack')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const makeCase = (vals: [number, number, number, number]): Case => {
- const vals_f32 = new Array<Scalar>(4) as [Scalar, Scalar, Scalar, Scalar];
+ const vals_f32 = new Array<ScalarValue>(4) as [
+ ScalarValue,
+ ScalarValue,
+ ScalarValue,
+ ScalarValue,
+ ];
for (const idx in vals) {
vals[idx] = quantizeToF32(vals[idx]);
vals_f32[idx] = f32(vals[idx]);
@@ -56,5 +53,5 @@ g.test('pack')
];
});
- await run(t, builtin('pack4x8snorm'), [TypeVec(4, TypeF32)], TypeU32, t.params, cases);
+ await run(t, builtin('pack4x8snorm'), [Type.vec4f], Type.u32, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts
index b670e92fbb..c7d62e722b 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts
@@ -8,18 +8,10 @@ bits 8 × i through 8 × i + 7 of the result.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
import { kValue } from '../../../../../util/constants.js';
-import {
- f32,
- pack4x8unorm,
- Scalar,
- TypeF32,
- TypeU32,
- TypeVec,
- u32,
- vec4,
-} from '../../../../../util/conversion.js';
+import { f32, pack4x8unorm, ScalarValue, u32, vec4, Type } from '../../../../../util/conversion.js';
import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js';
-import { allInputSources, Case, run } from '../../expression.js';
+import { Case } from '../../case.js';
+import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -35,7 +27,12 @@ g.test('pack')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const makeCase = (vals: [number, number, number, number]): Case => {
- const vals_f32 = new Array<Scalar>(4) as [Scalar, Scalar, Scalar, Scalar];
+ const vals_f32 = new Array<ScalarValue>(4) as [
+ ScalarValue,
+ ScalarValue,
+ ScalarValue,
+ ScalarValue,
+ ];
for (const idx in vals) {
vals[idx] = quantizeToF32(vals[idx]);
vals_f32[idx] = f32(vals[idx]);
@@ -56,5 +53,5 @@ g.test('pack')
];
});
- await run(t, builtin('pack4x8unorm'), [TypeVec(4, TypeF32)], TypeU32, t.params, cases);
+ await run(t, builtin('pack4x8unorm'), [Type.vec4f], Type.u32, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts
new file mode 100644
index 0000000000..94aa67f0ae
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts
@@ -0,0 +1,69 @@
+export const description = `
+Execution tests for the 'pack4xI8' builtin function
+
+@const fn pack4xI8(e: vec4<i32>) -> u32
+Pack the lower 8 bits of each component of e into a u32 value and drop all the unused bits.
+Component e[i] of the input is mapped to bits (8 * i) through (8 * (i + 7)) of the result.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { u32, toVector, i32, Type } from '../../../../../util/conversion.js';
+import { Case } from '../../case.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('basic')
+ .specURL('https://www.w3.org/TR/WGSL/#pack4xI8-builtin')
+ .desc(
+ `
+@const fn pack4xI8(e: vec4<i32>) -> u32
+ `
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const pack4xI8 = (vals: readonly [number, number, number, number]) => {
+ const result = new Uint32Array(1);
+ for (let i = 0; i < 4; ++i) {
+ result[0] |= (vals[i] & 0xff) << (i * 8);
+ }
+ return result[0];
+ };
+
+ const testInputs = [
+ [0, 0, 0, 0],
+ [1, 2, 3, 4],
+ [-1, 2, 3, 4],
+ [1, -2, 3, 4],
+ [1, 2, -3, 4],
+ [1, 2, 3, -4],
+ [-1, -2, 3, 4],
+ [-1, 2, -3, 4],
+ [-1, 2, 3, -4],
+ [1, -2, -3, 4],
+ [1, -2, 3, -4],
+ [1, 2, -3, -4],
+ [-1, -2, -3, 4],
+ [-1, -2, 3, -4],
+ [-1, 2, -3, -4],
+ [1, -2, -3, -4],
+ [-1, -2, -3, -4],
+ [127, 128, -128, -129],
+ [128, 128, -128, -128],
+ [32767, 32768, -32768, -32769],
+ ] as const;
+
+ const makeCase = (vals: readonly [number, number, number, number]): Case => {
+ return { input: [toVector(vals, i32)], expected: u32(pack4xI8(vals)) };
+ };
+ const cases: Array<Case> = testInputs.flatMap(v => {
+ return [makeCase(v)];
+ });
+
+ await run(t, builtin('pack4xI8'), [Type.vec4i], Type.u32, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts
new file mode 100644
index 0000000000..4968ed1e04
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts
@@ -0,0 +1,73 @@
+export const description = `
+Execution tests for the 'pack4xI8Clamp' builtin function
+
+@const fn pack4xI8Clamp(e: vec4<i32>) -> u32
+Clamp each component of e in the range [-128, 127] and then pack the lower 8 bits of each component
+into a u32 value. Component e[i] of the input is mapped to bits (8 * i) through (8 * (i + 7)) of the
+result.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { u32, toVector, i32, Type } from '../../../../../util/conversion.js';
+import { clamp } from '../../../../../util/math.js';
+import { Case } from '../../case.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('basic')
+ .specURL('https://www.w3.org/TR/WGSL/#pack4xI8Clamp-builtin')
+ .desc(
+ `
+@const fn pack4xI8Clamp(e: vec4<i32>) -> u32
+ `
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const pack4xI8Clamp = (vals: readonly [number, number, number, number]) => {
+ const result = new Uint32Array(1);
+ for (let i = 0; i < 4; ++i) {
+ const clampedValue = clamp(vals[i], { min: -128, max: 127 });
+ result[0] |= (clampedValue & 0xff) << (i * 8);
+ }
+ return result[0];
+ };
+
+ const testInputs = [
+ [0, 0, 0, 0],
+ [1, 2, 3, 4],
+ [-1, 2, 3, 4],
+ [1, -2, 3, 4],
+ [1, 2, -3, 4],
+ [1, 2, 3, -4],
+ [-1, -2, 3, 4],
+ [-1, 2, -3, 4],
+ [-1, 2, 3, -4],
+ [1, -2, -3, 4],
+ [1, -2, 3, -4],
+ [1, 2, -3, -4],
+ [-1, -2, -3, 4],
+ [-1, -2, 3, -4],
+ [-1, 2, -3, -4],
+ [1, -2, -3, -4],
+ [-1, -2, -3, -4],
+ [126, 127, 128, 129],
+ [-130, -129, -128, -127],
+ [127, 128, -128, -129],
+ [32767, 32768, -32768, -32769],
+ ] as const;
+
+ const makeCase = (vals: readonly [number, number, number, number]): Case => {
+ return { input: [toVector(vals, i32)], expected: u32(pack4xI8Clamp(vals)) };
+ };
+ const cases: Array<Case> = testInputs.flatMap(v => {
+ return [makeCase(v)];
+ });
+
+ await run(t, builtin('pack4xI8Clamp'), [Type.vec4i], Type.u32, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts
new file mode 100644
index 0000000000..9d08e88d44
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts
@@ -0,0 +1,54 @@
+export const description = `
+Execution tests for the 'pack4xU8' builtin function
+
+@const fn pack4xU8(e: vec4<u32>) -> u32
+Pack the lower 8 bits of each component of e into a u32 value and drop all the unused bits.
+Component e[i] of the input is mapped to bits (8 * i) through (8 * (i + 7)) of the result.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { u32, toVector, Type } from '../../../../../util/conversion.js';
+import { Case } from '../../case.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('basic')
+ .specURL('https://www.w3.org/TR/WGSL/#pack4xU8-builtin')
+ .desc(
+ `
+@const fn pack4xU8(e: vec4<u32>) -> u32
+ `
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const pack4xU8 = (vals: readonly [number, number, number, number]) => {
+ const result = new Uint32Array(1);
+ for (let i = 0; i < 4; ++i) {
+ result[0] |= (vals[i] & 0xff) << (i * 8);
+ }
+ return result[0];
+ };
+
+ const testInputs = [
+ [0, 0, 0, 0],
+ [1, 2, 3, 4],
+ [255, 255, 255, 255],
+ [254, 255, 256, 257],
+ [65535, 65536, 255, 254],
+ ] as const;
+
+ const makeCase = (vals: readonly [number, number, number, number]): Case => {
+ return { input: [toVector(vals, u32)], expected: u32(pack4xU8(vals)) };
+ };
+ const cases: Array<Case> = testInputs.flatMap(v => {
+ return [makeCase(v)];
+ });
+
+ await run(t, builtin('pack4xU8'), [Type.vec4u], Type.u32, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts
new file mode 100644
index 0000000000..ffecf9b877
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts
@@ -0,0 +1,57 @@
+export const description = `
+Execution tests for the 'pack4xU8Clamp' builtin function
+
+@const fn pack4xU8Clamp(e: vec4<u32>) -> u32
+Clamp each component of e in the range of [0, 255] and then pack the lower 8 bits of each component
+into a u32 value. Component e[i] of the input is mapped to bits (8 * i) through (8 * (i + 7)) of the
+result.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { u32, toVector, Type } from '../../../../../util/conversion.js';
+import { clamp } from '../../../../../util/math.js';
+import { Case } from '../../case.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('basic')
+ .specURL('https://www.w3.org/TR/WGSL/#pack4xU8Clamp-builtin')
+ .desc(
+ `
+@const fn pack4xU8Clamp(e: vec4<u32>) -> u32
+ `
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const pack4xU8Clamp = (vals: readonly [number, number, number, number]) => {
+ const result = new Uint32Array(1);
+ for (let i = 0; i < 4; ++i) {
+ const clampedValue = clamp(vals[i], { min: 0, max: 255 });
+ result[0] |= clampedValue << (i * 8);
+ }
+ return result[0];
+ };
+
+ const testInputs = [
+ [0, 0, 0, 0],
+ [1, 2, 3, 4],
+ [255, 255, 255, 255],
+ [254, 255, 256, 257],
+ [65535, 65536, 255, 254],
+ ] as const;
+
+ const makeCase = (vals: readonly [number, number, number, number]): Case => {
+ return { input: [toVector(vals, u32)], expected: u32(pack4xU8Clamp(vals)) };
+ };
+ const cases: Array<Case> = testInputs.flatMap(v => {
+ return [makeCase(v)];
+ });
+
+ await run(t, builtin('pack4xU8Clamp'), [Type.vec4u], Type.u32, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.cache.ts
new file mode 100644
index 0000000000..54777e702f
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.cache.ts
@@ -0,0 +1,24 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarPairToIntervalCases(
+ FP[trait].scalarRange(),
+ FP[trait].scalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // pow has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].powInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('pow', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts
index f9b4fe1cfa..84e9649c96 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'pow' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn pow(e1: T ,e2: T ) -> T
Returns e1 raised to the power e2. Component-wise when T is a vector.
@@ -9,58 +9,33 @@ Returns e1 raised to the power e2. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './pow.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('pow', {
- f32_const: () => {
- return FP.f32.generateScalarPairToIntervalCases(
- fullF32Range(),
- fullF32Range(),
- 'finite',
- FP.f32.powInterval
- );
- },
- f32_non_const: () => {
- return FP.f32.generateScalarPairToIntervalCases(
- fullF32Range(),
- fullF32Range(),
- 'unfiltered',
- FP.f32.powInterval
- );
- },
- f16_const: () => {
- return FP.f16.generateScalarPairToIntervalCases(
- fullF16Range(),
- fullF16Range(),
- 'finite',
- FP.f16.powInterval
- );
- },
- f16_non_const: () => {
- return FP.f16.generateScalarPairToIntervalCases(
- fullF16Range(),
- fullF16Range(),
- 'unfiltered',
- FP.f16.powInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('pow'),
+ [Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -70,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('pow'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('pow'), [Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -84,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('pow'), [TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('pow'), [Type.f16, Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.cache.ts
new file mode 100644
index 0000000000..91aa845d29
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.cache.ts
@@ -0,0 +1,41 @@
+import { kValue } from '../../../../../util/constants.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { scalarF16Range, scalarF32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+export const d = makeCaseCache('quantizeToF16', {
+ f32_const: () => {
+ return FP.f32.generateScalarToIntervalCases(
+ [
+ kValue.f16.negative.min,
+ kValue.f16.negative.max,
+ kValue.f16.negative.subnormal.min,
+ kValue.f16.negative.subnormal.max,
+ kValue.f16.positive.subnormal.min,
+ kValue.f16.positive.subnormal.max,
+ kValue.f16.positive.min,
+ kValue.f16.positive.max,
+ ...scalarF16Range(),
+ ],
+ 'finite',
+ FP.f32.quantizeToF16Interval
+ );
+ },
+ f32_non_const: () => {
+ return FP.f32.generateScalarToIntervalCases(
+ [
+ kValue.f16.negative.min,
+ kValue.f16.negative.max,
+ kValue.f16.negative.subnormal.min,
+ kValue.f16.negative.subnormal.max,
+ kValue.f16.positive.subnormal.min,
+ kValue.f16.positive.subnormal.max,
+ kValue.f16.positive.min,
+ kValue.f16.positive.max,
+ ...scalarF32Range(),
+ ],
+ 'unfiltered',
+ FP.f32.quantizeToF16Interval
+ );
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts
index b37d4c5afb..0aa9669e93 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts
@@ -10,54 +10,14 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { kValue } from '../../../../../util/constants.js';
-import { TypeF32 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF16Range, fullF32Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
+import { d } from './quantizeToF16.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('quantizeToF16', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- kValue.f16.negative.min,
- kValue.f16.negative.max,
- kValue.f16.negative.subnormal.min,
- kValue.f16.negative.subnormal.max,
- kValue.f16.positive.subnormal.min,
- kValue.f16.positive.subnormal.max,
- kValue.f16.positive.min,
- kValue.f16.positive.max,
- ...fullF16Range(),
- ],
- 'finite',
- FP.f32.quantizeToF16Interval
- );
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- kValue.f16.negative.min,
- kValue.f16.negative.max,
- kValue.f16.negative.subnormal.min,
- kValue.f16.negative.subnormal.max,
- kValue.f16.positive.subnormal.min,
- kValue.f16.positive.subnormal.max,
- kValue.f16.positive.min,
- kValue.f16.positive.max,
- ...fullF32Range(),
- ],
- 'unfiltered',
- FP.f32.quantizeToF16Interval
- );
- },
-});
-
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`f32 tests`)
@@ -66,5 +26,5 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('quantizeToF16'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('quantizeToF16'), [Type.f32], Type.f32, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.cache.ts
new file mode 100644
index 0000000000..8ed0fbbd2b
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.cache.ts
@@ -0,0 +1,18 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ trait !== 'abstract' ? 'unfiltered' : 'finite',
+ // radians has an inherited accuracy, so abstract is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].radiansInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('radians', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts
index 63ae45b656..a405807ec0 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'radians' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn radians(e1: T ) -> T
Converts degrees to radians, approximating e1 * π / 180.
@@ -10,40 +10,14 @@ Component-wise when T is a vector
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF16Range, fullF32Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './radians.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('radians', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- fullF32Range(),
- 'unfiltered',
- FP.f32.radiansInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- fullF16Range(),
- 'unfiltered',
- FP.f16.radiansInterval
- );
- },
- abstract: () => {
- return FP.abstract.generateScalarToIntervalCases(
- fullF16Range(),
- 'unfiltered',
- FP.abstract.radiansInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
@@ -56,9 +30,9 @@ g.test('abstract_float')
const cases = await d.get('abstract');
await run(
t,
- abstractBuiltin('radians'),
- [TypeAbstractFloat],
- TypeAbstractFloat,
+ abstractFloatBuiltin('radians'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
t.params,
cases
);
@@ -72,7 +46,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('radians'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('radians'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -86,5 +60,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('radians'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('radians'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.cache.ts
new file mode 100644
index 0000000000..ca57226f3a
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.cache.ts
@@ -0,0 +1,26 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_vecN_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(dim =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateVectorPairToVectorCases(
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseVectorRange(dim),
+ nonConst ? 'unfiltered' : 'finite',
+ // reflect has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].reflectInterval
+ );
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('reflect', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts
index 2614c4e686..e36558d30c 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'reflect' builtin function
-T is vecN<AbstractFloat>, vecN<f32>, or vecN<f16>
+T is vecN<Type.abstractFloat>, vecN<f32>, or vecN<f16>
@const fn reflect(e1: T, e2: T ) -> T
For the incident vector e1 and surface orientation e2, returns the reflection
direction e1-2*dot(e2,e1)*e2.
@@ -9,58 +9,61 @@ direction e1-2*dot(e2,e1)*e2.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { sparseVectorF32Range, sparseVectorF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './reflect.cache.js';
export const g = makeTestGroup(GPUTest);
-// Cases: f32_vecN_[non_]const
-const f32_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f32_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f32.generateVectorPairToVectorCases(
- sparseVectorF32Range(n),
- sparseVectorF32Range(n),
- nonConst ? 'unfiltered' : 'finite',
- FP.f32.reflectInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: f16_vecN_[non_]const
-const f16_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f16_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f16.generateVectorPairToVectorCases(
- sparseVectorF16Range(n),
- sparseVectorF16Range(n),
- nonConst ? 'unfiltered' : 'finite',
- FP.f16.reflectInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
+g.test('abstract_float_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec2s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec2_const');
+ await run(
+ t,
+ abstractFloatBuiltin('reflect'),
+ [Type.vec2af, Type.vec2af],
+ Type.vec2af,
+ t.params,
+ cases
+ );
+ });
-export const d = makeCaseCache('reflect', {
- ...f32_vec_cases,
- ...f16_vec_cases,
-});
+g.test('abstract_float_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec3s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec3_const');
+ await run(
+ t,
+ abstractFloatBuiltin('reflect'),
+ [Type.vec3af, Type.vec3af],
+ Type.vec3af,
+ t.params,
+ cases
+ );
+ });
-g.test('abstract_float')
- .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
- .desc(`abstract float tests`)
- .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
- .unimplemented();
+g.test('abstract_float_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec4s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec4_const');
+ await run(
+ t,
+ abstractFloatBuiltin('reflect'),
+ [Type.vec4af, Type.vec4af],
+ Type.vec4af,
+ t.params,
+ cases
+ );
+ });
g.test('f32_vec2')
.specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
@@ -70,14 +73,7 @@ g.test('f32_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const'
);
- await run(
- t,
- builtin('reflect'),
- [TypeVec(2, TypeF32), TypeVec(2, TypeF32)],
- TypeVec(2, TypeF32),
- t.params,
- cases
- );
+ await run(t, builtin('reflect'), [Type.vec2f, Type.vec2f], Type.vec2f, t.params, cases);
});
g.test('f32_vec3')
@@ -88,14 +84,7 @@ g.test('f32_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const'
);
- await run(
- t,
- builtin('reflect'),
- [TypeVec(3, TypeF32), TypeVec(3, TypeF32)],
- TypeVec(3, TypeF32),
- t.params,
- cases
- );
+ await run(t, builtin('reflect'), [Type.vec3f, Type.vec3f], Type.vec3f, t.params, cases);
});
g.test('f32_vec4')
@@ -106,14 +95,7 @@ g.test('f32_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const'
);
- await run(
- t,
- builtin('reflect'),
- [TypeVec(4, TypeF32), TypeVec(4, TypeF32)],
- TypeVec(4, TypeF32),
- t.params,
- cases
- );
+ await run(t, builtin('reflect'), [Type.vec4f, Type.vec4f], Type.vec4f, t.params, cases);
});
g.test('f16_vec2')
@@ -127,14 +109,7 @@ g.test('f16_vec2')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const'
);
- await run(
- t,
- builtin('reflect'),
- [TypeVec(2, TypeF16), TypeVec(2, TypeF16)],
- TypeVec(2, TypeF16),
- t.params,
- cases
- );
+ await run(t, builtin('reflect'), [Type.vec2h, Type.vec2h], Type.vec2h, t.params, cases);
});
g.test('f16_vec3')
@@ -148,14 +123,7 @@ g.test('f16_vec3')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const'
);
- await run(
- t,
- builtin('reflect'),
- [TypeVec(3, TypeF16), TypeVec(3, TypeF16)],
- TypeVec(3, TypeF16),
- t.params,
- cases
- );
+ await run(t, builtin('reflect'), [Type.vec3h, Type.vec3h], Type.vec3h, t.params, cases);
});
g.test('f16_vec4')
@@ -169,12 +137,5 @@ g.test('f16_vec4')
const cases = await d.get(
t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const'
);
- await run(
- t,
- builtin('reflect'),
- [TypeVec(4, TypeF16), TypeVec(4, TypeF16)],
- TypeVec(4, TypeF16),
- t.params,
- cases
- );
+ await run(t, builtin('reflect'), [Type.vec4h, Type.vec4h], Type.vec4h, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.cache.ts
new file mode 100644
index 0000000000..a759f5b669
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.cache.ts
@@ -0,0 +1,116 @@
+import { ROArrayArray } from '../../../../../../common/util/types.js';
+import { toVector } from '../../../../../util/conversion.js';
+import { FP, FPKind } from '../../../../../util/floating_point.js';
+import { Case, selectNCases } from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+import { IntervalFilter } from '../../interval_filter.js';
+
+// Using a bespoke implementation of make*Case and generate*Cases here
+// since refract is the only builtin with the API signature
+// (vec, vec, scalar) -> vec
+
+/**
+ * @returns a Case for `refract`
+ * @param argumentKind what kind of floating point numbers being operated on
+ * @param parameterKind what kind of floating point operation should be performed,
+ * should be the same as argumentKind, except for abstract
+ * @param i the `i` param for the case
+ * @param s the `s` param for the case
+ * @param r the `r` param for the case
+ * @param check what interval checking to apply
+ * */
+function makeCase(
+ argumentKind: FPKind,
+ parameterKind: FPKind,
+ i: readonly number[],
+ s: readonly number[],
+ r: number,
+ check: IntervalFilter
+): Case | undefined {
+ const fp = FP[argumentKind];
+ i = i.map(fp.quantize);
+ s = s.map(fp.quantize);
+ r = fp.quantize(r);
+
+ const vectors = FP[parameterKind].refractInterval(i, s, r);
+ if (check === 'finite' && vectors.some(e => !e.isFinite())) {
+ return undefined;
+ }
+
+ return {
+ input: [toVector(i, fp.scalarBuilder), toVector(s, fp.scalarBuilder), fp.scalarBuilder(r)],
+ expected: vectors,
+ };
+}
+
+/**
+ * @returns an array of Cases for `refract`
+ * @param argumentKind what kind of floating point numbers being operated on
+ * @param parameterKind what kind of floating point operation should be performed,
+ * should be the same as argumentKind, except for abstract
+ * @param param_is array of inputs to try for the `i` param
+ * @param param_ss array of inputs to try for the `s` param
+ * @param param_rs array of inputs to try for the `r` param
+ * @param check what interval checking to apply
+ */
+function generateCases(
+ argumentKind: FPKind,
+ parameterKind: FPKind,
+ param_is: ROArrayArray<number>,
+ param_ss: ROArrayArray<number>,
+ param_rs: readonly number[],
+ check: IntervalFilter
+): Case[] {
+ // Cannot use `cartesianProduct` here due to heterogeneous param types
+ return param_is
+ .flatMap(i => {
+ return param_ss.flatMap(s => {
+ return param_rs.map(r => {
+ return makeCase(argumentKind, parameterKind, i, s, r, check);
+ });
+ });
+ })
+ .filter((c): c is Case => c !== undefined);
+}
+
+// Cases: [f32|f16|abstract]_vecN_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(dim =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ if (trait !== 'abstract') {
+ return generateCases(
+ trait,
+ trait,
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseScalarRange(),
+ nonConst ? 'unfiltered' : 'finite'
+ );
+ } else {
+ // Restricting the number of cases, because a vector of abstract floats needs to be returned, which is costly.
+ return selectNCases(
+ 'faceForward',
+ 20,
+ generateCases(
+ trait,
+ // refract has an inherited accuracy, so is only expected to be as accurate as f32
+ 'f32',
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseVectorRange(dim),
+ FP[trait].sparseScalarRange(),
+ nonConst ? 'unfiltered' : 'finite'
+ )
+ );
+ }
+ },
+ }))
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('refract', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts
index be1a76b437..5b51f30eee 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts
@@ -2,7 +2,7 @@ export const description = `
Execution tests for the 'refract' builtin function
T is vecN<I>
-I is AbstractFloat, f32, or f16
+I is abstract-float, f32, or f16
@const fn refract(e1: T ,e2: T ,e3: I ) -> T
For the incident vector e1 and surface normal e2, and the ratio of indices of
refraction e3, let k = 1.0 -e3*e3* (1.0 - dot(e2,e1) * dot(e2,e1)).
@@ -11,129 +11,62 @@ vector e3*e1- (e3* dot(e2,e1) + sqrt(k)) *e2.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
-import { ROArrayArray } from '../../../../../../common/util/types.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { toVector, TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js';
-import { FP, FPKind } from '../../../../../util/floating_point.js';
-import {
- sparseVectorF32Range,
- sparseVectorF16Range,
- sparseF32Range,
- sparseF16Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, Case, IntervalFilter, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './refract.cache.js';
export const g = makeTestGroup(GPUTest);
-// Using a bespoke implementation of make*Case and generate*Cases here
-// since refract is the only builtin with the API signature
-// (vec, vec, scalar) -> vec
-
-/**
- * @returns a Case for `refract`
- * @param kind what type of floating point numbers to operate on
- * @param i the `i` param for the case
- * @param s the `s` param for the case
- * @param r the `r` param for the case
- * @param check what interval checking to apply
- * */
-function makeCase(
- kind: FPKind,
- i: readonly number[],
- s: readonly number[],
- r: number,
- check: IntervalFilter
-): Case | undefined {
- const fp = FP[kind];
- i = i.map(fp.quantize);
- s = s.map(fp.quantize);
- r = fp.quantize(r);
-
- const vectors = fp.refractInterval(i, s, r);
- if (check === 'finite' && vectors.some(e => !e.isFinite())) {
- return undefined;
- }
-
- return {
- input: [toVector(i, fp.scalarBuilder), toVector(s, fp.scalarBuilder), fp.scalarBuilder(r)],
- expected: fp.refractInterval(i, s, r),
- };
-}
-
-/**
- * @returns an array of Cases for `refract`
- * @param kind what type of floating point numbers to operate on
- * @param param_is array of inputs to try for the `i` param
- * @param param_ss array of inputs to try for the `s` param
- * @param param_rs array of inputs to try for the `r` param
- * @param check what interval checking to apply
- */
-function generateCases(
- kind: FPKind,
- param_is: ROArrayArray<number>,
- param_ss: ROArrayArray<number>,
- param_rs: readonly number[],
- check: IntervalFilter
-): Case[] {
- // Cannot use `cartesianProduct` here due to heterogeneous param types
- return param_is
- .flatMap(i => {
- return param_ss.flatMap(s => {
- return param_rs.map(r => {
- return makeCase(kind, i, s, r, check);
- });
- });
- })
- .filter((c): c is Case => c !== undefined);
-}
-
-// Cases: f32_vecN_[non_]const
-const f32_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f32_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return generateCases(
- 'f32',
- sparseVectorF32Range(n),
- sparseVectorF32Range(n),
- sparseF32Range(),
- nonConst ? 'unfiltered' : 'finite'
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: f16_vecN_[non_]const
-const f16_vec_cases = ([2, 3, 4] as const)
- .flatMap(n =>
- ([true, false] as const).map(nonConst => ({
- [`f16_vec${n}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return generateCases(
- 'f16',
- sparseVectorF16Range(n),
- sparseVectorF16Range(n),
- sparseF16Range(),
- nonConst ? 'unfiltered' : 'finite'
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
+g.test('abstract_float_vec2')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec2s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec2_const');
+ await run(
+ t,
+ abstractFloatBuiltin('refract'),
+ [Type.vec2af, Type.vec2af, Type.abstractFloat],
+ Type.vec2af,
+ t.params,
+ cases
+ );
+ });
-export const d = makeCaseCache('refract', {
- ...f32_vec_cases,
- ...f16_vec_cases,
-});
+g.test('abstract_float_vec3')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec3s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec3_const');
+ await run(
+ t,
+ abstractFloatBuiltin('refract'),
+ [Type.vec3af, Type.vec3af, Type.abstractFloat],
+ Type.vec3af,
+ t.params,
+ cases
+ );
+ });
-g.test('abstract_float')
- .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
- .desc(`abstract float tests`)
- .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const))
- .unimplemented();
+g.test('abstract_float_vec4')
+ .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
+ .desc(`abstract float tests using vec4s`)
+ .params(u => u.combine('inputSource', onlyConstInputSource))
+ .fn(async t => {
+ const cases = await d.get('abstract_vec4_const');
+ await run(
+ t,
+ abstractFloatBuiltin('refract'),
+ [Type.vec4af, Type.vec4af, Type.abstractFloat],
+ Type.vec4af,
+ t.params,
+ cases
+ );
+ });
g.test('f32_vec2')
.specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions')
@@ -146,8 +79,8 @@ g.test('f32_vec2')
await run(
t,
builtin('refract'),
- [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeF32],
- TypeVec(2, TypeF32),
+ [Type.vec2f, Type.vec2f, Type.f32],
+ Type.vec2f,
t.params,
cases
);
@@ -164,8 +97,8 @@ g.test('f32_vec3')
await run(
t,
builtin('refract'),
- [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeF32],
- TypeVec(3, TypeF32),
+ [Type.vec3f, Type.vec3f, Type.f32],
+ Type.vec3f,
t.params,
cases
);
@@ -182,8 +115,8 @@ g.test('f32_vec4')
await run(
t,
builtin('refract'),
- [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeF32],
- TypeVec(4, TypeF32),
+ [Type.vec4f, Type.vec4f, Type.f32],
+ Type.vec4f,
t.params,
cases
);
@@ -203,8 +136,8 @@ g.test('f16_vec2')
await run(
t,
builtin('refract'),
- [TypeVec(2, TypeF16), TypeVec(2, TypeF16), TypeF16],
- TypeVec(2, TypeF16),
+ [Type.vec2h, Type.vec2h, Type.f16],
+ Type.vec2h,
t.params,
cases
);
@@ -224,8 +157,8 @@ g.test('f16_vec3')
await run(
t,
builtin('refract'),
- [TypeVec(3, TypeF16), TypeVec(3, TypeF16), TypeF16],
- TypeVec(3, TypeF16),
+ [Type.vec3h, Type.vec3h, Type.f16],
+ Type.vec3h,
t.params,
cases
);
@@ -245,8 +178,8 @@ g.test('f16_vec4')
await run(
t,
builtin('refract'),
- [TypeVec(4, TypeF16), TypeVec(4, TypeF16), TypeF16],
- TypeVec(4, TypeF16),
+ [Type.vec4h, Type.vec4h, Type.f16],
+ Type.vec4h,
t.params,
cases
);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts
index 6acb359822..e235e62a52 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts
@@ -10,7 +10,7 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeU32, u32Bits, TypeI32, i32Bits } from '../../../../../util/conversion.js';
+import { u32Bits, i32Bits, Type } from '../../../../../util/conversion.js';
import { allInputSources, Config, run } from '../../expression.js';
import { builtin } from './builtin.js';
@@ -26,7 +26,7 @@ g.test('u32')
.fn(async t => {
const cfg: Config = t.params;
// prettier-ignore
- await run(t, builtin('reverseBits'), [TypeU32], TypeU32, cfg, [
+ await run(t, builtin('reverseBits'), [Type.u32], Type.u32, cfg, [
// Zero
{ input: u32Bits(0b00000000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000000) },
@@ -142,7 +142,7 @@ g.test('i32')
.fn(async t => {
const cfg: Config = t.params;
// prettier-ignore
- await run(t, builtin('reverseBits'), [TypeI32], TypeI32, cfg, [
+ await run(t, builtin('reverseBits'), [Type.i32], Type.i32, cfg, [
// Zero
{ input: i32Bits(0b00000000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000000) },
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.cache.ts
new file mode 100644
index 0000000000..e5383b2075
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.cache.ts
@@ -0,0 +1,24 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// See https://github.com/gpuweb/cts/issues/2766 for details
+const kIssue2766Value = {
+ abstract: 0x8000_0000_0000_0000,
+ f32: 0x8000_0000,
+ f16: 0x8000,
+};
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ [kIssue2766Value[trait], ...FP[trait].scalarRange()],
+ 'unfiltered',
+ FP[trait].roundInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('round', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts
index bd40ed4b2a..eeaf41b381 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'round' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn round(e: T) -> T
Result is the integer k nearest to e, as a floating point value.
@@ -12,46 +12,33 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './round.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('round', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- 0x80000000, // https://github.com/gpuweb/cts/issues/2766,
- ...fullF32Range(),
- ],
- 'unfiltered',
- FP.f32.roundInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- [
- 0x8000, // https://github.com/gpuweb/cts/issues/2766
- ...fullF16Range(),
- ],
- 'unfiltered',
- FP.f16.roundInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('round'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -61,7 +48,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('round'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('round'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -75,5 +62,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('round'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('round'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.cache.ts
new file mode 100644
index 0000000000..4a4ffeee30
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.cache.ts
@@ -0,0 +1,18 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ [...linearRange(0.0, 1.0, 20), ...FP[trait].scalarRange()],
+ 'unfiltered',
+ FP[trait].saturateInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('saturate', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts
index 2f16502921..79c61e4eec 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'saturate' builtin function
-S is AbstractFloat, f32, or f16
+S is abstract-float, f32, or f16
T is S or vecN<S>
@const fn saturate(e: T) -> T
Returns clamp(e, 0.0, 1.0). Component-wise when T is a vector.
@@ -9,52 +9,14 @@ Returns clamp(e, 0.0, 1.0). Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF16Range, fullF32Range, fullF64Range, linearRange } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './saturate.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('saturate', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- // Non-clamped values
- ...linearRange(0.0, 1.0, 20),
- ...fullF32Range(),
- ],
- 'unfiltered',
- FP.f32.saturateInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- [
- // Non-clamped values
- ...linearRange(0.0, 1.0, 20),
- ...fullF16Range(),
- ],
- 'unfiltered',
- FP.f16.saturateInterval
- );
- },
- abstract: () => {
- return FP.abstract.generateScalarToIntervalCases(
- [
- // Non-clamped values
- ...linearRange(0.0, 1.0, 20),
- ...fullF64Range(),
- ],
- 'unfiltered',
- FP.abstract.saturateInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
@@ -67,9 +29,9 @@ g.test('abstract_float')
const cases = await d.get('abstract');
await run(
t,
- abstractBuiltin('saturate'),
- [TypeAbstractFloat],
- TypeAbstractFloat,
+ abstractFloatBuiltin('saturate'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
t.params,
cases
);
@@ -82,7 +44,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('saturate'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('saturate'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -96,5 +58,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('saturate'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('saturate'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts
index c64f989f42..63accbc2d4 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts
@@ -14,12 +14,6 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js'
import { GPUTest } from '../../../../../gpu_test.js';
import {
VectorType,
- TypeVec,
- TypeBool,
- TypeF32,
- TypeF16,
- TypeI32,
- TypeU32,
f32,
f16,
i32,
@@ -31,11 +25,14 @@ import {
vec3,
vec4,
abstractFloat,
- TypeAbstractFloat,
+ abstractInt,
+ ScalarValue,
+ Type,
} from '../../../../../util/conversion.js';
-import { run, CaseList, allInputSources } from '../../expression.js';
+import { Case } from '../../case.js';
+import { run, allInputSources } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js';
export const g = makeTestGroup(GPUTest);
@@ -43,32 +40,47 @@ function makeBool(n: number) {
return bool((n & 1) === 1);
}
-type scalarKind = 'b' | 'af' | 'f' | 'h' | 'i' | 'u';
+type scalarKind = 'b' | 'af' | 'f' | 'h' | 'ai' | 'i' | 'u';
const dataType = {
b: {
- type: TypeBool,
- constructor: makeBool,
+ type: Type.bool,
+ scalar_builder: makeBool,
+ shader_builder: builtin('select'),
},
af: {
- type: TypeAbstractFloat,
- constructor: abstractFloat,
+ type: Type.abstractFloat,
+ scalar_builder: abstractFloat,
+ shader_builder: abstractFloatBuiltin('select'),
},
f: {
- type: TypeF32,
- constructor: f32,
+ type: Type.f32,
+ scalar_builder: f32,
+ shader_builder: builtin('select'),
},
h: {
- type: TypeF16,
- constructor: f16,
+ type: Type.f16,
+ scalar_builder: f16,
+ shader_builder: builtin('select'),
+ },
+ ai: {
+ type: Type.abstractInt,
+ // Only ints are used in the tests below, so the conversion to bigint will
+ // be safe. If a non-int is passed in this will Error.
+ scalar_builder: (v: number): ScalarValue => {
+ return abstractInt(BigInt(v));
+ },
+ shader_builder: abstractIntBuiltin('select'),
},
i: {
- type: TypeI32,
- constructor: i32,
+ type: Type.i32,
+ scalar_builder: i32,
+ shader_builder: builtin('select'),
},
u: {
- type: TypeU32,
- constructor: u32,
+ type: Type.u32,
+ scalar_builder: u32,
+ shader_builder: builtin('select'),
},
};
@@ -78,7 +90,7 @@ g.test('scalar')
.params(u =>
u
.combine('inputSource', allInputSources)
- .combine('component', ['b', 'af', 'f', 'h', 'i', 'u'] as const)
+ .combine('component', ['b', 'af', 'f', 'h', 'ai', 'i', 'u'] as const)
.combine('overload', ['scalar', 'vec2', 'vec3', 'vec4'] as const)
)
.beforeAllSubcases(t => {
@@ -86,10 +98,11 @@ g.test('scalar')
t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] });
}
t.skipIf(t.params.component === 'af' && t.params.inputSource !== 'const');
+ t.skipIf(t.params.component === 'ai' && t.params.inputSource !== 'const');
})
.fn(async t => {
- const componentType = dataType[t.params.component as scalarKind].type;
- const cons = dataType[t.params.component as scalarKind].constructor;
+ const componentType = dataType[t.params.component].type;
+ const scalar_builder = dataType[t.params.component].scalar_builder;
// Create the scalar values that will be selected from, either as scalars
// or vectors.
@@ -97,39 +110,40 @@ g.test('scalar')
// Each boolean will select between c[k] and c[k+4]. Those values must
// always compare as different. The tricky case is boolean, where the parity
// has to be different, i.e. c[k]-c[k+4] must be odd.
- const c = [0, 1, 2, 3, 5, 6, 7, 8].map(i => cons(i));
+ const scalars = [0, 1, 2, 3, 5, 6, 7, 8].map(i => scalar_builder(i));
+
// Now form vectors that will have different components from each other.
- const v2a = vec2(c[0], c[1]);
- const v2b = vec2(c[4], c[5]);
- const v3a = vec3(c[0], c[1], c[2]);
- const v3b = vec3(c[4], c[5], c[6]);
- const v4a = vec4(c[0], c[1], c[2], c[3]);
- const v4b = vec4(c[4], c[5], c[6], c[7]);
+ const v2a = vec2(scalars[0], scalars[1]);
+ const v2b = vec2(scalars[4], scalars[5]);
+ const v3a = vec3(scalars[0], scalars[1], scalars[2]);
+ const v3b = vec3(scalars[4], scalars[5], scalars[6]);
+ const v4a = vec4(scalars[0], scalars[1], scalars[2], scalars[3]);
+ const v4b = vec4(scalars[4], scalars[5], scalars[6], scalars[7]);
const overloads = {
scalar: {
type: componentType,
cases: [
- { input: [c[0], c[1], False], expected: c[0] },
- { input: [c[0], c[1], True], expected: c[1] },
+ { input: [scalars[0], scalars[1], False], expected: scalars[0] },
+ { input: [scalars[0], scalars[1], True], expected: scalars[1] },
],
},
vec2: {
- type: TypeVec(2, componentType),
+ type: Type.vec(2, componentType),
cases: [
{ input: [v2a, v2b, False], expected: v2a },
{ input: [v2a, v2b, True], expected: v2b },
],
},
vec3: {
- type: TypeVec(3, componentType),
+ type: Type.vec(3, componentType),
cases: [
{ input: [v3a, v3b, False], expected: v3a },
{ input: [v3a, v3b, True], expected: v3b },
],
},
vec4: {
- type: TypeVec(4, componentType),
+ type: Type.vec(4, componentType),
cases: [
{ input: [v4a, v4b, False], expected: v4a },
{ input: [v4a, v4b, True], expected: v4b },
@@ -140,8 +154,8 @@ g.test('scalar')
await run(
t,
- t.params.component === 'af' ? abstractBuiltin('select') : builtin('select'),
- [overload.type, overload.type, TypeBool],
+ dataType[t.params.component as scalarKind].shader_builder,
+ [overload.type, overload.type, Type.bool],
overload.type,
t.params,
overload.cases
@@ -154,7 +168,7 @@ g.test('vector')
.params(u =>
u
.combine('inputSource', allInputSources)
- .combine('component', ['b', 'af', 'f', 'h', 'i', 'u'] as const)
+ .combine('component', ['b', 'af', 'f', 'h', 'ai', 'i', 'u'] as const)
.combine('overload', ['vec2', 'vec3', 'vec4'] as const)
)
.beforeAllSubcases(t => {
@@ -162,29 +176,30 @@ g.test('vector')
t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] });
}
t.skipIf(t.params.component === 'af' && t.params.inputSource !== 'const');
+ t.skipIf(t.params.component === 'ai' && t.params.inputSource !== 'const');
})
.fn(async t => {
- const componentType = dataType[t.params.component as scalarKind].type;
- const cons = dataType[t.params.component as scalarKind].constructor;
+ const componentType = dataType[t.params.component].type;
+ const scalar_builder = dataType[t.params.component].scalar_builder;
// Create the scalar values that will be selected from.
//
// Each boolean will select between c[k] and c[k+4]. Those values must
// always compare as different. The tricky case is boolean, where the parity
// has to be different, i.e. c[k]-c[k+4] must be odd.
- const c = [0, 1, 2, 3, 5, 6, 7, 8].map(i => cons(i));
+ const scalars = [0, 1, 2, 3, 5, 6, 7, 8].map(i => scalar_builder(i));
const T = True;
const F = False;
- let tests: { dataType: VectorType; boolType: VectorType; cases: CaseList };
+ let tests: { dataType: VectorType; boolType: VectorType; cases: Case[] };
switch (t.params.overload) {
case 'vec2': {
- const a = vec2(c[0], c[1]);
- const b = vec2(c[4], c[5]);
+ const a = vec2(scalars[0], scalars[1]);
+ const b = vec2(scalars[4], scalars[5]);
tests = {
- dataType: TypeVec(2, componentType),
- boolType: TypeVec(2, TypeBool),
+ dataType: Type.vec(2, componentType),
+ boolType: Type.vec(2, Type.bool),
cases: [
{ input: [a, b, vec2(F, F)], expected: vec2(a.x, a.y) },
{ input: [a, b, vec2(F, T)], expected: vec2(a.x, b.y) },
@@ -195,11 +210,11 @@ g.test('vector')
break;
}
case 'vec3': {
- const a = vec3(c[0], c[1], c[2]);
- const b = vec3(c[4], c[5], c[6]);
+ const a = vec3(scalars[0], scalars[1], scalars[2]);
+ const b = vec3(scalars[4], scalars[5], scalars[6]);
tests = {
- dataType: TypeVec(3, componentType),
- boolType: TypeVec(3, TypeBool),
+ dataType: Type.vec(3, componentType),
+ boolType: Type.vec(3, Type.bool),
cases: [
{ input: [a, b, vec3(F, F, F)], expected: vec3(a.x, a.y, a.z) },
{ input: [a, b, vec3(F, F, T)], expected: vec3(a.x, a.y, b.z) },
@@ -214,11 +229,11 @@ g.test('vector')
break;
}
case 'vec4': {
- const a = vec4(c[0], c[1], c[2], c[3]);
- const b = vec4(c[4], c[5], c[6], c[7]);
+ const a = vec4(scalars[0], scalars[1], scalars[2], scalars[3]);
+ const b = vec4(scalars[4], scalars[5], scalars[6], scalars[7]);
tests = {
- dataType: TypeVec(4, componentType),
- boolType: TypeVec(4, TypeBool),
+ dataType: Type.vec(4, componentType),
+ boolType: Type.vec(4, Type.bool),
cases: [
{ input: [a, b, vec4(F, F, F, F)], expected: vec4(a.x, a.y, a.z, a.w) },
{ input: [a, b, vec4(F, F, F, T)], expected: vec4(a.x, a.y, a.z, b.w) },
@@ -244,7 +259,7 @@ g.test('vector')
await run(
t,
- t.params.component === 'af' ? abstractBuiltin('select') : builtin('select'),
+ dataType[t.params.component].shader_builder,
[tests.dataType, tests.dataType, tests.boolType],
tests.dataType,
t.params,
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.cache.ts
new file mode 100644
index 0000000000..09f4de19ac
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.cache.ts
@@ -0,0 +1,31 @@
+import { abstractInt, i32 } from '../../../../../util/conversion.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { fullI32Range, fullI64Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const fp_cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait === 'abstract' ? 'abstract_float' : trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ 'unfiltered',
+ FP[trait].signInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('sign', {
+ ...fp_cases,
+ i32: () =>
+ fullI32Range().map(i => {
+ const signFunc = (i: number): number => (i < 0 ? -1 : i > 0 ? 1 : 0);
+ return { input: [i32(i)], expected: i32(signFunc(i)) };
+ }),
+ abstract_int: () =>
+ fullI64Range().map(i => {
+ const signFunc = (i: bigint): bigint => (i < 0n ? -1n : i > 0n ? 1n : 0n);
+ return { input: [abstractInt(i)], expected: abstractInt(signFunc(i)) };
+ }),
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts
index a147acf6fb..f7d2e3ccfd 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'sign' builtin function
-S is AbstractFloat, AbstractInt, i32, f32, f16
+S is abstract-float, Type.abstractInt, i32, f32, f16
T is S or vecN<S>
@const fn sign(e: T ) -> T
Returns the sign of e. Component-wise when T is a vector.
@@ -9,48 +9,14 @@ Returns the sign of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import {
- i32,
- TypeF32,
- TypeF16,
- TypeI32,
- TypeAbstractFloat,
-} from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import {
- fullF32Range,
- fullF16Range,
- fullI32Range,
- fullF64Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js';
+import { d } from './sign.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('sign', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'unfiltered', FP.f32.signInterval);
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'unfiltered', FP.f16.signInterval);
- },
- abstract_float: () => {
- return FP.abstract.generateScalarToIntervalCases(
- fullF64Range(),
- 'unfiltered',
- FP.abstract.signInterval
- );
- },
- i32: () =>
- fullI32Range().map(i => {
- const signFunc = (i: number): number => (i < 0 ? -1 : i > 0 ? 1 : 0);
- return { input: [i32(i)], expected: i32(signFunc(i)) };
- }),
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#sign-builtin')
.desc(`abstract float tests`)
@@ -61,16 +27,28 @@ g.test('abstract_float')
)
.fn(async t => {
const cases = await d.get('abstract_float');
- await run(t, abstractBuiltin('sign'), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases);
+ await run(
+ t,
+ abstractFloatBuiltin('sign'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
});
g.test('abstract_int')
.specURL('https://www.w3.org/TR/WGSL/#sign-builtin')
.desc(`abstract int tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_int');
+ await run(t, abstractIntBuiltin('sign'), [Type.abstractInt], Type.abstractInt, t.params, cases);
+ });
g.test('i32')
.specURL('https://www.w3.org/TR/WGSL/#sign-builtin')
@@ -80,7 +58,7 @@ g.test('i32')
)
.fn(async t => {
const cases = await d.get('i32');
- await run(t, builtin('sign'), [TypeI32], TypeI32, t.params, cases);
+ await run(t, builtin('sign'), [Type.i32], Type.i32, t.params, cases);
});
g.test('f32')
@@ -91,7 +69,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('sign'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('sign'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -105,5 +83,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('sign'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('sign'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.cache.ts
new file mode 100644
index 0000000000..74acf6a62c
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.cache.ts
@@ -0,0 +1,23 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ [
+ // Well-defined accuracy range
+ ...linearRange(-Math.PI, Math.PI, 100),
+ ...FP[trait].scalarRange(),
+ ],
+ trait === 'abstract' ? 'finite' : 'unfiltered',
+ // sin has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].sinInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('sin', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts
index 4ab3ae7a3d..fc706c5d63 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'sin' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn sin(e: T ) -> T
Returns the sine of e. Component-wise when T is a vector.
@@ -9,48 +9,33 @@ Returns the sine of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range, linearRange } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './sin.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('sin', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- // Well-defined accuracy range
- ...linearRange(-Math.PI, Math.PI, 1000),
- ...fullF32Range(),
- ],
- 'unfiltered',
- FP.f32.sinInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- [
- // Well-defined accuracy range
- ...linearRange(-Math.PI, Math.PI, 1000),
- ...fullF16Range(),
- ],
- 'unfiltered',
- FP.f16.sinInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('sin'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -66,7 +51,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1]
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('sin'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('sin'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -80,5 +65,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('sin'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('sin'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.cache.ts
new file mode 100644
index 0000000000..aba275581d
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.cache.ts
@@ -0,0 +1,23 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // sinh has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].sinhInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('sinh', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts
index d9b93a3dc8..5ffe628de3 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'sinh' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn sinh(e: T ) -> T
Returns the hyperbolic sine of e. Component-wise when T is a vector.
@@ -9,38 +9,33 @@ Returns the hyperbolic sine of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './sinh.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('sinh', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'finite', FP.f32.sinhInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'unfiltered', FP.f32.sinhInterval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'finite', FP.f16.sinhInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'unfiltered', FP.f16.sinhInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('sinh'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -50,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('sinh'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('sinh'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -64,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('sinh'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('sinh'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.cache.ts
new file mode 100644
index 0000000000..4ba7615b43
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.cache.ts
@@ -0,0 +1,25 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarTripleToIntervalCases(
+ FP[trait].sparseScalarRange(),
+ FP[trait].sparseScalarRange(),
+ FP[trait].sparseScalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // smoothstep has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].smoothStepInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('smoothstep', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts
index 20d2a4edbc..42d8d09ff5 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'smoothstep' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn smoothstep(low: T , high: T , x: T ) -> T
Returns the smooth Hermite interpolation between 0 and 1.
@@ -11,62 +11,33 @@ For scalar T, the result is t * t * (3.0 - 2.0 * t), where t = clamp((x - low) /
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { sparseF32Range, sparseF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './smoothstep.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('smoothstep', {
- f32_const: () => {
- return FP.f32.generateScalarTripleToIntervalCases(
- sparseF32Range(),
- sparseF32Range(),
- sparseF32Range(),
- 'finite',
- FP.f32.smoothStepInterval
- );
- },
- f32_non_const: () => {
- return FP.f32.generateScalarTripleToIntervalCases(
- sparseF32Range(),
- sparseF32Range(),
- sparseF32Range(),
- 'unfiltered',
- FP.f32.smoothStepInterval
- );
- },
- f16_const: () => {
- return FP.f16.generateScalarTripleToIntervalCases(
- sparseF16Range(),
- sparseF16Range(),
- sparseF16Range(),
- 'finite',
- FP.f16.smoothStepInterval
- );
- },
- f16_non_const: () => {
- return FP.f16.generateScalarTripleToIntervalCases(
- sparseF16Range(),
- sparseF16Range(),
- sparseF16Range(),
- 'unfiltered',
- FP.f16.smoothStepInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('smoothstep'),
+ [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -76,7 +47,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('smoothstep'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('smoothstep'), [Type.f32, Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -90,5 +61,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('smoothstep'), [TypeF16, TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('smoothstep'), [Type.f16, Type.f16, Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.cache.ts
new file mode 100644
index 0000000000..2151b2730a
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.cache.ts
@@ -0,0 +1,23 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16]_[non_]const
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ nonConst ? 'unfiltered' : 'finite',
+ // sqrt has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].sqrtInterval
+ );
+ },
+ }))
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('sqrt', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts
index a092438043..3d6c4390e2 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'sqrt' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn sqrt(e: T ) -> T
Returns the square root of e. Component-wise when T is a vector.
@@ -9,38 +9,33 @@ Returns the square root of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './sqrt.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('sqrt', {
- f32_const: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'finite', FP.f32.sqrtInterval);
- },
- f32_non_const: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'unfiltered', FP.f32.sqrtInterval);
- },
- f16_const: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'finite', FP.f16.sqrtInterval);
- },
- f16_non_const: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'unfiltered', FP.f16.sqrtInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract_const');
+ await run(
+ t,
+ abstractFloatBuiltin('sqrt'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -50,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const');
- await run(t, builtin('sqrt'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('sqrt'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -64,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const');
- await run(t, builtin('sqrt'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('sqrt'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.cache.ts
new file mode 100644
index 0000000000..28d1e1952b
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.cache.ts
@@ -0,0 +1,41 @@
+import { anyOf } from '../../../../../util/compare.js';
+import { FP } from '../../../../../util/floating_point.js';
+import { Case } from '../../case.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// stepInterval's return value can't always be interpreted as a single acceptance
+// interval, valid result may be 0.0 or 1.0 or both of them, but will never be a
+// value in interval (0.0, 1.0).
+// See the comment block on stepInterval for more details
+const makeCase = (trait: 'f32' | 'f16' | 'abstract', edge: number, x: number): Case => {
+ const FPTrait = FP[trait];
+ edge = FPTrait.quantize(edge);
+ x = FPTrait.quantize(x);
+ const expected = FPTrait.stepInterval(edge, x);
+
+ // [0, 0], [1, 1], or [-∞, +∞] cases
+ if (expected.isPoint() || !expected.isFinite()) {
+ return { input: [FPTrait.scalarBuilder(edge), FPTrait.scalarBuilder(x)], expected };
+ }
+
+ // [0, 1] case, valid result is either 0.0 or 1.0.
+ const zeroInterval = FPTrait.toInterval(0);
+ const oneInterval = FPTrait.toInterval(1);
+ return {
+ input: [FPTrait.scalarBuilder(edge), FPTrait.scalarBuilder(x)],
+ expected: anyOf(zeroInterval, oneInterval),
+ };
+};
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait]
+ .sparseScalarRange()
+ .flatMap(edge => FP[trait].sparseScalarRange().map(x => makeCase(trait, edge, x)));
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('step', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts
index 752e2676e6..fd76ee16c1 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'step' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn step(edge: T ,x: T ) -> T
Returns 1.0 if edge ≤ x, and 0.0 otherwise. Component-wise when T is a vector.
@@ -9,57 +9,33 @@ Returns 1.0 if edge ≤ x, and 0.0 otherwise. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { anyOf } from '../../../../../util/compare.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, Case, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './step.cache.js';
export const g = makeTestGroup(GPUTest);
-// stepInterval's return value can't always be interpreted as a single acceptance
-// interval, valid result may be 0.0 or 1.0 or both of them, but will never be a
-// value in interval (0.0, 1.0).
-// See the comment block on stepInterval for more details
-const makeCase = (trait: 'f32' | 'f16', edge: number, x: number): Case => {
- const FPTrait = FP[trait];
- edge = FPTrait.quantize(edge);
- x = FPTrait.quantize(x);
- const expected = FPTrait.stepInterval(edge, x);
-
- // [0, 0], [1, 1], or [-∞, +∞] cases
- if (expected.isPoint() || !expected.isFinite()) {
- return { input: [FPTrait.scalarBuilder(edge), FPTrait.scalarBuilder(x)], expected };
- }
-
- // [0, 1] case, valid result is either 0.0 or 1.0.
- const zeroInterval = FPTrait.toInterval(0);
- const oneInterval = FPTrait.toInterval(1);
- return {
- input: [FPTrait.scalarBuilder(edge), FPTrait.scalarBuilder(x)],
- expected: anyOf(zeroInterval, oneInterval),
- };
-};
-
-export const d = makeCaseCache('step', {
- f32: () => {
- return fullF32Range().flatMap(edge => fullF32Range().map(x => makeCase('f32', edge, x)));
- },
- f16: () => {
- return fullF16Range().flatMap(edge => fullF16Range().map(x => makeCase('f16', edge, x)));
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('step'),
+ [Type.abstractFloat, Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -69,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('step'), [TypeF32, TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('step'), [Type.f32, Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -83,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('step'), [TypeF16, TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('step'), [Type.f16, Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.cache.ts
new file mode 100644
index 0000000000..8d8e0d980b
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.cache.ts
@@ -0,0 +1,23 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { linearRange } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ [
+ // Well-defined accuracy range
+ ...linearRange(-Math.PI, Math.PI, 100),
+ ...FP[trait].scalarRange(),
+ ],
+ 'unfiltered',
+ // tan has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].tanInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('tan', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts
index be3bdee046..7b682d0968 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'tan' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn tan(e: T ) -> T
Returns the tangent of e. Component-wise when T is a vector.
@@ -9,48 +9,33 @@ Returns the tangent of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range, linearRange } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './tan.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('tan', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(
- [
- // Defined accuracy range
- ...linearRange(-Math.PI, Math.PI, 100),
- ...fullF32Range(),
- ],
- 'unfiltered',
- FP.f32.tanInterval
- );
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(
- [
- // Defined accuracy range
- ...linearRange(-Math.PI, Math.PI, 100),
- ...fullF16Range(),
- ],
- 'unfiltered',
- FP.f16.tanInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('tan'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -60,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('tan'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('tan'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -74,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('tan'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('tan'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.cache.ts
new file mode 100644
index 0000000000..5bada7fef5
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.cache.ts
@@ -0,0 +1,18 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ 'unfiltered',
+ // tanh has an inherited accuracy, so is only expected to be as accurate as f32
+ FP[trait !== 'abstract' ? trait : 'f32'].tanhInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('tanh', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts
index 3aca5b924b..17978926a3 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'tanh' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn tanh(e: T ) -> T
Returns the hyperbolic tangent of e. Component-wise when T is a vector.
@@ -9,32 +9,33 @@ Returns the hyperbolic tangent of e. Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeF16 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF16Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
-import { allInputSources, run } from '../../expression.js';
+import { Type } from '../../../../../util/conversion.js';
+import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './tanh.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('tanh', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'unfiltered', FP.f32.tanhInterval);
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(fullF16Range(), 'unfiltered', FP.f16.tanhInterval);
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
.params(u =>
- u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
+ u
+ .combine('inputSource', onlyConstInputSource)
+ .combine('vectorize', [undefined, 2, 3, 4] as const)
)
- .unimplemented();
+ .fn(async t => {
+ const cases = await d.get('abstract');
+ await run(
+ t,
+ abstractFloatBuiltin('tanh'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
+ });
g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
@@ -44,7 +45,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('tanh'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('tanh'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -58,5 +59,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('tanh'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('tanh'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts
deleted file mode 100644
index 0ecb9964cf..0000000000
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts
+++ /dev/null
@@ -1,160 +0,0 @@
-export const description = `
-Execution tests for the 'textureDimension' builtin function
-
-The dimensions of the texture in texels.
-For textures based on cubes, the results are the dimensions of each face of the cube.
-Cube faces are square, so the x and y components of the result are equal.
-If level is outside the range [0, textureNumLevels(t)) then any valid value for the return type may be returned.
-`;
-
-import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
-import { GPUTest } from '../../../../../gpu_test.js';
-
-export const g = makeTestGroup(GPUTest);
-
-g.test('sampled')
- .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
- .desc(
- `
-T: f32, i32, u32
-
-fn textureDimensions(t: texture_1d<T>) -> u32
-fn textureDimensions(t: texture_1d<T>, level: u32) -> u32
-fn textureDimensions(t: texture_2d<T>) -> vec2<u32>
-fn textureDimensions(t: texture_2d<T>, level: u32) -> vec2<u32>
-fn textureDimensions(t: texture_2d_array<T>) -> vec2<u32>
-fn textureDimensions(t: texture_2d_array<T>, level: u32) -> vec2<u32>
-fn textureDimensions(t: texture_3d<T>) -> vec3<u32>
-fn textureDimensions(t: texture_3d<T>, level: u32) -> vec3<u32>
-fn textureDimensions(t: texture_cube<T>) -> vec2<u32>
-fn textureDimensions(t: texture_cube<T>, level: u32) -> vec2<u32>
-fn textureDimensions(t: texture_cube_array<T>) -> vec2<u32>
-fn textureDimensions(t: texture_cube_array<T>, level: u32) -> vec2<u32>
-fn textureDimensions(t: texture_multisampled_2d<T>)-> vec2<u32>
-
-Parameters:
- * t: the sampled texture
- * level:
- - The mip level, with level 0 containing a full size version of the texture.
- - If omitted, the dimensions of level 0 are returned.
-`
- )
- .params(u =>
- u
- .combine('texture_type', [
- 'texture_1d',
- 'texture_2d',
- 'texture_2d_array',
- 'texture_3d',
- 'texture_cube',
- 'texture_cube_array',
- 'texture_multisampled_2d',
- ] as const)
- .beginSubcases()
- .combine('sampled_type', ['f32-only', 'i32', 'u32'] as const)
- .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
- )
- .unimplemented();
-
-g.test('depth')
- .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
- .desc(
- `
-fn textureDimensions(t: texture_depth_2d) -> vec2<u32>
-fn textureDimensions(t: texture_depth_2d, level: u32) -> vec2<u32>
-fn textureDimensions(t: texture_depth_2d_array) -> vec2<u32>
-fn textureDimensions(t: texture_depth_2d_array, level: u32) -> vec2<u32>
-fn textureDimensions(t: texture_depth_cube) -> vec2<u32>
-fn textureDimensions(t: texture_depth_cube, level: u32) -> vec2<u32>
-fn textureDimensions(t: texture_depth_cube_array) -> vec2<u32>
-fn textureDimensions(t: texture_depth_cube_array, level: u32) -> vec2<u32>
-fn textureDimensions(t: texture_depth_multisampled_2d)-> vec2<u32>
-
-Parameters:
- * t: the depth or multisampled texture
- * level:
- - The mip level, with level 0 containing a full size version of the texture.
- - If omitted, the dimensions of level 0 are returned.
-`
- )
- .params(u =>
- u
- .combine('texture_type', [
- 'texture_depth_2d',
- 'texture_depth_2d_array',
- 'texture_depth_cube',
- 'texture_depth_cube_array',
- 'texture_depth_multisampled_2d',
- ])
- .beginSubcases()
- .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const)
- )
- .unimplemented();
-
-g.test('storage')
- .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
- .desc(
- `
-F: rgba8unorm
- rgba8snorm
- rgba8uint
- rgba8sint
- rgba16uint
- rgba16sint
- rgba16float
- r32uint
- r32sint
- r32float
- rg32uint
- rg32sint
- rg32float
- rgba32uint
- rgba32sint
- rgba32float
-A: read, write, read_write
-
-fn textureDimensions(t: texture_storage_1d<F,A>) -> u32
-fn textureDimensions(t: texture_storage_2d<F,A>) -> vec2<u32>
-fn textureDimensions(t: texture_storage_2d_array<F,A>) -> vec2<u32>
-fn textureDimensions(t: texture_storage_3d<F,A>) -> vec3<u32>
-
-Parameters:
- * t: the storage texture
-`
- )
- .params(u =>
- u
- .combine('texel_format', [
- 'rgba8unorm',
- 'rgba8snorm',
- 'rgba8uint',
- 'rgba8sint',
- 'rgba16uint',
- 'rgba16sint',
- 'rgba16float',
- 'r32uint',
- 'r32sint',
- 'r32float',
- 'rg32uint',
- 'rg32sint',
- 'rg32float',
- 'rgba32uint',
- 'rgba32sint',
- 'rgba32float',
- ] as const)
- .beginSubcases()
- .combine('access_mode', ['read', 'write', 'read_write'] as const)
- )
- .unimplemented();
-
-g.test('external')
- .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
- .desc(
- `
-fn textureDimensions(t: texture_external) -> vec2<u32>
-
-Parameters:
- * t: the external texture
-`
- )
- .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimensions.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimensions.spec.ts
new file mode 100644
index 0000000000..1e025ca618
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimensions.spec.ts
@@ -0,0 +1,518 @@
+export const description = `
+Execution tests for the 'textureDimensions' builtin function
+
+The dimensions of the texture in texels.
+For textures based on cubes, the results are the dimensions of each face of the cube.
+Cube faces are square, so the x and y components of the result are equal.
+If level is outside the range [0, textureNumLevels(t)) then any valid value for the return type may be returned.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import {
+ kAllTextureFormats,
+ kColorTextureFormats,
+ kTextureFormatInfo,
+ sampleTypeForFormatAndAspect,
+ textureDimensionAndFormatCompatible,
+} from '../../../../../format_info.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { align } from '../../../../../util/math.js';
+
+export const g = makeTestGroup(GPUTest);
+
+/// The maximum number of texture mipmap levels to test.
+/// Keep this small to reduce memory and test permutations.
+const kMaxMipsForTest = 3;
+
+/// The maximum number of texture samples to test.
+const kMaxSamplesForTest = 4;
+
+/// All the possible GPUTextureViewDimensions.
+const kAllViewDimensions: readonly GPUTextureViewDimension[] = [
+ '1d',
+ '2d',
+ '2d-array',
+ '3d',
+ 'cube',
+ 'cube-array',
+] as const;
+
+/** @returns the aspects to test for the given format */
+function aspectsForFormat(format: GPUTextureFormat): readonly GPUTextureAspect[] {
+ const formatInfo = kTextureFormatInfo[format];
+ if (formatInfo.depth !== undefined && formatInfo.stencil !== undefined) {
+ return ['depth-only', 'stencil-only'];
+ }
+ return ['all'];
+}
+
+/** @returns the sample counts to test for the given format */
+function samplesForFormat(format: GPUTextureFormat): readonly number[] {
+ const info = kTextureFormatInfo[format];
+ return info.multisample ? [1, kMaxSamplesForTest] : [1];
+}
+
+/**
+ * @returns a list of number of texture mipmap levels to test, given the format, view dimensions and
+ * number of samples.
+ */
+function textureMipCount(params: {
+ format: GPUTextureFormat;
+ dimensions: GPUTextureViewDimension;
+ samples?: number;
+}): readonly number[] {
+ if (params.samples !== undefined && params.samples !== 1) {
+ // https://www.w3.org/TR/webgpu/#texture-creation
+ // If descriptor.sampleCount > 1: descriptor.mipLevelCount must be 1.
+ return [1];
+ }
+ if (textureDimensionsForViewDimensions(params.dimensions) === '1d') {
+ // https://www.w3.org/TR/webgpu/#dom-gputexturedimension-2d
+ // Only "2d" textures may have mipmaps, be multisampled, use a compressed or depth/stencil
+ // format, and be used as a render attachment.
+ return [1];
+ }
+ return [1, kMaxMipsForTest];
+}
+
+/**
+ * @returns a list of GPUTextureViewDescriptor.baseMipLevel to test, give the texture mipmap count.
+ */
+function baseMipLevel(params: { textureMipCount: number }): readonly number[] {
+ const out: number[] = [];
+ for (let i = 0; i < params.textureMipCount; i++) {
+ out.push(i);
+ }
+ return out;
+}
+
+/**
+ * @returns the argument values for the textureDimensions() `level` parameter to test.
+ * An `undefined` represents a call to textureDimensions() without the level argument.
+ */
+function textureDimensionsLevel(params: {
+ samples?: number;
+ textureMipCount: number;
+ baseMipLevel: number;
+}): readonly (number | undefined)[] {
+ if (params.samples !== undefined && params.samples > 1) {
+ return [undefined]; // textureDimensions() overload with `level` not available.
+ }
+ const out: (number | undefined)[] = [undefined];
+ for (let i = 0; i < params.textureMipCount - params.baseMipLevel; i++) {
+ out.push(i);
+ }
+ return out;
+}
+
+/** @returns the GPUTextureViewDimensions to test for the format and number of samples */
+function viewDimensions(params: {
+ format: GPUTextureFormat;
+ samples?: number;
+}): readonly GPUTextureViewDimension[] {
+ if (params.samples !== undefined && params.samples > 1) {
+ // https://www.w3.org/TR/webgpu/#dom-gputexturedimension-2d
+ // Only 2d textures can be multisampled
+ return ['2d'];
+ }
+
+ return kAllViewDimensions.filter(dim =>
+ textureDimensionAndFormatCompatible(textureDimensionsForViewDimensions(dim), params.format)
+ );
+}
+
+/** @returns the GPUTextureDimension for the GPUTextureViewDimension */
+function textureDimensionsForViewDimensions(dim: GPUTextureViewDimension): GPUTextureDimension {
+ switch (dim) {
+ case '1d':
+ return '1d';
+ case '2d':
+ case '2d-array':
+ case 'cube':
+ case 'cube-array':
+ return '2d';
+ case '3d':
+ return '3d';
+ }
+}
+
+/** TestValues holds the texture size and expected return value of textureDimensions() */
+type TestValues = {
+ /** The value to pass to GPUTextureDescriptor.size, when creating the texture */
+ size: number[];
+ /** The expected result of calling textureDimensions() */
+ expected: number[];
+};
+
+/** @returns The TestValues to use for the given texture dimensions and format */
+function testValues(params: {
+ dimensions: GPUTextureViewDimension;
+ format: GPUTextureFormat;
+ baseMipLevel: number;
+ textureDimensionsLevel?: number;
+}): TestValues {
+ // The minimum dimension length, given the number of mipmap levels that are being tested.
+ const kMinLen = 1 << kMaxMipsForTest;
+ const kNumCubeFaces = 6;
+
+ const formatInfo = kTextureFormatInfo[params.format];
+ const bw = formatInfo.blockWidth;
+ const bh = formatInfo.blockHeight;
+ let mip = params.baseMipLevel;
+ if (params.textureDimensionsLevel !== undefined) {
+ mip += params.textureDimensionsLevel;
+ }
+
+ // Magic constants to multiply the minimum texture dimensions with, to provide
+ // different dimension values in the test. These could be parameterized, but
+ // these are currently fixed to reduce the number of test parameterizations.
+ const kMultipleA = 2;
+ const kMultipleB = 3;
+ const kMultipleC = 4;
+
+ switch (params.dimensions) {
+ case '1d': {
+ const w = align(kMinLen, bw) * kMultipleA;
+ return { size: [w], expected: [w >>> mip] };
+ }
+ case '2d': {
+ const w = align(kMinLen, bw) * kMultipleA;
+ const h = align(kMinLen, bh) * kMultipleB;
+ return { size: [w, h], expected: [w >>> mip, h >>> mip] };
+ }
+ case '2d-array': {
+ const w = align(kMinLen, bw) * kMultipleC;
+ const h = align(kMinLen, bh) * kMultipleB;
+ return { size: [w, h, 4], expected: [w >>> mip, h >>> mip] };
+ }
+ case '3d': {
+ const w = align(kMinLen, bw) * kMultipleA;
+ const h = align(kMinLen, bh) * kMultipleB;
+ const d = kMinLen * kMultipleC;
+ return {
+ size: [w, h, d],
+ expected: [w >>> mip, h >>> mip, d >>> mip],
+ };
+ }
+ case 'cube': {
+ const l = align(kMinLen, bw) * align(kMinLen, bh) * kMultipleB;
+ return {
+ size: [l, l, kNumCubeFaces],
+ expected: [l >>> mip, l >>> mip],
+ };
+ }
+ case 'cube-array': {
+ const l = align(kMinLen, bw) * align(kMinLen, bh) * kMultipleC;
+ return {
+ size: [l, l, kNumCubeFaces * 3],
+ expected: [l >>> mip, l >>> mip],
+ };
+ }
+ }
+}
+
+/**
+ * Builds a shader module with the texture view bound to the WGSL texture with the given WGSL type,
+ * which calls textureDimensions(), assigning the result to an output buffer.
+ * This shader is executed with a compute shader, and the output buffer is compared to
+ * `values.expected`.
+ */
+function run(
+ t: GPUTest,
+ view: GPUTextureView,
+ textureType: string,
+ levelArg: number | undefined,
+ values: TestValues
+) {
+ const outputType = values.expected.length > 1 ? `vec${values.expected.length}u` : 'u32';
+ const wgsl = `
+@group(0) @binding(0) var texture : ${textureType};
+@group(0) @binding(1) var<storage, read_write> output : ${outputType};
+
+@compute @workgroup_size(1)
+fn main() {
+output = ${
+ levelArg !== undefined
+ ? `textureDimensions(texture, ${levelArg})`
+ : 'textureDimensions(texture)'
+ };
+}
+`;
+ const module = t.device.createShaderModule({
+ code: wgsl,
+ });
+ const pipeline = t.device.createComputePipeline({
+ compute: { module },
+ layout: 'auto',
+ });
+ const outputBuffer = t.device.createBuffer({
+ size: 32,
+ usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE,
+ });
+ const bindgroup = t.device.createBindGroup({
+ layout: pipeline.getBindGroupLayout(0),
+ entries: [
+ { binding: 0, resource: view },
+ { binding: 1, resource: { buffer: outputBuffer } },
+ ],
+ });
+ const encoder = t.device.createCommandEncoder();
+ const pass = encoder.beginComputePass();
+ pass.setPipeline(pipeline);
+ pass.setBindGroup(0, bindgroup);
+ pass.dispatchWorkgroups(1);
+ pass.end();
+ t.device.queue.submit([encoder.finish()]);
+
+ t.expectGPUBufferValuesEqual(outputBuffer, new Uint32Array(values.expected));
+}
+
+/** @returns true if the GPUTextureViewDimension is valid for a storage texture */
+function dimensionsValidForStorage(dimensions: GPUTextureViewDimension) {
+ switch (dimensions) {
+ case '1d':
+ case '2d':
+ case '2d-array':
+ case '3d':
+ return true;
+ default:
+ return false;
+ }
+}
+
+g.test('sampled_and_multisampled')
+ .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
+ .desc(
+ `
+T: f32, i32, u32
+
+fn textureDimensions(t: texture_1d<T>) -> u32
+fn textureDimensions(t: texture_1d<T>, level: u32) -> u32
+fn textureDimensions(t: texture_2d<T>) -> vec2<u32>
+fn textureDimensions(t: texture_2d<T>, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_2d_array<T>) -> vec2<u32>
+fn textureDimensions(t: texture_2d_array<T>, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_3d<T>) -> vec3<u32>
+fn textureDimensions(t: texture_3d<T>, level: u32) -> vec3<u32>
+fn textureDimensions(t: texture_cube<T>) -> vec2<u32>
+fn textureDimensions(t: texture_cube<T>, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_cube_array<T>) -> vec2<u32>
+fn textureDimensions(t: texture_cube_array<T>, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_multisampled_2d<T>)-> vec2<u32>
+
+Parameters:
+ * t: the sampled texture
+ * level:
+ - The mip level, with level 0 containing a full size version of the texture.
+ - If omitted, the dimensions of level 0 are returned.
+`
+ )
+ .params(u =>
+ u
+ .combine('format', kAllTextureFormats)
+ .unless(p => kTextureFormatInfo[p.format].color?.type === 'unfilterable-float')
+ .expand('aspect', u => aspectsForFormat(u.format))
+ .expand('samples', u => samplesForFormat(u.format))
+ .beginSubcases()
+ .expand('dimensions', viewDimensions)
+ .expand('textureMipCount', textureMipCount)
+ .expand('baseMipLevel', baseMipLevel)
+ .expand('textureDimensionsLevel', textureDimensionsLevel)
+ )
+ .beforeAllSubcases(t => {
+ const info = kTextureFormatInfo[t.params.format];
+ t.skipIfTextureFormatNotSupported(t.params.format);
+ t.selectDeviceOrSkipTestCase(info.feature);
+ })
+ .fn(t => {
+ t.skipIfTextureViewDimensionNotSupported(t.params.dimensions);
+ const values = testValues(t.params);
+ const texture = t.device.createTexture({
+ size: values.size,
+ dimension: textureDimensionsForViewDimensions(t.params.dimensions),
+ ...(t.isCompatibility && { textureBindingViewDimension: t.params.dimensions }),
+ usage:
+ t.params.samples === 1
+ ? GPUTextureUsage.TEXTURE_BINDING
+ : GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
+ format: t.params.format,
+ sampleCount: t.params.samples,
+ mipLevelCount: t.params.textureMipCount,
+ });
+ const textureView = texture.createView({
+ dimension: t.params.dimensions,
+ aspect: t.params.aspect,
+ baseMipLevel: t.params.baseMipLevel,
+ });
+
+ function wgslSampledTextureType(): string {
+ const base = t.params.samples !== 1 ? 'texture_multisampled' : 'texture';
+ const dimensions = t.params.dimensions.replace('-', '_');
+ const sampleType = sampleTypeForFormatAndAspect(t.params.format, t.params.aspect);
+ switch (sampleType) {
+ case 'depth':
+ case 'float':
+ return `${base}_${dimensions}<f32>`;
+ case 'uint':
+ return `${base}_${dimensions}<u32>`;
+ case 'sint':
+ return `${base}_${dimensions}<i32>`;
+ case 'unfilterable-float':
+ throw new Error(`'${t.params.format}' does not support sampling`);
+ }
+ }
+
+ run(t, textureView, wgslSampledTextureType(), t.params.textureDimensionsLevel, values);
+ });
+
+g.test('depth')
+ .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
+ .desc(
+ `
+fn textureDimensions(t: texture_depth_2d) -> vec2<u32>
+fn textureDimensions(t: texture_depth_2d, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_depth_2d_array) -> vec2<u32>
+fn textureDimensions(t: texture_depth_2d_array, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_depth_cube) -> vec2<u32>
+fn textureDimensions(t: texture_depth_cube, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_depth_cube_array) -> vec2<u32>
+fn textureDimensions(t: texture_depth_cube_array, level: u32) -> vec2<u32>
+fn textureDimensions(t: texture_depth_multisampled_2d)-> vec2<u32>
+
+Parameters:
+ * t: the depth or multisampled texture
+ * level:
+ - The mip level, with level 0 containing a full size version of the texture.
+ - If omitted, the dimensions of level 0 are returned.
+`
+ )
+ .params(u =>
+ u
+ .combine('format', kAllTextureFormats)
+ .filter(p => !!kTextureFormatInfo[p.format].depth)
+ .expand('aspect', u => aspectsForFormat(u.format))
+ .unless(u => u.aspect === 'stencil-only')
+ .expand('samples', u => samplesForFormat(u.format))
+ .beginSubcases()
+ .expand('dimensions', viewDimensions)
+ .expand('textureMipCount', textureMipCount)
+ .expand('baseMipLevel', baseMipLevel)
+ .expand('textureDimensionsLevel', textureDimensionsLevel)
+ )
+ .beforeAllSubcases(t => {
+ const info = kTextureFormatInfo[t.params.format];
+ t.skipIfTextureFormatNotSupported(t.params.format);
+ t.selectDeviceOrSkipTestCase(info.feature);
+ })
+ .fn(t => {
+ t.skipIfTextureViewDimensionNotSupported(t.params.dimensions);
+ const values = testValues(t.params);
+ const texture = t.device.createTexture({
+ size: values.size,
+ dimension: textureDimensionsForViewDimensions(t.params.dimensions),
+ ...(t.isCompatibility && { textureBindingViewDimension: t.params.dimensions }),
+ usage:
+ t.params.samples === 1
+ ? GPUTextureUsage.TEXTURE_BINDING
+ : GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
+ format: t.params.format,
+ sampleCount: t.params.samples,
+ mipLevelCount: t.params.textureMipCount,
+ });
+ const textureView = texture.createView({
+ dimension: t.params.dimensions,
+ aspect: t.params.aspect,
+ baseMipLevel: t.params.baseMipLevel,
+ });
+
+ function wgslDepthTextureType(): string {
+ const base = t.params.samples !== 1 ? 'texture_depth_multisampled' : 'texture_depth';
+ const dimensions = t.params.dimensions.replace('-', '_');
+ return `${base}_${dimensions}`;
+ }
+
+ run(t, textureView, wgslDepthTextureType(), t.params.textureDimensionsLevel, values);
+ });
+
+g.test('storage')
+ .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
+ .desc(
+ `
+F: rgba8unorm
+ rgba8snorm
+ rgba8uint
+ rgba8sint
+ rgba16uint
+ rgba16sint
+ rgba16float
+ r32uint
+ r32sint
+ r32float
+ rg32uint
+ rg32sint
+ rg32float
+ rgba32uint
+ rgba32sint
+ rgba32float
+A: read, write, read_write
+
+fn textureDimensions(t: texture_storage_1d<F,A>) -> u32
+fn textureDimensions(t: texture_storage_2d<F,A>) -> vec2<u32>
+fn textureDimensions(t: texture_storage_2d_array<F,A>) -> vec2<u32>
+fn textureDimensions(t: texture_storage_3d<F,A>) -> vec3<u32>
+
+Parameters:
+ * t: the storage texture
+`
+ )
+ .params(u =>
+ u
+ .combine('format', kColorTextureFormats)
+ .filter(p => kTextureFormatInfo[p.format].color?.storage === true)
+ .expand('aspect', u => aspectsForFormat(u.format))
+ .beginSubcases()
+ .expand('dimensions', u => viewDimensions(u).filter(dimensionsValidForStorage))
+ .expand('textureMipCount', textureMipCount)
+ .expand('baseMipLevel', baseMipLevel)
+ )
+ .beforeAllSubcases(t => {
+ const info = kTextureFormatInfo[t.params.format];
+ t.skipIfTextureFormatNotSupported(t.params.format);
+ t.skipIfTextureFormatNotUsableAsStorageTexture(t.params.format);
+ t.selectDeviceOrSkipTestCase(info.feature);
+ })
+ .fn(t => {
+ const values = testValues(t.params);
+ const texture = t.device.createTexture({
+ size: values.size,
+ dimension: textureDimensionsForViewDimensions(t.params.dimensions),
+ usage: GPUTextureUsage.STORAGE_BINDING,
+ format: t.params.format,
+ mipLevelCount: t.params.textureMipCount,
+ });
+ const textureView = texture.createView({
+ dimension: t.params.dimensions,
+ aspect: t.params.aspect,
+ mipLevelCount: 1,
+ baseMipLevel: t.params.baseMipLevel,
+ });
+
+ function wgslStorageTextureType(): string {
+ const dimensions = t.params.dimensions.replace('-', '_');
+ return `texture_storage_${dimensions}<${t.params.format}, write>`;
+ }
+
+ run(t, textureView, wgslStorageTextureType(), undefined, values);
+ });
+
+g.test('external')
+ .specURL('https://www.w3.org/TR/WGSL/#texturedimensions')
+ .desc(
+ `
+fn textureDimensions(t: texture_external) -> vec2<u32>
+
+Parameters:
+ * t: the external texture
+`
+ )
+ .unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts
index f5b01dfc63..7c743576e9 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts
@@ -1,26 +1,24 @@
export const description = `
Samples a texture.
-
-Must only be used in a fragment shader stage.
-Must only be invoked in uniform control flow.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
-import { GPUTest } from '../../../../../gpu_test.js';
+import { kEncodableTextureFormats, kTextureFormatInfo } from '../../../../../format_info.js';
+import { GPUTest, TextureTestMixin } from '../../../../../gpu_test.js';
+import { hashU32 } from '../../../../../util/math.js';
+import { kTexelRepresentationInfo } from '../../../../../util/texture/texel_data.js';
+import {
+ vec2,
+ createRandomTexelView,
+ TextureCall,
+ putDataInTextureThenDrawAndCheckResults,
+ generateSamplePoints,
+ kSamplePointMethods,
+} from './texture_utils.js';
import { generateCoordBoundaries, generateOffsets } from './utils.js';
-export const g = makeTestGroup(GPUTest);
-
-g.test('stage')
- .specURL('https://www.w3.org/TR/WGSL/#texturesample')
- .desc(
- `
-Tests that 'textureSample' can only be called in 'fragment' shaders.
-`
- )
- .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
- .unimplemented();
+export const g = makeTestGroup(TextureTestMixin(GPUTest));
g.test('control_flow')
.specURL('https://www.w3.org/TR/WGSL/#texturesample')
@@ -70,11 +68,74 @@ Parameters:
Values outside of this range will result in a shader-creation error.
`
)
- .paramsSubcasesOnly(u =>
+ .params(u =>
u
- .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
- .combine('coords', generateCoordBoundaries(2))
- .combine('offset', generateOffsets(2))
+ .combine('format', kEncodableTextureFormats)
+ .filter(t => {
+ const type = kTextureFormatInfo[t.format].color?.type;
+ return type === 'float' || type === 'unfilterable-float';
+ })
+ .combine('sample_points', kSamplePointMethods)
+ .combine('addressModeU', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('addressModeV', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const)
+ .combine('minFilter', ['nearest', 'linear'] as const)
+ .combine('offset', [false, true] as const)
+ )
+ .beforeAllSubcases(t => {
+ const format = kTexelRepresentationInfo[t.params.format];
+ t.skipIfTextureFormatNotSupported(t.params.format);
+ const hasFloat32 = format.componentOrder.some(c => {
+ const info = format.componentInfo[c]!;
+ return info.dataType === 'float' && info.bitLength === 32;
+ });
+ if (hasFloat32) {
+ t.selectDeviceOrSkipTestCase('float32-filterable');
+ }
+ })
+ .fn(async t => {
+ const descriptor: GPUTextureDescriptor = {
+ format: t.params.format,
+ size: { width: 8, height: 8 },
+ usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
+ };
+ const texelView = createRandomTexelView(descriptor);
+ const calls: TextureCall<vec2>[] = generateSamplePoints(50, t.params.minFilter === 'nearest', {
+ method: t.params.sample_points,
+ textureWidth: 8,
+ textureHeight: 8,
+ }).map((c, i) => {
+ const hash = hashU32(i) & 0xff;
+ return {
+ builtin: 'textureSample',
+ coordType: 'f',
+ coords: c,
+ offset: t.params.offset ? [(hash & 15) - 8, (hash >> 4) - 8] : undefined,
+ };
+ });
+ const sampler: GPUSamplerDescriptor = {
+ addressModeU: t.params.addressModeU,
+ addressModeV: t.params.addressModeV,
+ minFilter: t.params.minFilter,
+ magFilter: t.params.minFilter,
+ };
+ const res = await putDataInTextureThenDrawAndCheckResults(
+ t.device,
+ { texels: texelView, descriptor },
+ sampler,
+ calls
+ );
+ t.expectOK(res);
+ });
+
+g.test('sampled_2d_coords,derivatives')
+ .specURL('https://www.w3.org/TR/WGSL/#texturesample')
+ .desc(
+ `
+fn textureSample(t: texture_2d<f32>, s: sampler, coords: vec2<f32>) -> vec4<f32>
+fn textureSample(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<f32>
+
+test mip level selection based on derivatives
+ `
)
.unimplemented();
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts
index 786bce4830..1c61c1a5f2 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts
@@ -2,8 +2,6 @@ export const description = `
Execution tests for the 'textureSampleBias' builtin function
Samples a texture with a bias to the mip level.
-Must only be used in a fragment shader stage.
-Must only be invoked in uniform control flow.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
@@ -13,26 +11,6 @@ import { generateCoordBoundaries, generateOffsets } from './utils.js';
export const g = makeTestGroup(GPUTest);
-g.test('stage')
- .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
- .desc(
- `
-Tests that 'textureSampleBias' can only be called in 'fragment' shaders.
-`
- )
- .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
- .unimplemented();
-
-g.test('control_flow')
- .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
- .desc(
- `
-Tests that 'textureSampleBias' can only be called in uniform control flow.
-`
- )
- .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
- .unimplemented();
-
g.test('sampled_2d_coords')
.specURL('https://www.w3.org/TR/WGSL/#texturesamplebias')
.desc(
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts
index 9f723fac2e..eae5098257 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts
@@ -1,8 +1,5 @@
export const description = `
Samples a depth texture and compares the sampled depth values against a reference value.
-
-Must only be used in a fragment shader stage.
-Must only be invoked in uniform control flow.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
@@ -12,26 +9,6 @@ import { generateCoordBoundaries, generateOffsets } from './utils.js';
export const g = makeTestGroup(GPUTest);
-g.test('stage')
- .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare')
- .desc(
- `
-Tests that 'textureSampleCompare' can only be called in 'fragment' shaders.
-`
- )
- .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
- .unimplemented();
-
-g.test('control_flow')
- .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare')
- .desc(
- `
-Tests that 'textureSampleCompare' can only be called in uniform control flow.
-`
- )
- .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const))
- .unimplemented();
-
g.test('2d_coords')
.specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare')
.desc(
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts
new file mode 100644
index 0000000000..fe2da53d5a
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts
@@ -0,0 +1,809 @@
+import { assert, range, unreachable } from '../../../../../../common/util/util.js';
+import { EncodableTextureFormat } from '../../../../../format_info.js';
+import { float32ToUint32 } from '../../../../../util/conversion.js';
+import { align, clamp, hashU32, lerp, quantizeToF32 } from '../../../../../util/math.js';
+import {
+ kTexelRepresentationInfo,
+ PerTexelComponent,
+ TexelRepresentationInfo,
+} from '../../../../../util/texture/texel_data.js';
+import { TexelView } from '../../../../../util/texture/texel_view.js';
+import { createTextureFromTexelView } from '../../../../../util/texture.js';
+import { reifyExtent3D } from '../../../../../util/unions.js';
+
+function getLimitValue(v: number) {
+ switch (v) {
+ case Number.POSITIVE_INFINITY:
+ return 1000;
+ case Number.NEGATIVE_INFINITY:
+ return -1000;
+ default:
+ return v;
+ }
+}
+
+function getValueBetweenMinAndMaxTexelValueInclusive(
+ rep: TexelRepresentationInfo,
+ normalized: number
+) {
+ return lerp(
+ getLimitValue(rep.numericRange!.min),
+ getLimitValue(rep.numericRange!.max),
+ normalized
+ );
+}
+
+/**
+ * Creates a TexelView filled with random values.
+ */
+export function createRandomTexelView(info: {
+ format: GPUTextureFormat;
+ size: GPUExtent3D;
+}): TexelView {
+ const rep = kTexelRepresentationInfo[info.format as EncodableTextureFormat];
+ const generator = (coords: Required<GPUOrigin3DDict>): Readonly<PerTexelComponent<number>> => {
+ const texel: PerTexelComponent<number> = {};
+ for (const component of rep.componentOrder) {
+ const rnd = hashU32(coords.x, coords.y, coords.z, component.charCodeAt(0));
+ const normalized = clamp(rnd / 0xffffffff, { min: 0, max: 1 });
+ texel[component] = getValueBetweenMinAndMaxTexelValueInclusive(rep, normalized);
+ }
+ return quantize(texel, rep);
+ };
+ return TexelView.fromTexelsAsColors(info.format as EncodableTextureFormat, generator);
+}
+
+export type vec2 = [number, number];
+export type vec3 = [number, number, number];
+export type vec4 = [number, number, number, number];
+export type Dimensionality = number | vec2 | vec3;
+
+type TextureCallArgKeys = keyof TextureCallArgs<number>;
+const kTextureCallArgNames: TextureCallArgKeys[] = [
+ 'coords',
+ 'mipLevel',
+ 'arrayIndex',
+ 'ddx',
+ 'ddy',
+ 'offset',
+];
+
+export interface TextureCallArgs<T extends Dimensionality> {
+ coords?: T;
+ mipLevel?: number;
+ arrayIndex?: number;
+ ddx?: T;
+ ddy?: T;
+ offset?: T;
+}
+
+export interface TextureCall<T extends Dimensionality> extends TextureCallArgs<T> {
+ builtin: 'textureSample' | 'textureLoad';
+ coordType: 'f';
+}
+
+function toArray(coords: Dimensionality): number[] {
+ if (coords instanceof Array) {
+ return coords;
+ }
+ return [coords];
+}
+
+function quantize(texel: PerTexelComponent<number>, repl: TexelRepresentationInfo) {
+ return repl.bitsToNumber(repl.unpackBits(new Uint8Array(repl.pack(repl.encode(texel)))));
+}
+
+function apply(a: number[], b: number[], op: (x: number, y: number) => number) {
+ assert(a.length === b.length, `apply(${a}, ${b}): arrays must have same length`);
+ return a.map((v, i) => op(v, b[i]));
+}
+
+const add = (a: number[], b: number[]) => apply(a, b, (x, y) => x + y);
+
+export interface Texture {
+ texels: TexelView;
+ descriptor: GPUTextureDescriptor;
+}
+
+/**
+ * Returns the expect value for a WGSL builtin texture function
+ */
+export function expected<T extends Dimensionality>(
+ call: TextureCall<T>,
+ texture: Texture,
+ sampler: GPUSamplerDescriptor
+): PerTexelComponent<number> {
+ const rep = kTexelRepresentationInfo[texture.texels.format];
+ const textureExtent = reifyExtent3D(texture.descriptor.size);
+ const textureSize = [textureExtent.width, textureExtent.height, textureExtent.depthOrArrayLayers];
+ const addressMode = [
+ sampler.addressModeU ?? 'clamp-to-edge',
+ sampler.addressModeV ?? 'clamp-to-edge',
+ sampler.addressModeW ?? 'clamp-to-edge',
+ ];
+
+ const load = (at: number[]) =>
+ texture.texels.color({
+ x: Math.floor(at[0]),
+ y: Math.floor(at[1] ?? 0),
+ z: Math.floor(at[2] ?? 0),
+ });
+
+ switch (call.builtin) {
+ case 'textureSample': {
+ const coords = toArray(call.coords!);
+
+ // convert normalized to absolute texel coordinate
+ // ┌───┬───┬───┬───┐
+ // │ a │ │ │ │ norm: a = 1/8, b = 7/8
+ // ├───┼───┼───┼───┤ abs: a = 0, b = 3
+ // │ │ │ │ │
+ // ├───┼───┼───┼───┤
+ // │ │ │ │ │
+ // ├───┼───┼───┼───┤
+ // │ │ │ │ b │
+ // └───┴───┴───┴───┘
+ let at = coords.map((v, i) => v * textureSize[i] - 0.5);
+
+ // Apply offset in whole texel units
+ if (call.offset !== undefined) {
+ at = add(at, toArray(call.offset));
+ }
+
+ const samples: { at: number[]; weight: number }[] = [];
+
+ const filter = sampler.minFilter;
+ switch (filter) {
+ case 'linear': {
+ // 'p0' is the lower texel for 'at'
+ const p0 = at.map(v => Math.floor(v));
+ // 'p1' is the higher texel for 'at'
+ const p1 = p0.map(v => v + 1);
+
+ // interpolation weights for p0 and p1
+ const p1W = at.map((v, i) => v - p0[i]);
+ const p0W = p1W.map(v => 1 - v);
+
+ switch (coords.length) {
+ case 1:
+ samples.push({ at: p0, weight: p0W[0] });
+ samples.push({ at: p1, weight: p1W[0] });
+ break;
+ case 2: {
+ samples.push({ at: p0, weight: p0W[0] * p0W[1] });
+ samples.push({ at: [p1[0], p0[1]], weight: p1W[0] * p0W[1] });
+ samples.push({ at: [p0[0], p1[1]], weight: p0W[0] * p1W[1] });
+ samples.push({ at: p1, weight: p1W[0] * p1W[1] });
+ break;
+ }
+ }
+ break;
+ }
+ case 'nearest': {
+ const p = at.map(v => Math.round(quantizeToF32(v)));
+ samples.push({ at: p, weight: 1 });
+ break;
+ }
+ default:
+ unreachable();
+ }
+
+ const out: PerTexelComponent<number> = {};
+ const ss = [];
+ for (const sample of samples) {
+ // Apply sampler address mode
+ const c = sample.at.map((v, i) => {
+ switch (addressMode[i]) {
+ case 'clamp-to-edge':
+ return clamp(v, { min: 0, max: textureSize[i] - 1 });
+ case 'mirror-repeat': {
+ const n = Math.floor(v / textureSize[i]);
+ v = v - n * textureSize[i];
+ return (n & 1) !== 0 ? textureSize[i] - v - 1 : v;
+ }
+ case 'repeat':
+ return v - Math.floor(v / textureSize[i]) * textureSize[i];
+ default:
+ unreachable();
+ }
+ });
+ const v = load(c);
+ ss.push(v);
+ for (const component of rep.componentOrder) {
+ out[component] = (out[component] ?? 0) + v[component]! * sample.weight;
+ }
+ }
+
+ return out;
+ }
+ case 'textureLoad': {
+ return load(toArray(call.coords!));
+ }
+ }
+}
+
+/**
+ * Puts random data in a texture, generates a shader that implements `calls`
+ * such that each call's result is written to the next consecutive texel of
+ * a rgba32float texture. It then checks the result of each call matches
+ * the expected result.
+ */
+export async function putDataInTextureThenDrawAndCheckResults<T extends Dimensionality>(
+ device: GPUDevice,
+ texture: Texture,
+ sampler: GPUSamplerDescriptor,
+ calls: TextureCall<T>[]
+) {
+ const results = await doTextureCalls(device, texture, sampler, calls);
+ const errs: string[] = [];
+ const rep = kTexelRepresentationInfo[texture.texels.format];
+ for (let callIdx = 0; callIdx < calls.length; callIdx++) {
+ const call = calls[callIdx];
+ const got = results[callIdx];
+ const expect = expected(call, texture, sampler);
+
+ const gULP = rep.bitsToULPFromZero(rep.numberToBits(got));
+ const eULP = rep.bitsToULPFromZero(rep.numberToBits(expect));
+ for (const component of rep.componentOrder) {
+ const g = got[component]!;
+ const e = expect[component]!;
+ const absDiff = Math.abs(g - e);
+ const ulpDiff = Math.abs(gULP[component]! - eULP[component]!);
+ const relDiff = absDiff / Math.max(Math.abs(g), Math.abs(e));
+ if (ulpDiff > 3 && relDiff > 0.03) {
+ const desc = describeTextureCall(call);
+ errs.push(`component was not as expected:
+ call: ${desc}
+ component: ${component}
+ got: ${g}
+ expected: ${e}
+ abs diff: ${absDiff.toFixed(4)}
+ rel diff: ${(relDiff * 100).toFixed(2)}%
+ ulp diff: ${ulpDiff}
+ sample points:
+`);
+ const expectedSamplePoints = [
+ 'expected:',
+ ...(await identifySamplePoints(texture.descriptor, (texels: TexelView) => {
+ return Promise.resolve(
+ expected(call, { texels, descriptor: texture.descriptor }, sampler)
+ );
+ })),
+ ];
+ const gotSamplePoints = [
+ 'got:',
+ ...(await identifySamplePoints(
+ texture.descriptor,
+ async (texels: TexelView) =>
+ (
+ await doTextureCalls(device, { texels, descriptor: texture.descriptor }, sampler, [
+ call,
+ ])
+ )[0]
+ )),
+ ];
+ errs.push(layoutTwoColumns(expectedSamplePoints, gotSamplePoints).join('\n'));
+ errs.push('', '');
+ }
+ }
+ }
+
+ return errs.length > 0 ? new Error(errs.join('\n')) : undefined;
+}
+
+/**
+ * Generates a text art grid showing which texels were sampled
+ * followed by a list of the samples and the weights used for each
+ * component.
+ *
+ * Example:
+ *
+ * 0 1 2 3 4 5 6 7
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┐
+ * 0 │ │ │ │ │ │ │ │ │
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
+ * 1 │ │ │ │ │ │ │ │ a │
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
+ * 2 │ │ │ │ │ │ │ │ b │
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
+ * 3 │ │ │ │ │ │ │ │ │
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
+ * 4 │ │ │ │ │ │ │ │ │
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
+ * 5 │ │ │ │ │ │ │ │ │
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
+ * 6 │ │ │ │ │ │ │ │ │
+ * ├───┼───┼───┼───┼───┼───┼───┼───┤
+ * 7 │ │ │ │ │ │ │ │ │
+ * └───┴───┴───┴───┴───┴───┴───┴───┘
+ * a: at: [7, 1], weights: [R: 0.75000]
+ * b: at: [7, 2], weights: [R: 0.25000]
+ */
+async function identifySamplePoints(
+ info: GPUTextureDescriptor,
+ run: (texels: TexelView) => Promise<PerTexelComponent<number>>
+) {
+ const textureSize = reifyExtent3D(info.size);
+ const numTexels = textureSize.width * textureSize.height;
+ const rep = kTexelRepresentationInfo[info.format as EncodableTextureFormat];
+
+ // Identify all the texels that are sampled, and their weights.
+ const sampledTexelWeights = new Map<number, PerTexelComponent<number>>();
+ const unclassifiedStack = [new Set<number>(range(numTexels, v => v))];
+ while (unclassifiedStack.length > 0) {
+ // Pop the an unclassified texels stack
+ const unclassified = unclassifiedStack.pop()!;
+
+ // Split unclassified texels evenly into two new sets
+ const setA = new Set<number>();
+ const setB = new Set<number>();
+ [...unclassified.keys()].forEach((t, i) => ((i & 1) === 0 ? setA : setB).add(t));
+
+ // Push setB to the unclassified texels stack
+ if (setB.size > 0) {
+ unclassifiedStack.push(setB);
+ }
+
+ // See if any of the texels in setA were sampled.
+ const results = await run(
+ TexelView.fromTexelsAsColors(
+ info.format as EncodableTextureFormat,
+ (coords: Required<GPUOrigin3DDict>): Readonly<PerTexelComponent<number>> => {
+ const isCandidate = setA.has(coords.x + coords.y * textureSize.width);
+ const texel: PerTexelComponent<number> = {};
+ for (const component of rep.componentOrder) {
+ texel[component] = isCandidate ? 1 : 0;
+ }
+ return texel;
+ }
+ )
+ );
+ if (rep.componentOrder.some(c => results[c] !== 0)) {
+ // One or more texels of setA were sampled.
+ if (setA.size === 1) {
+ // We identified a specific texel was sampled.
+ // As there was only one texel in the set, results holds the sampling weights.
+ setA.forEach(texel => sampledTexelWeights.set(texel, results));
+ } else {
+ // More than one texel in the set. Needs splitting.
+ unclassifiedStack.push(setA);
+ }
+ }
+ }
+
+ // ┌───┬───┬───┬───┐
+ // │ a │ │ │ │
+ // ├───┼───┼───┼───┤
+ // │ │ │ │ │
+ // ├───┼───┼───┼───┤
+ // │ │ │ │ │
+ // ├───┼───┼───┼───┤
+ // │ │ │ │ b │
+ // └───┴───┴───┴───┘
+ const letter = (idx: number) => String.fromCharCode(97 + idx); // 97: 'a'
+ const orderedTexelIndices: number[] = [];
+ const lines: string[] = [];
+ {
+ let line = ' ';
+ for (let x = 0; x < textureSize.width; x++) {
+ line += ` ${x} `;
+ }
+ lines.push(line);
+ }
+ {
+ let line = ' ┌';
+ for (let x = 0; x < textureSize.width; x++) {
+ line += x === textureSize.width - 1 ? '───┐' : '───┬';
+ }
+ lines.push(line);
+ }
+ for (let y = 0; y < textureSize.height; y++) {
+ {
+ let line = `${y} │`;
+ for (let x = 0; x < textureSize.width; x++) {
+ const texelIdx = x + y * textureSize.height;
+ const weight = sampledTexelWeights.get(texelIdx);
+ if (weight !== undefined) {
+ line += ` ${letter(orderedTexelIndices.length)} │`;
+ orderedTexelIndices.push(texelIdx);
+ } else {
+ line += ' │';
+ }
+ }
+ lines.push(line);
+ }
+ if (y < textureSize.height - 1) {
+ let line = ' ├';
+ for (let x = 0; x < textureSize.width; x++) {
+ line += x === textureSize.width - 1 ? '───┤' : '───┼';
+ }
+ lines.push(line);
+ }
+ }
+ {
+ let line = ' └';
+ for (let x = 0; x < textureSize.width; x++) {
+ line += x === textureSize.width - 1 ? '───┘' : '───┴';
+ }
+ lines.push(line);
+ }
+
+ orderedTexelIndices.forEach((texelIdx, i) => {
+ const weights = sampledTexelWeights.get(texelIdx)!;
+ const y = Math.floor(texelIdx / textureSize.width);
+ const x = texelIdx - y * textureSize.height;
+ const w = rep.componentOrder.map(c => `${c}: ${weights[c]?.toFixed(5)}`).join(', ');
+ lines.push(`${letter(i)}: at: [${x}, ${y}], weights: [${w}]`);
+ });
+ return lines;
+}
+
+function layoutTwoColumns(columnA: string[], columnB: string[]) {
+ const widthA = Math.max(...columnA.map(l => l.length));
+ const lines = Math.max(columnA.length, columnB.length);
+ const out: string[] = new Array<string>(lines);
+ for (let line = 0; line < lines; line++) {
+ const a = columnA[line] ?? '';
+ const b = columnB[line] ?? '';
+ out[line] = `${a}${' '.repeat(widthA - a.length)} | ${b}`;
+ }
+ return out;
+}
+
+export const kSamplePointMethods = ['texel-centre', 'spiral'] as const;
+export type SamplePointMethods = (typeof kSamplePointMethods)[number];
+
+/**
+ * Generates an array of coordinates at which to sample a texture.
+ */
+export function generateSamplePoints(
+ n: number,
+ nearest: boolean,
+ args:
+ | {
+ method: 'texel-centre';
+ textureWidth: number;
+ textureHeight: number;
+ }
+ | {
+ method: 'spiral';
+ radius?: number;
+ loops?: number;
+ textureWidth: number;
+ textureHeight: number;
+ }
+) {
+ const out: vec2[] = [];
+ switch (args.method) {
+ case 'texel-centre': {
+ for (let i = 0; i < n; i++) {
+ const r = hashU32(i);
+ const x = Math.floor(lerp(0, args.textureWidth - 1, (r & 0xffff) / 0xffff)) + 0.5;
+ const y = Math.floor(lerp(0, args.textureHeight - 1, (r >>> 16) / 0xffff)) + 0.5;
+ out.push([x / args.textureWidth, y / args.textureHeight]);
+ }
+ break;
+ }
+ case 'spiral': {
+ for (let i = 0; i < n; i++) {
+ const f = i / (Math.max(n, 2) - 1);
+ const r = (args.radius ?? 1.5) * f;
+ const a = (args.loops ?? 2) * 2 * Math.PI * f;
+ out.push([0.5 + r * Math.cos(a), 0.5 + r * Math.sin(a)]);
+ }
+ break;
+ }
+ }
+ // Samplers across devices use different methods to interpolate.
+ // Quantizing the texture coordinates seems to hit coords that produce
+ // comparable results to our computed results.
+ // Note: This value works with 8x8 textures. Other sizes have not been tested.
+ // Values that worked for reference:
+ // Win 11, NVidia 2070 Super: 16
+ // Linux, AMD Radeon Pro WX 3200: 256
+ // MacOS, M1 Mac: 256
+ const kSubdivisionsPerTexel = 4;
+ const q = [args.textureWidth * kSubdivisionsPerTexel, args.textureHeight * kSubdivisionsPerTexel];
+ return out.map(
+ c =>
+ c.map((v, i) => {
+ // Quantize to kSubdivisionsPerPixel
+ const v1 = Math.floor(v * q[i]);
+ // If it's nearest and we're on the edge of a texel then move us off the edge
+ // since the edge could choose one texel or another in nearest mode
+ const v2 = nearest && v1 % kSubdivisionsPerTexel === 0 ? v1 + 1 : v1;
+ // Convert back to texture coords
+ return v2 / q[i];
+ }) as vec2
+ );
+}
+
+function wgslTypeFor(data: Dimensionality, type: 'f' | 'i' | 'u'): string {
+ if (data instanceof Array) {
+ switch (data.length) {
+ case 2:
+ return `vec2${type}`;
+ case 3:
+ return `vec3${type}`;
+ }
+ }
+ return '${type}32';
+}
+
+function wgslExpr(data: number | vec2 | vec3 | vec4): string {
+ if (data instanceof Array) {
+ switch (data.length) {
+ case 2:
+ return `vec2(${data.map(v => v.toString()).join(', ')})`;
+ case 3:
+ return `vec3(${data.map(v => v.toString()).join(', ')})`;
+ }
+ }
+ return data.toString();
+}
+
+function binKey<T extends Dimensionality>(call: TextureCall<T>): string {
+ const keys: string[] = [];
+ for (const name of kTextureCallArgNames) {
+ const value = call[name];
+ if (value !== undefined) {
+ if (name === 'offset') {
+ // offset must be a constant expression
+ keys.push(`${name}: ${wgslExpr(value)}`);
+ } else {
+ keys.push(`${name}: ${wgslTypeFor(value, call.coordType)}`);
+ }
+ }
+ }
+ return `${call.builtin}(${keys.join(', ')})`;
+}
+
+function buildBinnedCalls<T extends Dimensionality>(calls: TextureCall<T>[]) {
+ const args: string[] = ['T']; // All texture builtins take the texture as the first argument
+ const fields: string[] = [];
+ const data: number[] = [];
+
+ const prototype = calls[0];
+ if (prototype.builtin.startsWith('textureSample')) {
+ // textureSample*() builtins take a sampler as the second argument
+ args.push('S');
+ }
+
+ for (const name of kTextureCallArgNames) {
+ const value = prototype[name];
+ if (value !== undefined) {
+ if (name === 'offset') {
+ args.push(`/* offset */ ${wgslExpr(value)}`);
+ } else {
+ args.push(`args.${name}`);
+ fields.push(`@align(16) ${name} : ${wgslTypeFor(value, prototype.coordType)}`);
+ }
+ }
+ }
+
+ for (const call of calls) {
+ for (const name of kTextureCallArgNames) {
+ const value = call[name];
+ assert(
+ (prototype[name] === undefined) === (value === undefined),
+ 'texture calls are not binned correctly'
+ );
+ if (value !== undefined && name !== 'offset') {
+ const bitcastToU32 = (value: number) => {
+ if (calls[0].coordType === 'f') {
+ return float32ToUint32(value);
+ }
+ return value;
+ };
+ if (value instanceof Array) {
+ for (const c of value) {
+ data.push(bitcastToU32(c));
+ }
+ } else {
+ data.push(bitcastToU32(value));
+ }
+ // All fields are aligned to 16 bytes.
+ while ((data.length & 3) !== 0) {
+ data.push(0);
+ }
+ }
+ }
+ }
+
+ const expr = `${prototype.builtin}(${args.join(', ')})`;
+
+ return { expr, fields, data };
+}
+
+function binCalls<T extends Dimensionality>(calls: TextureCall<T>[]): number[][] {
+ const map = new Map<string, number>(); // key to bin index
+ const bins: number[][] = [];
+ calls.forEach((call, callIdx) => {
+ const key = binKey(call);
+ const binIdx = map.get(key);
+ if (binIdx === undefined) {
+ map.set(key, bins.length);
+ bins.push([callIdx]);
+ } else {
+ bins[binIdx].push(callIdx);
+ }
+ });
+ return bins;
+}
+
+export function describeTextureCall<T extends Dimensionality>(call: TextureCall<T>): string {
+ const args: string[] = ['texture: T'];
+ if (call.builtin.startsWith('textureSample')) {
+ args.push('sampler: S');
+ }
+ for (const name of kTextureCallArgNames) {
+ const value = call[name];
+ if (value !== undefined) {
+ args.push(`${name}: ${wgslExpr(value)}`);
+ }
+ }
+ return `${call.builtin}(${args.join(', ')})`;
+}
+
+/**
+ * Given a list of "calls", each one of which has a texture coordinate,
+ * generates a fragment shader that uses the fragment position as an index
+ * (position.y * 256 + position.x) That index is then used to look up a
+ * coordinate from a storage buffer which is used to call the WGSL texture
+ * function to read/sample the texture, and then write to an rgba32float
+ * texture. We then read the rgba32float texture for the per "call" results.
+ *
+ * Calls are "binned" by call parameters. Each bin has its own structure and
+ * field in the storage buffer. This allows the calls to be non-homogenous and
+ * each have their own data type for coordinates.
+ */
+export async function doTextureCalls<T extends Dimensionality>(
+ device: GPUDevice,
+ texture: Texture,
+ sampler: GPUSamplerDescriptor,
+ calls: TextureCall<T>[]
+) {
+ let structs = '';
+ let body = '';
+ let dataFields = '';
+ const data: number[] = [];
+ let callCount = 0;
+ const binned = binCalls(calls);
+ binned.forEach((binCalls, binIdx) => {
+ const b = buildBinnedCalls(binCalls.map(callIdx => calls[callIdx]));
+ structs += `struct Args${binIdx} {
+ ${b.fields.join(', \n')}
+}
+`;
+ dataFields += ` args${binIdx} : array<Args${binIdx}, ${binCalls.length}>,
+`;
+ body += `
+ {
+ let is_active = (frag_idx >= ${callCount}) & (frag_idx < ${callCount + binCalls.length});
+ let args = data.args${binIdx}[frag_idx - ${callCount}];
+ let call = ${b.expr};
+ result = select(result, call, is_active);
+ }
+`;
+ callCount += binCalls.length;
+ data.push(...b.data);
+ });
+
+ const dataBuffer = device.createBuffer({
+ size: data.length * 4,
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.STORAGE,
+ });
+ device.queue.writeBuffer(dataBuffer, 0, new Uint32Array(data));
+
+ const rtWidth = 256;
+ const renderTarget = device.createTexture({
+ format: 'rgba32float',
+ size: { width: rtWidth, height: Math.ceil(calls.length / rtWidth) },
+ usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ const code = `
+${structs}
+
+struct Data {
+${dataFields}
+}
+
+@vertex
+fn vs_main(@builtin(vertex_index) vertex_index : u32) -> @builtin(position) vec4f {
+ let positions = array(
+ vec4f(-1, 1, 0, 1), vec4f( 1, 1, 0, 1),
+ vec4f(-1, -1, 0, 1), vec4f( 1, -1, 0, 1),
+ );
+ return positions[vertex_index];
+}
+
+@group(0) @binding(0) var T : texture_2d<f32>;
+@group(0) @binding(1) var S : sampler;
+@group(0) @binding(2) var<storage> data : Data;
+
+@fragment
+fn fs_main(@builtin(position) frag_pos : vec4f) -> @location(0) vec4f {
+ let frag_idx = u32(frag_pos.x) + u32(frag_pos.y) * ${renderTarget.width};
+ var result : vec4f;
+${body}
+ return result;
+}
+`;
+ const shaderModule = device.createShaderModule({ code });
+
+ const pipeline = device.createRenderPipeline({
+ layout: 'auto',
+ vertex: { module: shaderModule, entryPoint: 'vs_main' },
+ fragment: {
+ module: shaderModule,
+ entryPoint: 'fs_main',
+ targets: [{ format: renderTarget.format }],
+ },
+ primitive: { topology: 'triangle-strip', cullMode: 'none' },
+ });
+
+ const gpuTexture = createTextureFromTexelView(device, texture.texels, texture.descriptor);
+ const gpuSampler = device.createSampler(sampler);
+
+ const bindGroup = device.createBindGroup({
+ layout: pipeline.getBindGroupLayout(0),
+ entries: [
+ { binding: 0, resource: gpuTexture.createView() },
+ { binding: 1, resource: gpuSampler },
+ { binding: 2, resource: { buffer: dataBuffer } },
+ ],
+ });
+
+ const bytesPerRow = align(16 * renderTarget.width, 256);
+ const resultBuffer = device.createBuffer({
+ size: renderTarget.height * bytesPerRow,
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
+ });
+ const encoder = device.createCommandEncoder();
+
+ const renderPass = encoder.beginRenderPass({
+ colorAttachments: [{ view: renderTarget.createView(), loadOp: 'clear', storeOp: 'store' }],
+ });
+
+ renderPass.setPipeline(pipeline);
+ renderPass.setBindGroup(0, bindGroup);
+ renderPass.draw(4);
+ renderPass.end();
+ encoder.copyTextureToBuffer(
+ { texture: renderTarget },
+ { buffer: resultBuffer, bytesPerRow },
+ { width: renderTarget.width, height: renderTarget.height }
+ );
+ device.queue.submit([encoder.finish()]);
+
+ await resultBuffer.mapAsync(GPUMapMode.READ);
+
+ const view = TexelView.fromTextureDataByReference(
+ renderTarget.format as EncodableTextureFormat,
+ new Uint8Array(resultBuffer.getMappedRange()),
+ {
+ bytesPerRow,
+ rowsPerImage: renderTarget.height,
+ subrectOrigin: [0, 0, 0],
+ subrectSize: [renderTarget.width, renderTarget.height],
+ }
+ );
+
+ let outIdx = 0;
+ const out = new Array<PerTexelComponent<number>>(calls.length);
+ for (const bin of binned) {
+ for (const callIdx of bin) {
+ const x = outIdx % rtWidth;
+ const y = Math.floor(outIdx / rtWidth);
+ out[callIdx] = view.color({ x, y, z: 0 });
+ outIdx++;
+ }
+ }
+
+ renderTarget.destroy();
+ gpuTexture.destroy();
+ resultBuffer.destroy();
+
+ return out;
+}
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.cache.ts
new file mode 100644
index 0000000000..cb89e2d194
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.cache.ts
@@ -0,0 +1,27 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]_matCxR_[non_]const
+// abstract_matCxR_non_const is empty and not used
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .flatMap(trait =>
+ ([2, 3, 4] as const).flatMap(cols =>
+ ([2, 3, 4] as const).flatMap(rows =>
+ ([true, false] as const).map(nonConst => ({
+ [`${trait}_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => {
+ if (trait === 'abstract' && nonConst) {
+ return [];
+ }
+ return FP[trait].generateMatrixToMatrixCases(
+ FP[trait].sparseMatrixRange(cols, rows),
+ nonConst ? 'unfiltered' : 'finite',
+ FP[trait].transposeInterval
+ );
+ },
+ }))
+ )
+ )
+ )
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('transpose', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts
index 6fd4887f35..7a96906cfa 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts
@@ -1,82 +1,21 @@
export const description = `
Execution tests for the 'transpose' builtin function
-T is AbstractFloat, f32, or f16
+T is abstract-float, f32, or f16
@const transpose(e: matRxC<T> ) -> matCxR<T>
Returns the transpose of e.
`;
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeAbstractFloat, TypeF16, TypeF32, TypeMat } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import {
- sparseMatrixF16Range,
- sparseMatrixF32Range,
- sparseMatrixF64Range,
-} from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './transpose.cache.js';
export const g = makeTestGroup(GPUTest);
-// Cases: f32_matCxR_[non_]const
-const f32_cases = ([2, 3, 4] as const)
- .flatMap(cols =>
- ([2, 3, 4] as const).flatMap(rows =>
- ([true, false] as const).map(nonConst => ({
- [`f32_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f32.generateMatrixToMatrixCases(
- sparseMatrixF32Range(cols, rows),
- nonConst ? 'unfiltered' : 'finite',
- FP.f32.transposeInterval
- );
- },
- }))
- )
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: f16_matCxR_[non_]const
-const f16_cases = ([2, 3, 4] as const)
- .flatMap(cols =>
- ([2, 3, 4] as const).flatMap(rows =>
- ([true, false] as const).map(nonConst => ({
- [`f16_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => {
- return FP.f16.generateMatrixToMatrixCases(
- sparseMatrixF16Range(cols, rows),
- nonConst ? 'unfiltered' : 'finite',
- FP.f16.transposeInterval
- );
- },
- }))
- )
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-// Cases: abstract_matCxR
-const abstract_cases = ([2, 3, 4] as const)
- .flatMap(cols =>
- ([2, 3, 4] as const).map(rows => ({
- [`abstract_mat${cols}x${rows}`]: () => {
- return FP.abstract.generateMatrixToMatrixCases(
- sparseMatrixF64Range(cols, rows),
- 'finite',
- FP.abstract.transposeInterval
- );
- },
- }))
- )
- .reduce((a, b) => ({ ...a, ...b }), {});
-
-export const d = makeCaseCache('transpose', {
- ...f32_cases,
- ...f16_cases,
- ...abstract_cases,
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
.desc(`abstract float tests`)
@@ -89,12 +28,12 @@ g.test('abstract_float')
.fn(async t => {
const cols = t.params.cols;
const rows = t.params.rows;
- const cases = await d.get(`abstract_mat${cols}x${rows}`);
+ const cases = await d.get(`abstract_mat${cols}x${rows}_const`);
await run(
t,
- abstractBuiltin('transpose'),
- [TypeMat(cols, rows, TypeAbstractFloat)],
- TypeMat(rows, cols, TypeAbstractFloat),
+ abstractFloatBuiltin('transpose'),
+ [Type.mat(cols, rows, Type.abstractFloat)],
+ Type.mat(rows, cols, Type.abstractFloat),
t.params,
cases
);
@@ -120,8 +59,8 @@ g.test('f32')
await run(
t,
builtin('transpose'),
- [TypeMat(cols, rows, TypeF32)],
- TypeMat(rows, cols, TypeF32),
+ [Type.mat(cols, rows, Type.f32)],
+ Type.mat(rows, cols, Type.f32),
t.params,
cases
);
@@ -150,8 +89,8 @@ g.test('f16')
await run(
t,
builtin('transpose'),
- [TypeMat(cols, rows, TypeF16)],
- TypeMat(rows, cols, TypeF16),
+ [Type.mat(cols, rows, Type.f16)],
+ Type.mat(rows, cols, Type.f16),
t.params,
cases
);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.cache.ts
new file mode 100644
index 0000000000..061c95b07f
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.cache.ts
@@ -0,0 +1,17 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+// Cases: [f32|f16|abstract]
+const cases = (['f32', 'f16', 'abstract'] as const)
+ .map(trait => ({
+ [`${trait}`]: () => {
+ return FP[trait].generateScalarToIntervalCases(
+ FP[trait].scalarRange(),
+ 'unfiltered',
+ FP[trait].truncInterval
+ );
+ },
+ }))
+ .reduce((a, b) => ({ ...a, ...b }), {});
+
+export const d = makeCaseCache('trunc', cases);
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts
index 63cd8470f5..9d05709fc6 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts
@@ -1,7 +1,7 @@
export const description = `
Execution tests for the 'trunc' builtin function
-S is AbstractFloat, f32, f16
+S is abstract-float, f32, f16
T is S or vecN<S>
@const fn trunc(e: T ) -> T
Returns the nearest whole number whose absolute value is less than or equal to e.
@@ -10,32 +10,14 @@ Component-wise when T is a vector.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullF32Range, fullF64Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, onlyConstInputSource, run } from '../../expression.js';
-import { abstractBuiltin, builtin } from './builtin.js';
+import { abstractFloatBuiltin, builtin } from './builtin.js';
+import { d } from './trunc.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('trunc', {
- f32: () => {
- return FP.f32.generateScalarToIntervalCases(fullF32Range(), 'unfiltered', FP.f32.truncInterval);
- },
- f16: () => {
- return FP.f16.generateScalarToIntervalCases(fullF32Range(), 'unfiltered', FP.f16.truncInterval);
- },
- abstract: () => {
- return FP.abstract.generateScalarToIntervalCases(
- fullF64Range(),
- 'unfiltered',
- FP.abstract.truncInterval
- );
- },
-});
-
g.test('abstract_float')
.specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions')
.desc(`abstract float tests`)
@@ -46,7 +28,14 @@ g.test('abstract_float')
)
.fn(async t => {
const cases = await d.get('abstract');
- await run(t, abstractBuiltin('trunc'), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases);
+ await run(
+ t,
+ abstractFloatBuiltin('trunc'),
+ [Type.abstractFloat],
+ Type.abstractFloat,
+ t.params,
+ cases
+ );
});
g.test('f32')
@@ -57,7 +46,7 @@ g.test('f32')
)
.fn(async t => {
const cases = await d.get('f32');
- await run(t, builtin('trunc'), [TypeF32], TypeF32, t.params, cases);
+ await run(t, builtin('trunc'), [Type.f32], Type.f32, t.params, cases);
});
g.test('f16')
@@ -71,5 +60,5 @@ g.test('f16')
})
.fn(async t => {
const cases = await d.get('f16');
- await run(t, builtin('trunc'), [TypeF16], TypeF16, t.params, cases);
+ await run(t, builtin('trunc'), [Type.f16], Type.f16, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.cache.ts
new file mode 100644
index 0000000000..79a7a568d2
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.cache.ts
@@ -0,0 +1,20 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+export const d = makeCaseCache('unpack2x16float', {
+ u32_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'finite',
+ FP.f32.unpack2x16floatInterval
+ );
+ },
+ u32_non_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'unfiltered',
+ FP.f32.unpack2x16floatInterval
+ );
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts
index 4a0bf075e9..e145afca50 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts
@@ -7,33 +7,14 @@ interpretation of bits 16×i through 16×i+15 of e as an IEEE-754 binary16 value
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullU32Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
+import { d } from './unpack2x16float.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('unpack2x16float', {
- u32_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'finite',
- FP.f32.unpack2x16floatInterval
- );
- },
- u32_non_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'unfiltered',
- FP.f32.unpack2x16floatInterval
- );
- },
-});
-
g.test('unpack')
.specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
.desc(
@@ -44,5 +25,5 @@ g.test('unpack')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
- await run(t, builtin('unpack2x16float'), [TypeU32], TypeVec(2, TypeF32), t.params, cases);
+ await run(t, builtin('unpack2x16float'), [Type.u32], Type.vec2f, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.cache.ts
new file mode 100644
index 0000000000..89dfb475d3
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.cache.ts
@@ -0,0 +1,20 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+export const d = makeCaseCache('unpack2x16snorm', {
+ u32_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'finite',
+ FP.f32.unpack2x16snormInterval
+ );
+ },
+ u32_non_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'unfiltered',
+ FP.f32.unpack2x16snormInterval
+ );
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts
index 195cfd9a01..059a5664f9 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts
@@ -7,33 +7,14 @@ of bits 16×i through 16×i+15 of e as a twos-complement signed integer.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullU32Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
+import { d } from './unpack2x16snorm.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('unpack2x16snorm', {
- u32_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'finite',
- FP.f32.unpack2x16snormInterval
- );
- },
- u32_non_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'unfiltered',
- FP.f32.unpack2x16snormInterval
- );
- },
-});
-
g.test('unpack')
.specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
.desc(
@@ -44,5 +25,5 @@ g.test('unpack')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
- await run(t, builtin('unpack2x16snorm'), [TypeU32], TypeVec(2, TypeF32), t.params, cases);
+ await run(t, builtin('unpack2x16snorm'), [Type.u32], Type.vec2f, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.cache.ts
new file mode 100644
index 0000000000..7f0fe84af6
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.cache.ts
@@ -0,0 +1,20 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+export const d = makeCaseCache('unpack2x16unorm', {
+ u32_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'finite',
+ FP.f32.unpack2x16unormInterval
+ );
+ },
+ u32_non_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'unfiltered',
+ FP.f32.unpack2x16unormInterval
+ );
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts
index 16b4e6397c..971f384023 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts
@@ -7,33 +7,14 @@ Component i of the result is v ÷ 65535, where v is the interpretation of bits
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullU32Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
+import { d } from './unpack2x16unorm.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('unpack2x16unorm', {
- u32_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'finite',
- FP.f32.unpack2x16unormInterval
- );
- },
- u32_non_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'unfiltered',
- FP.f32.unpack2x16unormInterval
- );
- },
-});
-
g.test('unpack')
.specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
.desc(
@@ -44,5 +25,5 @@ g.test('unpack')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
- await run(t, builtin('unpack2x16unorm'), [TypeU32], TypeVec(2, TypeF32), t.params, cases);
+ await run(t, builtin('unpack2x16unorm'), [Type.u32], Type.vec2f, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.cache.ts
new file mode 100644
index 0000000000..3a4790f188
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.cache.ts
@@ -0,0 +1,20 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+export const d = makeCaseCache('unpack4x8snorm', {
+ u32_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'finite',
+ FP.f32.unpack4x8snormInterval
+ );
+ },
+ u32_non_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'unfiltered',
+ FP.f32.unpack4x8snormInterval
+ );
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts
index 7ea8d51918..d6e533621b 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts
@@ -7,33 +7,14 @@ bits 8×i through 8×i+7 of e as a twos-complement signed integer.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullU32Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
+import { d } from './unpack4x8snorm.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('unpack4x8snorm', {
- u32_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'finite',
- FP.f32.unpack4x8snormInterval
- );
- },
- u32_non_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'unfiltered',
- FP.f32.unpack4x8snormInterval
- );
- },
-});
-
g.test('unpack')
.specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
.desc(
@@ -44,5 +25,5 @@ g.test('unpack')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
- await run(t, builtin('unpack4x8snorm'), [TypeU32], TypeVec(4, TypeF32), t.params, cases);
+ await run(t, builtin('unpack4x8snorm'), [Type.u32], Type.vec4f, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.cache.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.cache.ts
new file mode 100644
index 0000000000..21390f74b1
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.cache.ts
@@ -0,0 +1,20 @@
+import { FP } from '../../../../../util/floating_point.js';
+import { fullU32Range } from '../../../../../util/math.js';
+import { makeCaseCache } from '../../case_cache.js';
+
+export const d = makeCaseCache('unpack4x8unorm', {
+ u32_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'finite',
+ FP.f32.unpack4x8unormInterval
+ );
+ },
+ u32_non_const: () => {
+ return FP.f32.generateU32ToIntervalCases(
+ fullU32Range(),
+ 'unfiltered',
+ FP.f32.unpack4x8unormInterval
+ );
+ },
+});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts
index bf54d23c12..026043da1a 100644
--- a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts
@@ -7,33 +7,14 @@ through 8×i+7 of e as an unsigned integer.
import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../../gpu_test.js';
-import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js';
-import { FP } from '../../../../../util/floating_point.js';
-import { fullU32Range } from '../../../../../util/math.js';
-import { makeCaseCache } from '../../case_cache.js';
+import { Type } from '../../../../../util/conversion.js';
import { allInputSources, run } from '../../expression.js';
import { builtin } from './builtin.js';
+import { d } from './unpack4x8unorm.cache.js';
export const g = makeTestGroup(GPUTest);
-export const d = makeCaseCache('unpack4x8unorm', {
- u32_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'finite',
- FP.f32.unpack4x8unormInterval
- );
- },
- u32_non_const: () => {
- return FP.f32.generateU32ToIntervalCases(
- fullU32Range(),
- 'unfiltered',
- FP.f32.unpack4x8unormInterval
- );
- },
-});
-
g.test('unpack')
.specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions')
.desc(
@@ -44,5 +25,5 @@ g.test('unpack')
.params(u => u.combine('inputSource', allInputSources))
.fn(async t => {
const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const');
- await run(t, builtin('unpack4x8unorm'), [TypeU32], TypeVec(4, TypeF32), t.params, cases);
+ await run(t, builtin('unpack4x8unorm'), [Type.u32], Type.vec4f, t.params, cases);
});
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts
new file mode 100644
index 0000000000..4f7e4ed3a7
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts
@@ -0,0 +1,56 @@
+export const description = `
+Execution tests for the 'unpack4xI8' builtin function
+
+@const fn unpack4xI8(e: u32) -> vec4<i32>
+e is interpreted as a vector with four 8-bit signed integer components. Unpack e into a vec4<i32>
+with sign extension.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { u32, toVector, i32, Type } from '../../../../../util/conversion.js';
+import { Case } from '../../case.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('basic')
+ .specURL('https://www.w3.org/TR/WGSL/#unpack4xI8-builtin')
+ .desc(
+ `
+@const fn unpack4xI8(e: u32) -> vec4<i32>
+ `
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const unpack4xI8 = (e: number) => {
+ const result: [number, number, number, number] = [0, 0, 0, 0];
+ for (let i = 0; i < 4; ++i) {
+ let intValue = (e >> (8 * i)) & 0xff;
+ if (intValue > 127) {
+ intValue -= 256;
+ }
+ result[i] = intValue;
+ }
+ return result;
+ };
+
+ const testInputs = [
+ 0, 0x01020304, 0xfcfdfeff, 0x040302ff, 0x0403fe01, 0x04fd0201, 0xfc030201, 0xfcfdfe01,
+ 0xfcfd02ff, 0xfc03feff, 0x04fdfeff, 0x0403feff, 0x04fd02ff, 0xfc0302ff, 0x04fdfe01,
+ 0xfc03fe01, 0xfcfd0201, 0x80817f7e,
+ ] as const;
+
+ const makeCase = (e: number): Case => {
+ return { input: [u32(e)], expected: toVector(unpack4xI8(e), i32) };
+ };
+ const cases: Array<Case> = testInputs.flatMap(v => {
+ return [makeCase(v)];
+ });
+
+ await run(t, builtin('unpack4xI8'), [Type.u32], Type.vec4i, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts
new file mode 100644
index 0000000000..09849030eb
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts
@@ -0,0 +1,48 @@
+export const description = `
+Execution tests for the 'unpack4xU8' builtin function
+
+@const fn unpack4xU8(e: u32) -> vec4<u32>
+e is interpreted as a vector with four 8-bit unsigned integer components. Unpack e into a vec4<u32>
+with zero extension.
+`;
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { u32, toVector, Type } from '../../../../../util/conversion.js';
+import { Case } from '../../case.js';
+import { allInputSources, Config, run } from '../../expression.js';
+
+import { builtin } from './builtin.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('basic')
+ .specURL('https://www.w3.org/TR/WGSL/#unpack4xU8-builtin')
+ .desc(
+ `
+@const fn unpack4xU8(e: u32) -> vec4<u32>
+ `
+ )
+ .params(u => u.combine('inputSource', allInputSources))
+ .fn(async t => {
+ const cfg: Config = t.params;
+
+ const unpack4xU8 = (e: number) => {
+ const result: [number, number, number, number] = [0, 0, 0, 0];
+ for (let i = 0; i < 4; ++i) {
+ result[i] = (e >> (8 * i)) & 0xff;
+ }
+ return result;
+ };
+
+ const testInputs = [0, 0x08060402, 0xffffffff, 0xfefdfcfb] as const;
+
+ const makeCase = (e: number): Case => {
+ return { input: [u32(e)], expected: toVector(unpack4xU8(e), u32) };
+ };
+ const cases: Array<Case> = testInputs.flatMap(v => {
+ return [makeCase(v)];
+ });
+
+ await run(t, builtin('unpack4xU8'), [Type.u32], Type.vec4u, cfg, cases);
+ });
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupUniformLoad.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupUniformLoad.spec.ts
new file mode 100644
index 0000000000..099b54146d
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupUniformLoad.spec.ts
@@ -0,0 +1,182 @@
+export const description = `
+Executes a control barrier synchronization function that affects memory and atomic operations in the workgroup address space.
+`;
+
+// NOTE: The control barrier executed by this builtin is tested in the memory_model tests.
+
+import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
+import { keysOf } from '../../../../../../common/util/data_tables.js';
+import {
+ TypedArrayBufferView,
+ TypedArrayBufferViewConstructor,
+ iterRange,
+} from '../../../../../../common/util/util.js';
+import { GPUTest } from '../../../../../gpu_test.js';
+import { checkElementsEqualGenerated } from '../../../../../util/check_contents.js';
+
+export const g = makeTestGroup(GPUTest);
+
+interface TypeConfig {
+ // The value to store the workgroup variable.
+ store_val: string;
+ // The expected values once the variable has been copied back to the host.
+ expected: TypedArrayBufferView;
+ // The type used for the host-visible buffer, if different from the workgroup variable.
+ host_type?: string;
+ // A type conversion function, if the types are different.
+ to_host?: (x: string) => string;
+ // Any additional module-scope declarations needed by the type.
+ decls?: string;
+}
+
+// A list of types configurations used for the workgroup variable.
+const kTypes: Record<string, TypeConfig> = {
+ bool: {
+ store_val: `true`,
+ expected: new Uint32Array([1]),
+ host_type: 'u32',
+ to_host: (x: string) => `u32(${x})`,
+ },
+ u32: {
+ store_val: `42`,
+ expected: new Uint32Array([42]),
+ },
+ vec4u: {
+ store_val: `vec4u(42, 1, 0xffffffff, 777)`,
+ expected: new Uint32Array([42, 1, 0xffffffff, 777]),
+ },
+ mat3x2f: {
+ store_val: `mat3x2(42, 1, 65536, -42, -1, -65536)`,
+ expected: new Float32Array([42, 1, 65536, -42, -1, -65536]),
+ },
+ 'array<u32, 4>': {
+ store_val: `array(42, 1, 0xffffffff, 777)`,
+ expected: new Uint32Array([42, 1, 0xffffffff, 777]),
+ },
+ SimpleStruct: {
+ decls: 'struct SimpleStruct { a: u32, b: u32, c: u32, d: u32, }',
+ store_val: `SimpleStruct(42, 1, 0xffffffff, 777)`,
+ expected: new Uint32Array([42, 1, 0xffffffff, 777]),
+ },
+ ComplexStruct: {
+ decls: `struct Inner { v: vec4u, }
+ struct ComplexStruct {
+ a: array<Inner, 4>,
+ @size(28) b: vec4u,
+ c: u32
+ }
+ const v = vec4(42, 1, 0xffffffff, 777);
+ const rhs = ComplexStruct(
+ array(Inner(v.xyzw), Inner(v.yzwx), Inner(v.zwxy), Inner(v.wxyz)),
+ v.xzxz,
+ 0x12345678,
+ );`,
+ store_val: `rhs`,
+ expected: new Uint32Array([
+ // v.xyzw
+ 42, 1, 0xffffffff, 777,
+ // v.yzwx
+ 1, 0xffffffff, 777, 42,
+ // v.zwxy
+ 0xffffffff, 777, 42, 1,
+ // v.wxyz
+ 777, 42, 1, 0xffffffff,
+ // v.xzxz
+ 42, 0xffffffff, 42, 0xffffffff,
+ // 12 bytes of padding
+ 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x12345678,
+ ]),
+ },
+};
+
+g.test('types')
+ .specURL('https://gpuweb.github.io/gpuweb/wgsl/#workgroupUniformLoad-builtin')
+ .desc(
+ `Test that the result of a workgroupUniformLoad is the value previously stored to the workgroup variable, for a variety of types.
+ `
+ )
+ .params(u =>
+ u.combine('type', keysOf(kTypes)).combine('wgsize', [
+ [1, 1],
+ [3, 7],
+ [1, 128],
+ [16, 16],
+ ])
+ )
+ .fn(t => {
+ const type = kTypes[t.params.type];
+ const wgsize_x = t.params.wgsize[0];
+ const wgsize_y = t.params.wgsize[1];
+ const num_invocations = wgsize_x * wgsize_y;
+ const num_words_per_invocation = type.expected.length;
+ const total_host_words = num_invocations * num_words_per_invocation;
+
+ t.skipIf(
+ num_invocations > t.device.limits.maxComputeInvocationsPerWorkgroup,
+ `num_invocations (${num_invocations}) > maxComputeInvocationsPerWorkgroup (${t.device.limits.maxComputeInvocationsPerWorkgroup})`
+ );
+
+ let load = `workgroupUniformLoad(&wgvar)`;
+ if (type.to_host) {
+ load = type.to_host(load);
+ }
+
+ // Construct a shader that stores a value to workgroup variable and then loads it using
+ // workgroupUniformLoad() in every invocation, copying the results back to a storage buffer.
+ const code = `
+ ${type.decls ? type.decls : ''}
+
+ @group(0) @binding(0) var<storage, read_write> buffer : array<${
+ type.host_type ? type.host_type : t.params.type
+ }, ${num_invocations}>;
+
+ var<workgroup> wgvar : ${t.params.type};
+
+ @compute @workgroup_size(${wgsize_x}, ${wgsize_y})
+ fn main(@builtin(local_invocation_index) lid: u32) {
+ if (lid == ${num_invocations - 1}) {
+ wgvar = ${type.store_val};
+ }
+ buffer[lid] = ${load};
+ }
+ `;
+ const pipeline = t.device.createComputePipeline({
+ layout: 'auto',
+ compute: {
+ module: t.device.createShaderModule({ code }),
+ entryPoint: 'main',
+ },
+ });
+
+ // Allocate a buffer and fill it with 0xdeadbeef values.
+ const outputBuffer = t.makeBufferWithContents(
+ new Uint32Array([...iterRange(total_host_words, _i => 0xdeadbeef)]),
+ GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
+ );
+ const bindGroup = t.device.createBindGroup({
+ layout: pipeline.getBindGroupLayout(0),
+ entries: [{ binding: 0, resource: { buffer: outputBuffer } }],
+ });
+
+ // Run the shader.
+ const encoder = t.device.createCommandEncoder();
+ const pass = encoder.beginComputePass();
+ pass.setPipeline(pipeline);
+ pass.setBindGroup(0, bindGroup);
+ pass.dispatchWorkgroups(1);
+ pass.end();
+ t.queue.submit([encoder.finish()]);
+
+ // Check that the output matches the expected values for each invocation.
+ t.expectGPUBufferValuesPassCheck(
+ outputBuffer,
+ data =>
+ checkElementsEqualGenerated(data, i => {
+ return Number(type.expected[i % num_words_per_invocation]);
+ }),
+ {
+ type: type.expected.constructor as TypedArrayBufferViewConstructor,
+ typedLength: total_host_words,
+ }
+ );
+ });