From 1acdb0dbd986f4a3e7e09d3804c26cb98033ef12 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Mon, 12 Feb 2024 17:32:27 -0500 Subject: [PATCH] wgsl: Implement AbstractFloat addition execution tests There is no compound tests, since += is not defined for consts, and AbstractFloats are always const. Lots of little tweaks in the PR wrt to renaming things to distinguish between AbstractFloat and AbstractInt, and threading generics through to allow reusing the same code for number and bigint. Issue #3390 --- src/resources/cache/hashes.json | 211 +++++++++--------- .../shader/execution/binary/ai_arithmetic.bin | Bin 0 -> 182184 bytes src/webgpu/listing_meta.json | 3 + .../expression/binary/af_addition.spec.ts | 10 +- .../expression/binary/af_division.spec.ts | 10 +- .../binary/af_matrix_addition.spec.ts | 4 +- .../binary/af_matrix_subtraction.spec.ts | 4 +- .../binary/af_multiplication.spec.ts | 10 +- .../expression/binary/af_remainder.spec.ts | 10 +- .../expression/binary/af_subtraction.spec.ts | 10 +- .../expression/binary/ai_arithmetic.cache.ts | 40 ++++ .../expression/binary/ai_arithmetic.spec.ts | 71 ++++++ .../execution/expression/binary/binary.ts | 8 +- .../shader/execution/expression/case.ts | 163 +++++++++----- src/webgpu/util/conversion.ts | 4 +- src/webgpu/util/math.ts | 78 ++++++- 16 files changed, 436 insertions(+), 200 deletions(-) create mode 100644 src/resources/cache/webgpu/shader/execution/binary/ai_arithmetic.bin create mode 100644 src/webgpu/shader/execution/expression/binary/ai_arithmetic.cache.ts create mode 100644 src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index a825a4d57297..bcef9ec2c51c 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,107 +1,108 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "d2a94254", - "webgpu/shader/execution/binary/af_logical.bin": "e32bdae7", - "webgpu/shader/execution/binary/af_division.bin": "998cb6fc", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "496bd4ec", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "dd4e9a4f", - "webgpu/shader/execution/binary/af_multiplication.bin": "e81deb2c", - "webgpu/shader/execution/binary/af_remainder.bin": "5aeed2e", - "webgpu/shader/execution/binary/af_subtraction.bin": "dc69ddc1", - "webgpu/shader/execution/binary/f16_addition.bin": "18815537", - "webgpu/shader/execution/binary/f16_logical.bin": "62ffb51f", - "webgpu/shader/execution/binary/f16_division.bin": "e4bf7770", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "ca4752e2", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "fec02413", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "1a7e0d08", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "8044743e", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "ffb049e3", - "webgpu/shader/execution/binary/f16_multiplication.bin": "321b90f1", - "webgpu/shader/execution/binary/f16_remainder.bin": "bafa853", - "webgpu/shader/execution/binary/f16_subtraction.bin": "95a6af75", - "webgpu/shader/execution/binary/f32_addition.bin": "84b9178a", - "webgpu/shader/execution/binary/f32_logical.bin": "bc43ddfb", - "webgpu/shader/execution/binary/f32_division.bin": "2a526e73", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "23f30d9c", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "955042e6", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "61829041", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "2f60e0a", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "3d6aba5e", - "webgpu/shader/execution/binary/f32_multiplication.bin": "2b4ddfb", - "webgpu/shader/execution/binary/f32_remainder.bin": "a3162fbe", - "webgpu/shader/execution/binary/f32_subtraction.bin": "5844472c", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "77bd762d", - "webgpu/shader/execution/binary/i32_comparison.bin": "deaf3d06", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "91ddc813", - "webgpu/shader/execution/binary/u32_comparison.bin": "82d7e10a", - "webgpu/shader/execution/abs.bin": "c2a08da0", - "webgpu/shader/execution/acos.bin": "ecb8f6e4", - "webgpu/shader/execution/acosh.bin": "e908c085", - "webgpu/shader/execution/asin.bin": "db1956dd", - "webgpu/shader/execution/asinh.bin": "d1c4370e", - "webgpu/shader/execution/atan.bin": "d32f84de", - "webgpu/shader/execution/atan2.bin": "c82ce2a9", - "webgpu/shader/execution/atanh.bin": "1d7fa05e", - "webgpu/shader/execution/bitcast.bin": "e6af5117", - "webgpu/shader/execution/ceil.bin": "6822702d", - "webgpu/shader/execution/clamp.bin": "2dcc8e33", - "webgpu/shader/execution/cos.bin": "1e4b5876", - "webgpu/shader/execution/cosh.bin": "88b43457", - "webgpu/shader/execution/cross.bin": "64b251e3", - "webgpu/shader/execution/degrees.bin": "ff5755de", - "webgpu/shader/execution/determinant.bin": "89ce1093", - "webgpu/shader/execution/distance.bin": "5694ecf1", - "webgpu/shader/execution/dot.bin": "60da4277", - "webgpu/shader/execution/exp.bin": "5ae18fc8", - "webgpu/shader/execution/exp2.bin": "ff8f009b", - "webgpu/shader/execution/faceForward.bin": "6ce196c1", - "webgpu/shader/execution/floor.bin": "addfc3d9", - "webgpu/shader/execution/fma.bin": "668f05ee", - "webgpu/shader/execution/fract.bin": "6d036c34", - "webgpu/shader/execution/frexp.bin": "3609ccd2", - "webgpu/shader/execution/inverseSqrt.bin": "b65957cd", - "webgpu/shader/execution/ldexp.bin": "99dcba57", - "webgpu/shader/execution/length.bin": "48edc788", - "webgpu/shader/execution/log.bin": "59d405b3", - "webgpu/shader/execution/log2.bin": "b897533e", - "webgpu/shader/execution/max.bin": "bb46b974", - "webgpu/shader/execution/min.bin": "e4e95641", - "webgpu/shader/execution/mix.bin": "4b781b3b", - "webgpu/shader/execution/modf.bin": "dda61537", - "webgpu/shader/execution/normalize.bin": "73a2bb69", - "webgpu/shader/execution/pack2x16float.bin": "d4a1d8f6", - "webgpu/shader/execution/pow.bin": "f4b1f7c6", - "webgpu/shader/execution/quantizeToF16.bin": "9632b1a3", - "webgpu/shader/execution/radians.bin": "838c408e", - "webgpu/shader/execution/reflect.bin": "401123c3", - "webgpu/shader/execution/refract.bin": "cf86546c", - "webgpu/shader/execution/round.bin": "d1cf1ebb", - "webgpu/shader/execution/saturate.bin": "6ecf8d76", - "webgpu/shader/execution/sign.bin": "f7757f54", - "webgpu/shader/execution/sin.bin": "aff5ff89", - "webgpu/shader/execution/sinh.bin": "10264f6", - "webgpu/shader/execution/smoothstep.bin": "cc165c8d", - "webgpu/shader/execution/sqrt.bin": "9288e28", - "webgpu/shader/execution/step.bin": "3e730ebc", - "webgpu/shader/execution/tan.bin": "b249b4ca", - "webgpu/shader/execution/tanh.bin": "add851dd", - "webgpu/shader/execution/transpose.bin": "ee64fd10", - "webgpu/shader/execution/trunc.bin": "b6c5fdcc", - "webgpu/shader/execution/unpack2x16float.bin": "46be8a57", - "webgpu/shader/execution/unpack2x16snorm.bin": "f2a50142", - "webgpu/shader/execution/unpack2x16unorm.bin": "6db99ed6", - "webgpu/shader/execution/unpack4x8snorm.bin": "4e88f425", - "webgpu/shader/execution/unpack4x8unorm.bin": "b818c7b", - "webgpu/shader/execution/unary/af_arithmetic.bin": "69e5897a", - "webgpu/shader/execution/unary/af_assignment.bin": "588f3b94", - "webgpu/shader/execution/unary/bool_conversion.bin": "7b22a4bf", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "88bfdf25", - "webgpu/shader/execution/unary/f16_conversion.bin": "257326e2", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "54315cef", - "webgpu/shader/execution/unary/f32_conversion.bin": "15bdedd7", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "d8556f43", - "webgpu/shader/execution/unary/i32_complement.bin": "d044f52", - "webgpu/shader/execution/unary/i32_conversion.bin": "d1d94808", - "webgpu/shader/execution/unary/u32_complement.bin": "5458ccb1", - "webgpu/shader/execution/unary/u32_conversion.bin": "a7b4272e", - "webgpu/shader/execution/unary/ai_assignment.bin": "b15b5af8" + "webgpu/shader/execution/binary/af_addition.bin": "6295ae8d", + "webgpu/shader/execution/binary/af_logical.bin": "7b3b4609", + "webgpu/shader/execution/binary/af_division.bin": "6d08dc82", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "fcb63375", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "84a55db0", + "webgpu/shader/execution/binary/af_multiplication.bin": "6973ad39", + "webgpu/shader/execution/binary/af_remainder.bin": "64ce8104", + "webgpu/shader/execution/binary/af_subtraction.bin": "b2c3bacd", + "webgpu/shader/execution/binary/f16_addition.bin": "ec50b194", + "webgpu/shader/execution/binary/f16_logical.bin": "478641c", + "webgpu/shader/execution/binary/f16_division.bin": "b244992c", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "fe01bbc0", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "648ea38f", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "88f5f34d", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "d318acba", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "a87759", + "webgpu/shader/execution/binary/f16_multiplication.bin": "37b44670", + "webgpu/shader/execution/binary/f16_remainder.bin": "27952a40", + "webgpu/shader/execution/binary/f16_subtraction.bin": "bebbb384", + "webgpu/shader/execution/binary/f32_addition.bin": "edfc25b3", + "webgpu/shader/execution/binary/f32_logical.bin": "68a96b19", + "webgpu/shader/execution/binary/f32_division.bin": "55a5ad13", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "95ab5a76", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "c58c207b", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "996185c6", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "4bb82fdf", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "3c1824d", + "webgpu/shader/execution/binary/f32_multiplication.bin": "59d49dd9", + "webgpu/shader/execution/binary/f32_remainder.bin": "29fda04c", + "webgpu/shader/execution/binary/f32_subtraction.bin": "73fbf5dc", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "646a8885", + "webgpu/shader/execution/binary/i32_comparison.bin": "d7b785fd", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "567c4904", + "webgpu/shader/execution/binary/u32_comparison.bin": "edd2c4d", + "webgpu/shader/execution/abs.bin": "75cfc247", + "webgpu/shader/execution/acos.bin": "a9f2fa11", + "webgpu/shader/execution/acosh.bin": "14c2c311", + "webgpu/shader/execution/asin.bin": "99dcf943", + "webgpu/shader/execution/asinh.bin": "1db7a683", + "webgpu/shader/execution/atan.bin": "76983868", + "webgpu/shader/execution/atan2.bin": "45dcaeaa", + "webgpu/shader/execution/atanh.bin": "5514ca60", + "webgpu/shader/execution/bitcast.bin": "befdc87", + "webgpu/shader/execution/ceil.bin": "5d6cafdf", + "webgpu/shader/execution/clamp.bin": "b6146dbf", + "webgpu/shader/execution/cos.bin": "711b4fd4", + "webgpu/shader/execution/cosh.bin": "d9897d30", + "webgpu/shader/execution/cross.bin": "2a359c16", + "webgpu/shader/execution/degrees.bin": "d4ed9a44", + "webgpu/shader/execution/determinant.bin": "367920d5", + "webgpu/shader/execution/distance.bin": "5425926a", + "webgpu/shader/execution/dot.bin": "47f600ab", + "webgpu/shader/execution/exp.bin": "422bde4c", + "webgpu/shader/execution/exp2.bin": "ed8f1603", + "webgpu/shader/execution/faceForward.bin": "a8fe4a20", + "webgpu/shader/execution/floor.bin": "b4f14f3b", + "webgpu/shader/execution/fma.bin": "f60736ee", + "webgpu/shader/execution/fract.bin": "6b208a6d", + "webgpu/shader/execution/frexp.bin": "a10c6804", + "webgpu/shader/execution/inverseSqrt.bin": "22d094d6", + "webgpu/shader/execution/ldexp.bin": "85904868", + "webgpu/shader/execution/length.bin": "70b32f6", + "webgpu/shader/execution/log.bin": "4521ed5d", + "webgpu/shader/execution/log2.bin": "9a123e7", + "webgpu/shader/execution/max.bin": "ade14b73", + "webgpu/shader/execution/min.bin": "759505c4", + "webgpu/shader/execution/mix.bin": "1ad590ad", + "webgpu/shader/execution/modf.bin": "e78708e1", + "webgpu/shader/execution/normalize.bin": "21415be9", + "webgpu/shader/execution/pack2x16float.bin": "d07f345b", + "webgpu/shader/execution/pow.bin": "ae217c82", + "webgpu/shader/execution/quantizeToF16.bin": "3f8977d5", + "webgpu/shader/execution/radians.bin": "109797b3", + "webgpu/shader/execution/reflect.bin": "d6d70976", + "webgpu/shader/execution/refract.bin": "2f0a69bb", + "webgpu/shader/execution/round.bin": "4b744389", + "webgpu/shader/execution/saturate.bin": "15f37d3c", + "webgpu/shader/execution/sign.bin": "509816ea", + "webgpu/shader/execution/sin.bin": "e7d03236", + "webgpu/shader/execution/sinh.bin": "ca4fbb5", + "webgpu/shader/execution/smoothstep.bin": "6f3c506a", + "webgpu/shader/execution/sqrt.bin": "64c67887", + "webgpu/shader/execution/step.bin": "62dcbffb", + "webgpu/shader/execution/tan.bin": "de5a76d4", + "webgpu/shader/execution/tanh.bin": "36000b2f", + "webgpu/shader/execution/transpose.bin": "36d83e3c", + "webgpu/shader/execution/trunc.bin": "1aad5e1c", + "webgpu/shader/execution/unpack2x16float.bin": "41fbf466", + "webgpu/shader/execution/unpack2x16snorm.bin": "b2fde57", + "webgpu/shader/execution/unpack2x16unorm.bin": "9531a4f4", + "webgpu/shader/execution/unpack4x8snorm.bin": "39b662f2", + "webgpu/shader/execution/unpack4x8unorm.bin": "a7cd2c93", + "webgpu/shader/execution/unary/af_arithmetic.bin": "55c1671e", + "webgpu/shader/execution/unary/af_assignment.bin": "60be3db0", + "webgpu/shader/execution/unary/bool_conversion.bin": "f6dac757", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "e762e795", + "webgpu/shader/execution/unary/f16_conversion.bin": "c568fe21", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "7210a687", + "webgpu/shader/execution/unary/f32_conversion.bin": "1cc5f3cd", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "9dfd1efe", + "webgpu/shader/execution/unary/i32_complement.bin": "405dfffa", + "webgpu/shader/execution/unary/i32_conversion.bin": "f2003321", + "webgpu/shader/execution/unary/u32_complement.bin": "987add5f", + "webgpu/shader/execution/unary/u32_conversion.bin": "3b3c5860", + "webgpu/shader/execution/unary/ai_assignment.bin": "bb95bb75", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "ea3dd3e1" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/binary/ai_arithmetic.bin b/src/resources/cache/webgpu/shader/execution/binary/ai_arithmetic.bin new file mode 100644 index 0000000000000000000000000000000000000000..3816f04d1fae4e00936e81221f1ebd49db1f5ead GIT binary patch literal 182184 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 bKgHv|Z~OiY->EJPqCfAIbP4w#d- literal 0 HcmV?d00001 diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index a672a5c8750b..96c2ee50cf52 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -896,6 +896,9 @@ "webgpu:idl,constructable:gpu_errors:*": { "subcaseMS": 0.101 }, "webgpu:idl,constructable:pipeline_errors:*": { "subcaseMS": 0.101 }, "webgpu:idl,constructable:uncaptured_error_event:*": { "subcaseMS": 0.101 }, + "webgpu:shader,execution,expression,binary,ai_arithmetic:addition:*": { "subcaseMS": 0 }, + "webgpu:shader,execution,expression,binary,ai_arithmetic:addition_scalar_vector:*": { "subcaseMS": 0 }, + "webgpu:shader,execution,expression,binary,ai_arithmetic:addition_vector_scalar:*": { "subcaseMS": 0 }, "webgpu:shader,execution,expression,binary,af_addition:scalar:*": { "subcaseMS": 815.300 }, "webgpu:shader,execution,expression,binary,af_addition:scalar_vector:*": { "subcaseMS": 1803.434 }, "webgpu:shader,execution,expression,binary,af_addition:vector:*": { "subcaseMS": 719.600 }, diff --git a/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts index b34fb4f150dd..3b14897c2227 100644 --- a/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts @@ -8,7 +8,7 @@ import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_addition.cache.js'; -import { abstractBinary } from './binary.js'; +import { abstractFloatBinary } from './binary.js'; export const g = makeTestGroup(GPUTest); @@ -25,7 +25,7 @@ Accuracy: Correctly rounded const cases = await d.get('scalar'); await run( t, - abstractBinary('+'), + abstractFloatBinary('+'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -48,7 +48,7 @@ Accuracy: Correctly rounded const cases = await d.get('scalar'); // Using vectorize to generate vector cases based on scalar cases await run( t, - abstractBinary('+'), + abstractFloatBinary('+'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -70,7 +70,7 @@ Accuracy: Correctly rounded const cases = await d.get(`vec${dim}_scalar`); await run( t, - abstractBinary('+'), + abstractFloatBinary('+'), [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], TypeVec(dim, TypeAbstractFloat), t.params, @@ -92,7 +92,7 @@ Accuracy: Correctly rounded const cases = await d.get(`scalar_vec${dim}`); await run( t, - abstractBinary('+'), + abstractFloatBinary('+'), [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], TypeVec(dim, TypeAbstractFloat), t.params, diff --git a/src/webgpu/shader/execution/expression/binary/af_division.spec.ts b/src/webgpu/shader/execution/expression/binary/af_division.spec.ts index 1e5492a34d9c..5080e62264d2 100644 --- a/src/webgpu/shader/execution/expression/binary/af_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_division.spec.ts @@ -8,7 +8,7 @@ import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_division.cache.js'; -import { abstractBinary } from './binary.js'; +import { abstractFloatBinary } from './binary.js'; export const g = makeTestGroup(GPUTest); @@ -25,7 +25,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get('scalar'); await run( t, - abstractBinary('/'), + abstractFloatBinary('/'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -48,7 +48,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get('scalar'); // Using vectorize to generate vector cases based on scalar cases await run( t, - abstractBinary('/'), + abstractFloatBinary('/'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -70,7 +70,7 @@ Accuracy: Correctly rounded const cases = await d.get(`vec${dim}_scalar`); await run( t, - abstractBinary('/'), + abstractFloatBinary('/'), [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], TypeVec(dim, TypeAbstractFloat), t.params, @@ -92,7 +92,7 @@ Accuracy: Correctly rounded const cases = await d.get(`scalar_vec${dim}`); await run( t, - abstractBinary('/'), + abstractFloatBinary('/'), [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], TypeVec(dim, TypeAbstractFloat), t.params, diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts index 9f232c1ef6e6..7f6019f53127 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts @@ -8,7 +8,7 @@ import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_addition.cache.js'; -import { abstractBinary } from './binary.js'; +import { abstractFloatBinary } from './binary.js'; export const g = makeTestGroup(GPUTest); @@ -32,7 +32,7 @@ Accuracy: Correctly rounded const cases = await d.get(`mat${cols}x${rows}`); await run( t, - abstractBinary('+'), + abstractFloatBinary('+'), [TypeMat(cols, rows, TypeAbstractFloat), TypeMat(cols, rows, TypeAbstractFloat)], TypeMat(cols, rows, TypeAbstractFloat), t.params, diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts index 2697d1fe8cb0..4c6d1d423234 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts @@ -8,7 +8,7 @@ import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_subtraction.cache.js'; -import { abstractBinary } from './binary.js'; +import { abstractFloatBinary } from './binary.js'; export const g = makeTestGroup(GPUTest); @@ -32,7 +32,7 @@ Accuracy: Correctly rounded const cases = await d.get(`mat${cols}x${rows}`); await run( t, - abstractBinary('-'), + abstractFloatBinary('-'), [TypeMat(cols, rows, TypeAbstractFloat), TypeMat(cols, rows, TypeAbstractFloat)], TypeMat(cols, rows, TypeAbstractFloat), t.params, diff --git a/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts index 57beaca49ee8..57cf0dbc4768 100644 --- a/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts @@ -8,7 +8,7 @@ import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_multiplication.cache.js'; -import { abstractBinary } from './binary.js'; +import { abstractFloatBinary } from './binary.js'; export const g = makeTestGroup(GPUTest); @@ -25,7 +25,7 @@ Accuracy: Correctly rounded const cases = await d.get('scalar'); await run( t, - abstractBinary('*'), + abstractFloatBinary('*'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -48,7 +48,7 @@ Accuracy: Correctly rounded const cases = await d.get('scalar'); // Using vectorize to generate vector cases based on scalar cases await run( t, - abstractBinary('*'), + abstractFloatBinary('*'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -70,7 +70,7 @@ Accuracy: Correctly rounded const cases = await d.get(`vec${dim}_scalar`); await run( t, - abstractBinary('*'), + abstractFloatBinary('*'), [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], TypeVec(dim, TypeAbstractFloat), t.params, @@ -92,7 +92,7 @@ Accuracy: Correctly rounded const cases = await d.get(`scalar_vec${dim}`); await run( t, - abstractBinary('*'), + abstractFloatBinary('*'), [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], TypeVec(dim, TypeAbstractFloat), t.params, diff --git a/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts b/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts index f53cb781b7cb..31a0991e02c4 100644 --- a/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts @@ -8,7 +8,7 @@ import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_remainder.cache.js'; -import { abstractBinary } from './binary.js'; +import { abstractFloatBinary } from './binary.js'; export const g = makeTestGroup(GPUTest); @@ -25,7 +25,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get('scalar'); await run( t, - abstractBinary('%'), + abstractFloatBinary('%'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -48,7 +48,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get('scalar'); // Using vectorize to generate vector cases based on scalar cases await run( t, - abstractBinary('%'), + abstractFloatBinary('%'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -70,7 +70,7 @@ Accuracy: Correctly rounded const cases = await d.get(`vec${dim}_scalar`); await run( t, - abstractBinary('%'), + abstractFloatBinary('%'), [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], TypeVec(dim, TypeAbstractFloat), t.params, @@ -92,7 +92,7 @@ Accuracy: Correctly rounded const cases = await d.get(`scalar_vec${dim}`); await run( t, - abstractBinary('%'), + abstractFloatBinary('%'), [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], TypeVec(dim, TypeAbstractFloat), t.params, diff --git a/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts index b9e304fa134f..72004cfb1cee 100644 --- a/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts @@ -8,7 +8,7 @@ import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_subtraction.cache.js'; -import { abstractBinary } from './binary.js'; +import { abstractFloatBinary } from './binary.js'; export const g = makeTestGroup(GPUTest); @@ -25,7 +25,7 @@ Accuracy: Correctly rounded const cases = await d.get('scalar'); await run( t, - abstractBinary('-'), + abstractFloatBinary('-'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -48,7 +48,7 @@ Accuracy: Correctly rounded const cases = await d.get('scalar'); // Using vectorize to generate vector cases based on scalar cases await run( t, - abstractBinary('-'), + abstractFloatBinary('-'), [TypeAbstractFloat, TypeAbstractFloat], TypeAbstractFloat, t.params, @@ -70,7 +70,7 @@ Accuracy: Correctly rounded const cases = await d.get(`vec${dim}_scalar`); await run( t, - abstractBinary('-'), + abstractFloatBinary('-'), [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], TypeVec(dim, TypeAbstractFloat), t.params, @@ -92,7 +92,7 @@ Accuracy: Correctly rounded const cases = await d.get(`scalar_vec${dim}`); await run( t, - abstractBinary('-'), + abstractFloatBinary('-'), [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], TypeVec(dim, TypeAbstractFloat), t.params, diff --git a/src/webgpu/shader/execution/expression/binary/ai_arithmetic.cache.ts b/src/webgpu/shader/execution/expression/binary/ai_arithmetic.cache.ts new file mode 100644 index 000000000000..1ebd7646514a --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/ai_arithmetic.cache.ts @@ -0,0 +1,40 @@ +import { kValue } from '../../../../util/constants.js'; +import { sparseI64Range, vectorI64Range } from '../../../../util/math.js'; +import { + generateBinaryToI64Cases, + generateI64VectorBinaryToVectorCases, + generateVectorI64BinaryToVectorCases, +} from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +function ai_add(x: bigint, y: bigint): bigint | undefined { + const result = x + y; + if (result > kValue.i64.positive.max || result < kValue.i64.negative.min) { + return undefined; + } + return result; +} + +export const d = makeCaseCache('binary/ai_arithmetic', { + addition: () => { + return generateBinaryToI64Cases(sparseI64Range(), sparseI64Range(), ai_add); + }, + addition_scalar_vector2: () => { + return generateI64VectorBinaryToVectorCases(sparseI64Range(), vectorI64Range(2), ai_add); + }, + addition_scalar_vector3: () => { + return generateI64VectorBinaryToVectorCases(sparseI64Range(), vectorI64Range(3), ai_add); + }, + addition_scalar_vector4: () => { + return generateI64VectorBinaryToVectorCases(sparseI64Range(), vectorI64Range(4), ai_add); + }, + addition_vector2_scalar: () => { + return generateVectorI64BinaryToVectorCases(vectorI64Range(2), sparseI64Range(), ai_add); + }, + addition_vector3_scalar: () => { + return generateVectorI64BinaryToVectorCases(vectorI64Range(3), sparseI64Range(), ai_add); + }, + addition_vector4_scalar: () => { + return generateVectorI64BinaryToVectorCases(vectorI64Range(4), sparseI64Range(), ai_add); + }, +}); diff --git a/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts new file mode 100644 index 000000000000..241ee80d6a27 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts @@ -0,0 +1,71 @@ +export const description = ` +Execution Tests for the abstract int arithmetic binary expression operations +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../gpu_test.js'; +import { TypeAbstractInt, TypeVec } from '../../../../util/conversion.js'; +import { onlyConstInputSource, run } from '../expression.js'; + +import { d } from './ai_arithmetic.cache.js'; +import { abstractIntBinary } from './binary.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('addition') + .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') + .desc( + ` +Expression: x + y +` + ) + .params(u => + u + .combine('inputSource', onlyConstInputSource) + .combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('addition'); + await run( + t, + abstractIntBinary('+'), + [TypeAbstractInt, TypeAbstractInt], + TypeAbstractInt, + t.params, + cases + ); + }); + +g.test('addition_scalar_vector') + .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr') + .desc( + ` +Expression: x + y +` + ) + .params(u => + u.combine('inputSource', onlyConstInputSource).combine('vectorize_rhs', [2, 3, 4] as const) + ) + .fn(async t => { + const vec_size = t.params.vectorize_rhs; + const vec_type = TypeVec(vec_size, TypeAbstractInt); + const cases = await d.get(`addition_scalar_vector${vec_size}`); + await run(t, abstractIntBinary('+'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + }); + +g.test('addition_vector_scalar') + .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr') + .desc( + ` +Expression: x + y +` + ) + .params(u => + u.combine('inputSource', onlyConstInputSource).combine('vectorize_lhs', [2, 3, 4] as const) + ) + .fn(async t => { + const vec_size = t.params.vectorize_lhs; + const vec_type = TypeVec(vec_size, TypeAbstractInt); + const cases = await d.get(`addition_vector${vec_size}_scalar`); + await run(t, abstractIntBinary('+'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + }); diff --git a/src/webgpu/shader/execution/expression/binary/binary.ts b/src/webgpu/shader/execution/expression/binary/binary.ts index f0b01b839b22..4df0c67d7883 100644 --- a/src/webgpu/shader/execution/expression/binary/binary.ts +++ b/src/webgpu/shader/execution/expression/binary/binary.ts @@ -3,6 +3,7 @@ import { basicExpressionBuilder, compoundAssignmentBuilder, abstractFloatShaderBuilder, + abstractIntShaderBuilder, } from '../expression.js'; /* @returns a ShaderBuilder that evaluates a binary operation */ @@ -16,6 +17,11 @@ export function compoundBinary(op: string): ShaderBuilder { } /* @returns a ShaderBuilder that evaluates a binary operation that returns AbstractFloats */ -export function abstractBinary(op: string): ShaderBuilder { +export function abstractFloatBinary(op: string): ShaderBuilder { return abstractFloatShaderBuilder(values => `(${values.map(v => `(${v})`).join(op)})`); } + +/* @returns a ShaderBuilder that evaluates a binary operation that returns AbstractFloats */ +export function abstractIntBinary(op: string): ShaderBuilder { + return abstractIntShaderBuilder(values => `(${values.map(v => `(${v})`).join(op)})`); +} diff --git a/src/webgpu/shader/execution/expression/case.ts b/src/webgpu/shader/execution/expression/case.ts index 65fd85d77bbc..b765e48eba17 100644 --- a/src/webgpu/shader/execution/expression/case.ts +++ b/src/webgpu/shader/execution/expression/case.ts @@ -1,14 +1,19 @@ import { ROArrayArray } from '../../../../common/util/types.js'; -import { ScalarBuilder, Value, Vector, i32, u32 } from '../../../util/conversion.js'; +import { ScalarBuilder, Value, Vector, i32, u32, abstractInt } from '../../../util/conversion.js'; import { QuantizeFunc, cartesianProduct, quantizeToI32, quantizeToU32, + quantizeToI64, } from '../../../util/math.js'; import { Expectation } from './expectation.js'; +function notUndefined(value: T | undefined): value is T { + return value !== undefined; +} + /** Case is a single expression test case. */ export type Case = { // The input value(s) @@ -21,11 +26,11 @@ export type Case = { export type CaseList = Array; /** - * A function that performs a binary operation on x and y, and returns the expected - * result. + * A function that performs a binary operation on x and y, and returns the + * expected result. */ -export interface BinaryOp { - (x: number, y: number): number | undefined; +export interface BinaryOp { + (x: T, y: T): T | undefined; } /** @@ -36,12 +41,12 @@ export interface BinaryOp { * @param quantize function to quantize all values in vectors and scalars * @param scalarize function to convert numbers to Scalars */ -function makeScalarVectorBinaryToVectorCase( - scalar: number, - vector: readonly number[], - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder +function makeScalarVectorBinaryToVectorCase( + scalar: T, + vector: readonly T[], + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder ): Case | undefined { scalar = quantize(scalar); vector = vector.map(quantize); @@ -51,7 +56,7 @@ function makeScalarVectorBinaryToVectorCase( } return { input: [scalarize(scalar), new Vector(vector.map(scalarize))], - expected: new Vector((result as readonly number[]).map(scalarize)), + expected: new Vector(result.filter(notUndefined).map(scalarize)), }; } @@ -63,23 +68,20 @@ function makeScalarVectorBinaryToVectorCase( * @param quantize function to quantize all values in vectors and scalars * @param scalarize function to convert numbers to Scalars */ -function generateScalarVectorBinaryToVectorCases( - scalars: readonly number[], - vectors: ROArrayArray, - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder +function generateScalarVectorBinaryToVectorCases( + scalars: readonly T[], + vectors: ROArrayArray, + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder ): Case[] { - const cases = new Array(); - scalars.forEach(s => { - vectors.forEach(v => { - const c = makeScalarVectorBinaryToVectorCase(s, v, op, quantize, scalarize); - if (c !== undefined) { - cases.push(c); - } - }); + return scalars.flatMap(s => { + return vectors + .map(v => { + return makeScalarVectorBinaryToVectorCase(s, v, op, quantize, scalarize); + }) + .filter(notUndefined); }); - return cases; } /** @@ -90,12 +92,12 @@ function generateScalarVectorBinaryToVectorCases( * @param quantize function to quantize all values in vectors and scalars * @param scalarize function to convert numbers to Scalars */ -function makeVectorScalarBinaryToVectorCase( - vector: readonly number[], - scalar: number, - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder +function makeVectorScalarBinaryToVectorCase( + vector: readonly T[], + scalar: T, + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder ): Case | undefined { vector = vector.map(quantize); scalar = quantize(scalar); @@ -105,7 +107,7 @@ function makeVectorScalarBinaryToVectorCase( } return { input: [new Vector(vector.map(scalarize)), scalarize(scalar)], - expected: new Vector((result as readonly number[]).map(scalarize)), + expected: new Vector(result.filter(notUndefined).map(scalarize)), }; } @@ -117,23 +119,20 @@ function makeVectorScalarBinaryToVectorCase( * @param quantize function to quantize all values in vectors and scalars * @param scalarize function to convert numbers to Scalars */ -function generateVectorScalarBinaryToVectorCases( - vectors: ROArrayArray, - scalars: readonly number[], - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder +function generateVectorScalarBinaryToVectorCases( + vectors: ROArrayArray, + scalars: readonly T[], + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder ): Case[] { - const cases = new Array(); - scalars.forEach(s => { - vectors.forEach(v => { - const c = makeVectorScalarBinaryToVectorCase(v, s, op, quantize, scalarize); - if (c !== undefined) { - cases.push(c); - } - }); + return scalars.flatMap(s => { + return vectors + .map(v => { + return makeVectorScalarBinaryToVectorCase(v, s, op, quantize, scalarize); + }) + .filter(notUndefined); }); - return cases; } /** @@ -145,7 +144,7 @@ function generateVectorScalarBinaryToVectorCases( export function generateU32VectorBinaryToVectorCases( scalars: readonly number[], vectors: ROArrayArray, - op: BinaryOp + op: BinaryOp ): Case[] { return generateScalarVectorBinaryToVectorCases(scalars, vectors, op, quantizeToU32, u32); } @@ -159,7 +158,7 @@ export function generateU32VectorBinaryToVectorCases( export function generateVectorU32BinaryToVectorCases( vectors: ROArrayArray, scalars: readonly number[], - op: BinaryOp + op: BinaryOp ): Case[] { return generateVectorScalarBinaryToVectorCases(vectors, scalars, op, quantizeToU32, u32); } @@ -173,7 +172,7 @@ export function generateVectorU32BinaryToVectorCases( export function generateI32VectorBinaryToVectorCases( scalars: readonly number[], vectors: ROArrayArray, - op: BinaryOp + op: BinaryOp ): Case[] { return generateScalarVectorBinaryToVectorCases(scalars, vectors, op, quantizeToI32, i32); } @@ -187,11 +186,39 @@ export function generateI32VectorBinaryToVectorCases( export function generateVectorI32BinaryToVectorCases( vectors: ROArrayArray, scalars: readonly number[], - op: BinaryOp + op: BinaryOp ): Case[] { return generateVectorScalarBinaryToVectorCases(vectors, scalars, op, quantizeToI32, i32); } +/** + * @returns array of Case for the input params with op applied + * @param scalars array of scalar params + * @param vectors array of vector params (2, 3, or 4 elements) + * @param op he op to apply to each pair of scalar and vector + */ +export function generateI64VectorBinaryToVectorCases( + scalars: readonly bigint[], + vectors: ROArrayArray, + op: BinaryOp +): Case[] { + return generateScalarVectorBinaryToVectorCases(scalars, vectors, op, quantizeToI64, abstractInt); +} + +/** + * @returns array of Case for the input params with op applied + * @param vectors array of vector params (2, 3, or 4 elements) + * @param scalars array of scalar params + * @param op he op to apply to each pair of vector and scalar + */ +export function generateVectorI64BinaryToVectorCases( + vectors: ROArrayArray, + scalars: readonly bigint[], + op: BinaryOp +): Case[] { + return generateVectorScalarBinaryToVectorCases(vectors, scalars, op, quantizeToI64, abstractInt); +} + /** * @returns array of Case for the input params with op applied * @param param0s array of inputs to try for the first param @@ -200,12 +227,12 @@ export function generateVectorI32BinaryToVectorCases( * @param quantize function to quantize all values * @param scalarize function to convert numbers to Scalars */ -function generateScalarBinaryToScalarCases( - param0s: readonly number[], - param1s: readonly number[], - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder +function generateScalarBinaryToScalarCases( + param0s: readonly T[], + param1s: readonly T[], + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder ): Case[] { param0s = param0s.map(quantize); param1s = param1s.map(quantize); @@ -227,7 +254,7 @@ function generateScalarBinaryToScalarCases( export function generateBinaryToI32Cases( param0s: readonly number[], param1s: readonly number[], - op: BinaryOp + op: BinaryOp ) { return generateScalarBinaryToScalarCases(param0s, param1s, op, quantizeToI32, i32); } @@ -241,7 +268,21 @@ export function generateBinaryToI32Cases( export function generateBinaryToU32Cases( param0s: readonly number[], param1s: readonly number[], - op: BinaryOp + op: BinaryOp ) { return generateScalarBinaryToScalarCases(param0s, param1s, op, quantizeToU32, u32); } + +/** + * @returns an array of Cases for operations over a range of inputs + * @param param0s array of inputs to try for the first param + * @param param1s array of inputs to try for the second param + * @param op callback called on each pair of inputs to produce each case + */ +export function generateBinaryToI64Cases( + param0s: readonly bigint[], + param1s: readonly bigint[], + op: BinaryOp +) { + return generateScalarBinaryToScalarCases(param0s, param1s, op, quantizeToI64, abstractInt); +} diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 91174df74b3d..2d6462451119 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -1032,8 +1032,8 @@ export class Scalar { } } -export interface ScalarBuilder { - (value: number): Scalar; +export interface ScalarBuilder { + (value: T): Scalar; } /** Create a Scalar of `type` by storing `value` as an element of `workingDataArray` and retrieving it. diff --git a/src/webgpu/util/math.ts b/src/webgpu/util/math.ts index 036552847f78..5b04bef963d7 100644 --- a/src/webgpu/util/math.ts +++ b/src/webgpu/util/math.ts @@ -1293,6 +1293,67 @@ export function fullU32Range(count: number = 50): Array { return [0, ...biasedRange(1, kValue.u32.max, count)].map(Math.trunc); } +/** Short list of i64 values of interest to test against */ +const kInterestingI64Values: readonly bigint[] = [ + kValue.i64.negative.max, + kValue.i64.negative.max / 2n, + -256n, + -10n, + -1n, + 0n, + 1n, + 10n, + 256n, + kValue.i64.positive.max / 2n, + kValue.i64.positive.max, +]; + +/** @returns minimal i64 values that cover the entire range of i64 behaviours + * + * This is used instead of fullI64Range when the number of test cases being + * generated is a super linear function of the length of i64 values which is + * leading to time outs. + */ +export function sparseI64Range(): readonly bigint[] { + return kInterestingI64Values; +} + +const kVectorI64Values = { + 2: kInterestingI64Values.flatMap(f => [ + [f, 1n], + [-1n, f], + ]), + 3: kInterestingI64Values.flatMap(f => [ + [f, 1n, -2n], + [-1n, f, 2n], + [1n, -2n, f], + ]), + 4: kInterestingI64Values.flatMap(f => [ + [f, -1n, 2n, 3n], + [1n, f, -2n, 3n], + [1n, 2n, f, -3n], + [-1n, 2n, -3n, f], + ]), +}; + +/** + * Returns set of vectors, indexed by dimension containing interesting i64 + * values. + * + * The tests do not do the simple option for coverage of computing the cartesian + * product of all of the interesting i64 values N times for vecN tests, + * because that creates a huge number of tests for vec3 and vec4, leading to + * time outs. + * + * Instead they insert the interesting i64 values into each location of the + * vector to get a spread of testing over the entire range. This reduces the + * number of cases being run substantially, but maintains coverage. + */ +export function vectorI64Range(dim: number): ROArrayArray { + assert(dim === 2 || dim === 3 || dim === 4, 'vectorI64Range only accepts dimensions 2, 3, and 4'); + return kVectorI64Values[dim]; +} + /** * @returns an ascending sorted array of numbers spread over the entire range of 64-bit signed ints * @@ -2030,8 +2091,8 @@ export function signExtend(n: number, bits: number): number { return (n << shift) >> shift; } -export interface QuantizeFunc { - (num: number): number; +export interface QuantizeFunc { + (num: T): T; } /** @returns the closest 32-bit floating point value to the input */ @@ -2072,6 +2133,19 @@ export function quantizeToU32(num: number): number { return Math.trunc(num); } +/** + * @returns the closest 64-bit signed integer value to the input. + */ +export function quantizeToI64(num: bigint): bigint { + if (num >= kValue.i64.positive.max) { + return kValue.i64.positive.max; + } + if (num <= kValue.i64.negative.min) { + return kValue.i64.negative.min; + } + return num; +} + /** @returns whether the number is an integer and a power of two */ export function isPowerOfTwo(n: number): boolean { if (!Number.isInteger(n)) {