From ac1af685a3ae08e89d22c5497c56cab6453c9ab2 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Thu, 7 Mar 2024 12:58:56 +0000 Subject: [PATCH 01/33] Split the 'Scalar' class into one class per type. Attempting to support all the types in a single class resulted in messy dynamic type dispatching. --- src/resources/cache/hashes.json | 216 ++--- .../cache/webgpu/shader/execution/bitcast.bin | Bin 2221448 -> 2221448 bytes .../expression/call/builtin/bitcast.spec.ts | 72 +- .../shader/execution/expression/case_cache.ts | 4 +- .../execution/expression/expectation.ts | 4 +- src/webgpu/util/compare.ts | 6 +- src/webgpu/util/conversion.ts | 776 +++++++++++++----- 7 files changed, 721 insertions(+), 357 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 61bc0ff73192..8332f3962c10 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "1843098d", - "webgpu/shader/execution/binary/af_logical.bin": "1382526e", - "webgpu/shader/execution/binary/af_division.bin": "e0262a88", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "1e39ddea", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "dd6bc727", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "fb15115", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "d7ba7836", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "5a23b6cc", - "webgpu/shader/execution/binary/af_multiplication.bin": "e6a5c5e4", - "webgpu/shader/execution/binary/af_remainder.bin": "e063382c", - "webgpu/shader/execution/binary/af_subtraction.bin": "d43eb5af", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "2d6a0f7e", - "webgpu/shader/execution/binary/f16_addition.bin": "3346b4bd", - "webgpu/shader/execution/binary/f16_logical.bin": "b0bfe351", - "webgpu/shader/execution/binary/f16_division.bin": "db83c0cc", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "882f40bb", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "cfc4cb6", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "b2344dc1", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "1822ca", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "f5529ab4", - "webgpu/shader/execution/binary/f16_multiplication.bin": "9ea35453", - "webgpu/shader/execution/binary/f16_remainder.bin": "39fe22c1", - "webgpu/shader/execution/binary/f16_subtraction.bin": "d65e2e11", - "webgpu/shader/execution/binary/f32_addition.bin": "e2576a3b", - "webgpu/shader/execution/binary/f32_logical.bin": "a4e17c69", - "webgpu/shader/execution/binary/f32_division.bin": "24a41b78", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "4af13f54", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "ffe9e80b", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "1eb1789c", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "e2694b78", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "bbf90", - "webgpu/shader/execution/binary/f32_multiplication.bin": "63f63b02", - "webgpu/shader/execution/binary/f32_remainder.bin": "6a44daa2", - "webgpu/shader/execution/binary/f32_subtraction.bin": "a6240f28", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "6fee3e4e", - "webgpu/shader/execution/binary/i32_comparison.bin": "788881cd", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "23da1ed8", - "webgpu/shader/execution/binary/u32_comparison.bin": "8ab2e08c", - "webgpu/shader/execution/abs.bin": "19017bb1", - "webgpu/shader/execution/acos.bin": "9dd5c222", - "webgpu/shader/execution/acosh.bin": "c5b7a2b9", - "webgpu/shader/execution/asin.bin": "998df766", - "webgpu/shader/execution/asinh.bin": "ace06d5a", - "webgpu/shader/execution/atan.bin": "b94c5065", - "webgpu/shader/execution/atan2.bin": "a2c19388", - "webgpu/shader/execution/atanh.bin": "f726651b", - "webgpu/shader/execution/bitcast.bin": "81370da9", - "webgpu/shader/execution/ceil.bin": "f6fda5fa", - "webgpu/shader/execution/clamp.bin": "47a17327", - "webgpu/shader/execution/cos.bin": "6b63dc60", - "webgpu/shader/execution/cosh.bin": "8fe08827", - "webgpu/shader/execution/cross.bin": "2ef763e5", - "webgpu/shader/execution/degrees.bin": "c30f7423", - "webgpu/shader/execution/determinant.bin": "4dedfa96", - "webgpu/shader/execution/distance.bin": "9b71fb94", - "webgpu/shader/execution/dot.bin": "db15b119", - "webgpu/shader/execution/exp.bin": "65c5d3ab", - "webgpu/shader/execution/exp2.bin": "6e7766a1", - "webgpu/shader/execution/faceForward.bin": "282fbba6", - "webgpu/shader/execution/floor.bin": "dce512dd", - "webgpu/shader/execution/fma.bin": "21d0a25e", - "webgpu/shader/execution/fract.bin": "82f96adc", - "webgpu/shader/execution/frexp.bin": "a41c87a4", - "webgpu/shader/execution/inverseSqrt.bin": "ec953eb3", - "webgpu/shader/execution/ldexp.bin": "fcfd6224", - "webgpu/shader/execution/length.bin": "4da1998f", - "webgpu/shader/execution/log.bin": "d99e6690", - "webgpu/shader/execution/log2.bin": "4f7644b3", - "webgpu/shader/execution/max.bin": "f8bc9388", - "webgpu/shader/execution/min.bin": "7b1b016c", - "webgpu/shader/execution/mix.bin": "7b257d9c", - "webgpu/shader/execution/modf.bin": "fa632cd1", - "webgpu/shader/execution/normalize.bin": "ace99215", - "webgpu/shader/execution/pack2x16float.bin": "6ef332cb", - "webgpu/shader/execution/pow.bin": "7c042e04", - "webgpu/shader/execution/quantizeToF16.bin": "7fd41be3", - "webgpu/shader/execution/radians.bin": "3e3aae03", - "webgpu/shader/execution/reflect.bin": "c75fc3e0", - "webgpu/shader/execution/refract.bin": "8455f497", - "webgpu/shader/execution/round.bin": "95773d03", - "webgpu/shader/execution/saturate.bin": "e2d02ac5", - "webgpu/shader/execution/sign.bin": "9c56fda6", - "webgpu/shader/execution/sin.bin": "7256a0d6", - "webgpu/shader/execution/sinh.bin": "593345a9", - "webgpu/shader/execution/smoothstep.bin": "5064d00b", - "webgpu/shader/execution/sqrt.bin": "601a4fbc", - "webgpu/shader/execution/step.bin": "5596f86a", - "webgpu/shader/execution/tan.bin": "ba7975c1", - "webgpu/shader/execution/tanh.bin": "cac48441", - "webgpu/shader/execution/transpose.bin": "e5d5b2a9", - "webgpu/shader/execution/trunc.bin": "95811ca", - "webgpu/shader/execution/unpack2x16float.bin": "9995d9a7", - "webgpu/shader/execution/unpack2x16snorm.bin": "a4c1d0b4", - "webgpu/shader/execution/unpack2x16unorm.bin": "7b130e1f", - "webgpu/shader/execution/unpack4x8snorm.bin": "9879686e", - "webgpu/shader/execution/unpack4x8unorm.bin": "727028f2", - "webgpu/shader/execution/unary/af_arithmetic.bin": "4894a2dc", - "webgpu/shader/execution/unary/af_assignment.bin": "b4c538d5", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "3272cb52", - "webgpu/shader/execution/unary/ai_assignment.bin": "c2877676", - "webgpu/shader/execution/unary/bool_conversion.bin": "97ef95b3", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "86b56801", - "webgpu/shader/execution/unary/f16_conversion.bin": "5cf66fac", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "c7b8416", - "webgpu/shader/execution/unary/f32_conversion.bin": "b0af00cd", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "b2d016ce", - "webgpu/shader/execution/unary/i32_conversion.bin": "ca9cd82d", - "webgpu/shader/execution/unary/u32_conversion.bin": "40797b79" + "webgpu/shader/execution/binary/af_addition.bin": "cfc28768", + "webgpu/shader/execution/binary/af_logical.bin": "ae9b9ba0", + "webgpu/shader/execution/binary/af_division.bin": "30839274", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "e43d31a5", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "fbec81c8", + "webgpu/shader/execution/binary/af_multiplication.bin": "19681c0c", + "webgpu/shader/execution/binary/af_remainder.bin": "fb9c916", + "webgpu/shader/execution/binary/af_subtraction.bin": "6f448b72", + "webgpu/shader/execution/binary/f16_addition.bin": "83ac7e89", + "webgpu/shader/execution/binary/f16_logical.bin": "6c9cdfe8", + "webgpu/shader/execution/binary/f16_division.bin": "487729fe", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "54aa39b", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "3a4159f8", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "fceaaf1f", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "92151406", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "d920f7ec", + "webgpu/shader/execution/binary/f16_multiplication.bin": "fc8c2a0f", + "webgpu/shader/execution/binary/f16_remainder.bin": "6e60c5d0", + "webgpu/shader/execution/binary/f16_subtraction.bin": "4277226e", + "webgpu/shader/execution/binary/f32_addition.bin": "5b9c4270", + "webgpu/shader/execution/binary/f32_logical.bin": "32188047", + "webgpu/shader/execution/binary/f32_division.bin": "22edca65", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "62107e07", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "9e5677c9", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "d69476cb", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c429b29b", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "ded0240e", + "webgpu/shader/execution/binary/f32_multiplication.bin": "f088c036", + "webgpu/shader/execution/binary/f32_remainder.bin": "dcb44e72", + "webgpu/shader/execution/binary/f32_subtraction.bin": "dab334aa", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "2b48d275", + "webgpu/shader/execution/binary/i32_comparison.bin": "f2c3cd37", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "7ec7b66", + "webgpu/shader/execution/binary/u32_comparison.bin": "50ab2f61", + "webgpu/shader/execution/abs.bin": "e37fc25a", + "webgpu/shader/execution/acos.bin": "4dc16324", + "webgpu/shader/execution/acosh.bin": "1f999f3c", + "webgpu/shader/execution/asin.bin": "a653cf5b", + "webgpu/shader/execution/asinh.bin": "515e83f7", + "webgpu/shader/execution/atan.bin": "95006a05", + "webgpu/shader/execution/atan2.bin": "3cc4efac", + "webgpu/shader/execution/atanh.bin": "e2985906", + "webgpu/shader/execution/bitcast.bin": "bda67f6d", + "webgpu/shader/execution/ceil.bin": "5d285a4", + "webgpu/shader/execution/clamp.bin": "cd0271cf", + "webgpu/shader/execution/cos.bin": "dfd7be1f", + "webgpu/shader/execution/cosh.bin": "8a9b86a5", + "webgpu/shader/execution/cross.bin": "80fe94ce", + "webgpu/shader/execution/degrees.bin": "d9a6c350", + "webgpu/shader/execution/determinant.bin": "2e19c9bd", + "webgpu/shader/execution/distance.bin": "64daad1a", + "webgpu/shader/execution/dot.bin": "c78ce327", + "webgpu/shader/execution/exp.bin": "c1b5a4fd", + "webgpu/shader/execution/exp2.bin": "63ac077", + "webgpu/shader/execution/faceForward.bin": "92b334a6", + "webgpu/shader/execution/floor.bin": "7e1bf2c3", + "webgpu/shader/execution/fma.bin": "701a86c6", + "webgpu/shader/execution/fract.bin": "c29aadce", + "webgpu/shader/execution/frexp.bin": "5ae6ca4", + "webgpu/shader/execution/inverseSqrt.bin": "9563f18d", + "webgpu/shader/execution/ldexp.bin": "f005b4b5", + "webgpu/shader/execution/length.bin": "ee8e5573", + "webgpu/shader/execution/log.bin": "29c4187b", + "webgpu/shader/execution/log2.bin": "9ce20393", + "webgpu/shader/execution/max.bin": "7516dbff", + "webgpu/shader/execution/min.bin": "75e9d526", + "webgpu/shader/execution/mix.bin": "b224306a", + "webgpu/shader/execution/modf.bin": "ba63df78", + "webgpu/shader/execution/normalize.bin": "9842b756", + "webgpu/shader/execution/pack2x16float.bin": "5dee8700", + "webgpu/shader/execution/pow.bin": "81bac0a9", + "webgpu/shader/execution/quantizeToF16.bin": "6973d05d", + "webgpu/shader/execution/radians.bin": "128a0639", + "webgpu/shader/execution/reflect.bin": "5de2177e", + "webgpu/shader/execution/refract.bin": "de1e1ade", + "webgpu/shader/execution/round.bin": "70a9da36", + "webgpu/shader/execution/saturate.bin": "e8daa151", + "webgpu/shader/execution/sign.bin": "503847e9", + "webgpu/shader/execution/sin.bin": "51df6447", + "webgpu/shader/execution/sinh.bin": "dbc469f3", + "webgpu/shader/execution/smoothstep.bin": "16cdfbe9", + "webgpu/shader/execution/sqrt.bin": "b59d4606", + "webgpu/shader/execution/step.bin": "5df4b04b", + "webgpu/shader/execution/tan.bin": "f1535f0c", + "webgpu/shader/execution/tanh.bin": "6e653c6d", + "webgpu/shader/execution/transpose.bin": "41ff2823", + "webgpu/shader/execution/trunc.bin": "7c3baf7", + "webgpu/shader/execution/unpack2x16float.bin": "f994910", + "webgpu/shader/execution/unpack2x16snorm.bin": "df36ee6f", + "webgpu/shader/execution/unpack2x16unorm.bin": "ac195462", + "webgpu/shader/execution/unpack4x8snorm.bin": "39a4743a", + "webgpu/shader/execution/unpack4x8unorm.bin": "ecae6fe5", + "webgpu/shader/execution/unary/af_arithmetic.bin": "9800df7f", + "webgpu/shader/execution/unary/af_assignment.bin": "85c4d516", + "webgpu/shader/execution/unary/bool_conversion.bin": "85664e85", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "3e0dea1", + "webgpu/shader/execution/unary/f16_conversion.bin": "c1f37dd1", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "f2cd5bd2", + "webgpu/shader/execution/unary/f32_conversion.bin": "b4174de", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "b327298b", + "webgpu/shader/execution/unary/i32_conversion.bin": "ebdc408a", + "webgpu/shader/execution/unary/u32_conversion.bin": "aafe3d79", + "webgpu/shader/execution/unary/ai_assignment.bin": "ef0b469b", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "b22511b9", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "67c3d3b6", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "b72f2c53", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "7f328797", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "b34a4a2b" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/bitcast.bin b/src/resources/cache/webgpu/shader/execution/bitcast.bin index 3c24c88e1ffbc42f87639912bacab411154e6d63..d3954903ac89afcfbe5049a356005ed02cf10aae 100644 GIT binary patch delta 152 zcmWN=%MpSw6hP4l3d%o-f*(*Rf!897WKSO%h7G$EpiObl%&~u?EEgxU8};PHcu@>^ r;E^FCo_J==gef!TELc*r;)OM@ys=@+J0E=V#g0AS{P4SZ8Gh>y9V9$( delta 151 zcmWN=M-6~L006yPYE?jRVw)gr0)tx&a diff --git a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts index 5e7add44048d..4c1a8aa618e5 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts @@ -522,24 +522,24 @@ g.test('af_to_i32') 1, 10, 256, - u32Bits(0b11111111011111111111111111111111).value as number, - u32Bits(0b11111111010000000000000000000000).value as number, - u32Bits(0b11111110110000000000000000000000).value as number, - u32Bits(0b11111101110000000000000000000000).value as number, - u32Bits(0b11111011110000000000000000000000).value as number, - u32Bits(0b11110111110000000000000000000000).value as number, - u32Bits(0b11101111110000000000000000000000).value as number, - u32Bits(0b11011111110000000000000000000000).value as number, - u32Bits(0b10111111110000000000000000000000).value as number, - u32Bits(0b01111111011111111111111111111111).value as number, - u32Bits(0b01111111010000000000000000000000).value as number, - u32Bits(0b01111110110000000000000000000000).value as number, - u32Bits(0b01111101110000000000000000000000).value as number, - u32Bits(0b01111011110000000000000000000000).value as number, - u32Bits(0b01110111110000000000000000000000).value as number, - u32Bits(0b01101111110000000000000000000000).value as number, - u32Bits(0b01011111110000000000000000000000).value as number, - u32Bits(0b00111111110000000000000000000000).value as number, + 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 => { @@ -566,24 +566,24 @@ g.test('af_to_u32') 1, 10, 256, - u32Bits(0b11111111011111111111111111111111).value as number, - u32Bits(0b11111111010000000000000000000000).value as number, - u32Bits(0b11111110110000000000000000000000).value as number, - u32Bits(0b11111101110000000000000000000000).value as number, - u32Bits(0b11111011110000000000000000000000).value as number, - u32Bits(0b11110111110000000000000000000000).value as number, - u32Bits(0b11101111110000000000000000000000).value as number, - u32Bits(0b11011111110000000000000000000000).value as number, - u32Bits(0b10111111110000000000000000000000).value as number, - u32Bits(0b01111111011111111111111111111111).value as number, - u32Bits(0b01111111010000000000000000000000).value as number, - u32Bits(0b01111110110000000000000000000000).value as number, - u32Bits(0b01111101110000000000000000000000).value as number, - u32Bits(0b01111011110000000000000000000000).value as number, - u32Bits(0b01110111110000000000000000000000).value as number, - u32Bits(0b01101111110000000000000000000000).value as number, - u32Bits(0b01011111110000000000000000000000).value as number, - u32Bits(0b00111111110000000000000000000000).value as number, + 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 => { diff --git a/src/webgpu/shader/execution/expression/case_cache.ts b/src/webgpu/shader/execution/expression/case_cache.ts index 1991c78217e9..4321475bf83a 100644 --- a/src/webgpu/shader/execution/expression/case_cache.ts +++ b/src/webgpu/shader/execution/expression/case_cache.ts @@ -4,10 +4,10 @@ import BinaryStream from '../../../util/binary_stream.js'; import { deserializeComparator, serializeComparator } from '../../../util/compare.js'; import { Matrix, - Scalar, Value, Vector, deserializeValue, + isScalar, serializeValue, } from '../../../util/conversion.js'; import { @@ -31,7 +31,7 @@ enum SerializedExpectationKind { /** serializeExpectation() serializes an Expectation to a BinaryStream */ export function serializeExpectation(s: BinaryStream, e: Expectation) { - if (e instanceof Scalar || e instanceof Vector || e instanceof Matrix) { + if (isScalar(e) || e instanceof Vector || e instanceof Matrix) { s.writeU8(SerializedExpectationKind.Value); serializeValue(s, e); return; diff --git a/src/webgpu/shader/execution/expression/expectation.ts b/src/webgpu/shader/execution/expression/expectation.ts index 8078673f2569..e56577580c52 100644 --- a/src/webgpu/shader/execution/expression/expectation.ts +++ b/src/webgpu/shader/execution/expression/expectation.ts @@ -1,6 +1,6 @@ import { ROArrayArray } from '../../../../common/util/types.js'; import { Comparator, compare } from '../../../util/compare.js'; -import { Matrix, Scalar, Value, Vector } from '../../../util/conversion.js'; +import { Matrix, Value, Vector, isScalar } from '../../../util/conversion.js'; import { FPInterval } from '../../../util/floating_point.js'; export type Expectation = @@ -14,7 +14,7 @@ export type Expectation = export function isComparator(e: Expectation): e is Comparator { return !( e instanceof FPInterval || - e instanceof Scalar || + isScalar(e) || e instanceof Vector || e instanceof Matrix || e instanceof Array diff --git a/src/webgpu/util/compare.ts b/src/webgpu/util/compare.ts index 95573e6ceae2..d9cc693184f1 100644 --- a/src/webgpu/util/compare.ts +++ b/src/webgpu/util/compare.ts @@ -8,7 +8,7 @@ import { import { Expectation, toComparator } from '../shader/execution/expression/expectation.js'; import BinaryStream from './binary_stream.js'; -import { isFloatValue, Matrix, Scalar, Value, Vector } from './conversion.js'; +import { isFloatValue, isScalar, Matrix, Scalar, Value, Vector } from './conversion.js'; import { FPInterval } from './floating_point.js'; /** Comparison describes the result of a Comparator function. */ @@ -98,7 +98,7 @@ function compareValue(got: Value, expected: Value): Comparison { } } - if (got instanceof Scalar) { + if (isScalar(got)) { const g = got; const e = expected as Scalar; const isFloat = g.type.kind === 'f64' || g.type.kind === 'f32' || g.type.kind === 'f16'; @@ -175,7 +175,7 @@ function compareInterval(got: Value, expected: FPInterval): Comparison { } } - if (got instanceof Scalar) { + if (isScalar(got)) { const g = got.value as number; const matched = expected.contains(g); return { diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 8599e2335869..31ef330d50a5 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -800,44 +800,44 @@ function valueFromBytes( return workingDataOut[0] as ArrayElementType; } -export const TypeAbstractInt = new ScalarType( +export const TypeAbstractInt: ScalarType = new ScalarType( 'abstract-int', 8, (buf: Uint8Array, offset: number) => abstractInt(valueFromBytes(workingDataI64, buf, offset)) ); -export const TypeI32 = new ScalarType('i32', 4, (buf: Uint8Array, offset: number) => +export const TypeI32: ScalarType = new ScalarType('i32', 4, (buf: Uint8Array, offset: number) => i32(valueFromBytes(workingDataI32, buf, offset)) ); -export const TypeU32 = new ScalarType('u32', 4, (buf: Uint8Array, offset: number) => +export const TypeU32: ScalarType = new ScalarType('u32', 4, (buf: Uint8Array, offset: number) => u32(valueFromBytes(workingDataU32, buf, offset)) ); -export const TypeAbstractFloat = new ScalarType( +export const TypeI16: ScalarType = new ScalarType('i16', 2, (buf: Uint8Array, offset: number) => + i16(valueFromBytes(workingDataI16, buf, offset)) +); +export const TypeU16: ScalarType = new ScalarType('u16', 2, (buf: Uint8Array, offset: number) => + u16(valueFromBytes(workingDataU16, buf, offset)) +); +export const TypeI8: ScalarType = new ScalarType('i8', 1, (buf: Uint8Array, offset: number) => + i8(valueFromBytes(workingDataI8, buf, offset)) +); +export const TypeU8: ScalarType = new ScalarType('u8', 1, (buf: Uint8Array, offset: number) => + u8(valueFromBytes(workingDataU8, buf, offset)) +); +export const TypeAbstractFloat: ScalarType = new ScalarType( 'abstract-float', 8, (buf: Uint8Array, offset: number) => abstractFloat(valueFromBytes(workingDataF64, buf, offset)) ); -export const TypeF64 = new ScalarType('f64', 8, (buf: Uint8Array, offset: number) => +export const TypeF64: ScalarType = new ScalarType('f64', 8, (buf: Uint8Array, offset: number) => f64(valueFromBytes(workingDataF64, buf, offset)) ); -export const TypeF32 = new ScalarType('f32', 4, (buf: Uint8Array, offset: number) => +export const TypeF32: ScalarType = new ScalarType('f32', 4, (buf: Uint8Array, offset: number) => f32(valueFromBytes(workingDataF32, buf, offset)) ); -export const TypeI16 = new ScalarType('i16', 2, (buf: Uint8Array, offset: number) => - i16(valueFromBytes(workingDataI16, buf, offset)) -); -export const TypeU16 = new ScalarType('u16', 2, (buf: Uint8Array, offset: number) => - u16(valueFromBytes(workingDataU16, buf, offset)) -); -export const TypeF16 = new ScalarType('f16', 2, (buf: Uint8Array, offset: number) => +export const TypeF16: ScalarType = new ScalarType('f16', 2, (buf: Uint8Array, offset: number) => f16Bits(valueFromBytes(workingDataU16, buf, offset)) ); -export const TypeI8 = new ScalarType('i8', 1, (buf: Uint8Array, offset: number) => - i8(valueFromBytes(workingDataI8, buf, offset)) -); -export const TypeU8 = new ScalarType('u8', 1, (buf: Uint8Array, offset: number) => - u8(valueFromBytes(workingDataU8, buf, offset)) -); -export const TypeBool = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => +export const TypeBool: ScalarType = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => bool(valueFromBytes(workingDataU32, buf, offset) !== 0) ); @@ -887,7 +887,7 @@ export function numElementsOf(ty: Type): number { /** @returns the scalar elements of the given Value */ export function elementsOf(value: Value): Scalar[] { - if (value instanceof Scalar) { + if (isScalar(value)) { return [value]; } if (value instanceof Vector) { @@ -913,24 +913,34 @@ export function scalarTypeOf(ty: Type): ScalarType { throw new Error(`unhandled type ${ty}`); } -/** ScalarValue is the JS type that can be held by a Scalar */ -type ScalarValue = boolean | number | bigint; +function hex(sizeInBytes: number, bitsLow: number, bitsHigh?: number) { + let hex = ''; + workingDataU32[0] = bitsLow; + if (bitsHigh !== undefined) { + workingDataU32[1] = bitsHigh; + } + for (let i = 0; i < sizeInBytes; ++i) { + hex = workingDataU8[i].toString(16).padStart(2, '0') + hex; + } + return `0x${hex}`; +} -/** Class that encapsulates a single scalar value of various types. */ -export class Scalar { - readonly value: ScalarValue; // The scalar value - readonly type: ScalarType; // The type of the scalar +function withPoint(x: number) { + const str = `${x}`; + return str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; +} - // The scalar value, packed in one or two 32-bit unsigned integers. - // Whether or not the bits1 is used depends on `this.type.size`. - readonly bits1: number; - readonly bits0: number; +/** Class that encapsulates a single abstract-int value. */ +export class AbstractIntValue { + readonly value: bigint; // The abstract-integer value + readonly bitsLow: number; // The low 32 bits of the abstract-integer value. + readonly bitsHigh: number; // The high 32 bits of the abstract-integer value. + readonly type = TypeAbstractInt; // The type of the value. - public constructor(type: ScalarType, value: ScalarValue, bits1: number, bits0: number) { + public constructor(value: bigint, bitsLow: number, bitsHigh: number) { this.value = value; - this.type = type; - this.bits1 = bits1; - this.bits0 = bits0; + this.bitsLow = bitsLow; + this.bitsHigh = bitsHigh; } /** @@ -939,229 +949,583 @@ export class Scalar { * @param offset the offset in buffer, in units of `buffer` */ public copyTo(buffer: TypedArrayBufferView, offset: number) { - assert( - this.type.kind !== 'abstract-int', - `Copying 'abstract-int' values to/from buffers is yet implemented` - ); - assert(this.type.kind !== 'f64', `Copying f64 values to/from buffers is not defined`); - workingDataU32[1] = this.bits1; - workingDataU32[0] = this.bits0; - for (let i = 0; i < this.type.size; i++) { + workingDataU32[0] = this.bitsLow; + workingDataU32[1] = this.bitsHigh; + for (let i = 0; i < 8; i++) { buffer[offset + i] = workingDataU8[i]; } } + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + // WGSL parses negative numbers as a negated positive. + // This means '-9223372036854775808' parses as `-' & '9223372036854775808', so must be written as + // '(-9223372036854775807 - 1)' in WGSL, because '9223372036854775808' is not a valid AbstractInt. + if (this.value === -9223372036854775808n) { + return `(-9223372036854775807 - 1)`; + } + return `${this.value}`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(8, this.bitsLow, this.bitsHigh)})`; + } +} + +/** Class that encapsulates a single abstract-float value. */ +export class AbstractFloatValue { + readonly value: number; // The f32 value + readonly bitsLow: number; // The low 32 bits of the abstract-float value. + readonly bitsHigh: number; // The high 32 bits of the abstract-float value. + readonly type = TypeAbstractFloat; // The type of the value. + + public constructor(value: number, bitsLow: number, bitsHigh: number) { + this.value = value; + this.bitsLow = bitsLow; + this.bitsHigh = bitsHigh; + } + /** - * @returns the WGSL representation of this scalar value + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.bitsLow; + workingDataU32[1] = this.bitsHigh; + for (let i = 0; i < 8; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ public wgsl(): string { - const withPoint = (x: number) => { - const str = `${x}`; - return str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; - }; + return `${withPoint(this.value)}`; + } - switch (typeof this.value) { - case 'bigint': - if (this.type.kind === 'abstract-int') { - // WGSL parses negative numbers as a negated positive. - // This means '-9223372036854775808' parses as `-' & - // '9223372036854775808', so must be written as - // '(-9223372036854775807 - 1)' in WGSL, because '9223372036854775808' - // is not a valid AbstractInt. - if (this.value === -9223372036854775808n) { - return `(-9223372036854775807 - 1)`; - } - return `${this.value}`; - } - break; - case 'number': - if (!isFinite(this.value)) break; - switch (this.type.kind) { - case 'abstract-float': - return `${withPoint(this.value)}`; - case 'f64': - return `${withPoint(this.value)}`; - case 'f32': - return `${withPoint(this.value)}f`; - case 'f16': - return `${withPoint(this.value)}h`; - case 'u32': - return `${this.value}u`; - case 'i32': - return `i32(${this.value})`; - } - break; - case 'boolean': - return `${this.value}`; + public toString(): string { + switch (this.value) { + case Infinity: + case -Infinity: + return Colors.bold(this.value.toString()); + default: { + let str = this.value.toString(); + str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; + return isSubnormalNumberF64(this.value.valueOf()) + ? `${Colors.bold(str)} (${hex(8, this.bitsLow, this.bitsHigh)} subnormal)` + : `${Colors.bold(str)} (${hex(8, this.bitsLow, this.bitsHigh)})`; + } + } + } +} + +/** Class that encapsulates a single i32 value. */ +export class I32Value { + readonly value: number; // The i32 value + readonly bits: number; // The i32 value, bitcast to a 32-bit integer. + readonly type = TypeI32; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.bits; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; } + } - throw new Error( - `scalar of value ${this.value} and type ${this.type} has no WGSL representation` - ); + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `i32(${this.value})`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(4, this.bits)})`; + } +} + +/** Class that encapsulates a single u32 value. */ +export class U32Value { + readonly value: number; // The u32 value + readonly type = TypeU32; // The type of the value. + + public constructor(value: number) { + this.value = value; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.value; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `${this.value}u`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(4, this.value)})`; + } +} + +/** + * Class that encapsulates a single i16 value. + * @note type does not exist in WGSL yet + */ +export class I16Value { + readonly value: number; // The i16 value + readonly bits: number; // The i16 value, bitcast to a 16-bit integer. + readonly type = TypeI16; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU16[0] = this.bits; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `i16(${this.value})`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(2, this.bits)})`; + } +} + +/** + * Class that encapsulates a single u16 value. + * @note type does not exist in WGSL yet + */ +export class U16Value { + readonly value: number; // The u16 value + readonly type = TypeU16; // The type of the value. + + public constructor(value: number) { + this.value = value; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU16[0] = this.value; + for (let i = 0; i < 2; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + assert(false, 'u16 is not a WGSL type'); + return `u16(${this.value})`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(2, this.value)})`; + } +} + +/** + * Class that encapsulates a single i8 value. + * @note type does not exist in WGSL yet + */ +export class I8Value { + readonly value: number; // The i8 value + readonly bits: number; // The i8 value, bitcast to a 8-bit integer. + readonly type = TypeI8; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU8[0] = this.bits; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `i8(${this.value})`; } public toString(): string { - if (this.type.kind === 'bool') { - return Colors.bold(this.value.toString()); + return `${Colors.bold(this.value.toString())} (${hex(2, this.bits)})`; + } +} + +/** + * Class that encapsulates a single u8 value. + * @note type does not exist in WGSL yet + */ +export class U8Value { + readonly value: number; // The u8 value + readonly type = TypeU8; // The type of the value. + + public constructor(value: number) { + this.value = value; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU8[0] = this.value; + for (let i = 0; i < 2; i++) { + buffer[offset + i] = workingDataU8[i]; } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + assert(false, 'u8 is not a WGSL type'); + return `u8(${this.value})`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(2, this.value)})`; + } +} + +/** + * Class that encapsulates a single f64 value + * @note type does not exist in WGSL yet + */ +export class F64Value { + readonly value: number; // The f32 value + readonly bitsLow: number; // The low 32 bits of the abstract-float value. + readonly bitsHigh: number; // The high 32 bits of the abstract-float value. + readonly type = TypeF64; // The type of the value. + + public constructor(value: number, bitsLow: number, bitsHigh: number) { + this.value = value; + this.bitsLow = bitsLow; + this.bitsHigh = bitsHigh; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.bitsLow; + workingDataU32[1] = this.bitsHigh; + for (let i = 0; i < 8; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + assert(false, 'f64 is not a WGSL type'); + return `${withPoint(this.value)}`; + } + + public toString(): string { switch (this.value) { case Infinity: case -Infinity: return Colors.bold(this.value.toString()); default: { - workingDataU32[1] = this.bits1; - workingDataU32[0] = this.bits0; - let hex = ''; - for (let i = 0; i < this.type.size; ++i) { - hex = workingDataU8[i].toString(16).padStart(2, '0') + hex; - } - const n = this.value as Number; - if (n !== null && isFloatValue(this)) { - let str = this.value.toString(); - str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; - switch (this.type.kind) { - case 'abstract-float': - return isSubnormalNumberF64(n.valueOf()) - ? `${Colors.bold(str)} (0x${hex} subnormal)` - : `${Colors.bold(str)} (0x${hex})`; - case 'f64': - return isSubnormalNumberF64(n.valueOf()) - ? `${Colors.bold(str)} (0x${hex} subnormal)` - : `${Colors.bold(str)} (0x${hex})`; - case 'f32': - return isSubnormalNumberF32(n.valueOf()) - ? `${Colors.bold(str)} (0x${hex} subnormal)` - : `${Colors.bold(str)} (0x${hex})`; - case 'f16': - return isSubnormalNumberF16(n.valueOf()) - ? `${Colors.bold(str)} (0x${hex} subnormal)` - : `${Colors.bold(str)} (0x${hex})`; - default: - unreachable( - `Printing of floating point kind ${this.type.kind} is not implemented...` - ); - } - } - return `${Colors.bold(this.value.toString())} (0x${hex})`; + let str = this.value.toString(); + str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; + return isSubnormalNumberF64(this.value.valueOf()) + ? `${Colors.bold(str)} (${hex(8, this.bitsLow, this.bitsHigh)} subnormal)` + : `${Colors.bold(str)} (${hex(8, this.bitsLow, this.bitsHigh)})`; } } } } -export interface ScalarBuilder { - (value: T): Scalar; +/** Class that encapsulates a single f32 value. */ +export class F32Value { + readonly value: number; // The f32 value + readonly bits: number; // The f32 value, bitcast to a 32-bit integer. + readonly type = TypeF32; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.bits; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `${withPoint(this.value)}f`; + } + + public toString(): string { + switch (this.value) { + case Infinity: + case -Infinity: + return Colors.bold(this.value.toString()); + default: { + let str = this.value.toString(); + str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; + return isSubnormalNumberF32(this.value.valueOf()) + ? `${Colors.bold(str)} (${hex(4, this.bits)} subnormal)` + : `${Colors.bold(str)} (${hex(4, this.bits)})`; + } + } + } } -/** Create a Scalar of `type` by storing `value` as an element of `workingDataArray` and retrieving it. - * The working data array *must* be an alias of `workingData`. - */ -function scalarFromValue( - type: ScalarType, - workingDataArray: A, - value: ArrayElementType -): Scalar { - // Clear all bits of the working data since `value` may be smaller; the upper bits should be 0. - workingDataU32[1] = 0; - workingDataU32[0] = 0; - workingDataArray[0] = value; - return new Scalar(type, workingDataArray[0], workingDataU32[1], workingDataU32[0]); -} - -/** Create a Scalar of `type` by storing `value` as an element of `workingDataStoreArray` and - * reinterpreting it as an element of `workingDataLoadArray`. - * Both working data arrays *must* be aliases of `workingData`. - */ -function scalarFromBits( - type: ScalarType, - workingDataStoreArray: A, - workingDataLoadArray: TypedArrayBufferView, - bits: ArrayElementType -): Scalar { - // Clear all bits of the working data since `value` may be smaller; the upper bits should be 0. - workingDataU32[1] = 0; - workingDataU32[0] = 0; - workingDataStoreArray[0] = bits; - return new Scalar(type, workingDataLoadArray[0], workingDataU32[1], workingDataU32[0]); +/** Class that encapsulates a single f16 value. */ +export class F16Value { + readonly value: number; // The f16 value + readonly bits: number; // The f16 value, bitcast to a 16-bit integer. + readonly type = TypeF16; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU16[0] = this.bits; + for (let i = 0; i < 2; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `${withPoint(this.value)}h`; + } + + public toString(): string { + switch (this.value) { + case Infinity: + case -Infinity: + return Colors.bold(this.value.toString()); + default: { + let str = this.value.toString(); + str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; + return isSubnormalNumberF16(this.value.valueOf()) + ? `${Colors.bold(str)} (${hex(2, this.bits)} subnormal)` + : `${Colors.bold(str)} (${hex(2, this.bits)})`; + } + } + } } +/** Class that encapsulates a single bool value. */ +export class BoolValue { + readonly value: boolean; // The bool value + readonly type = TypeBool; // The type of the value. -/** Create an AbstractFloat from a numeric value, a JS `number`. */ -export const abstractFloat = (value: number): Scalar => - scalarFromValue(TypeAbstractFloat, workingDataF64, value); + public constructor(value: boolean) { + this.value = value; + } -/** Create an f64 from a numeric value, a JS `number`. */ -export const f64 = (value: number): Scalar => scalarFromValue(TypeF64, workingDataF64, value); + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + buffer[offset] = this.value ? 1 : 0; + } -/** Create an f32 from a numeric value, a JS `number`. */ -export const f32 = (value: number): Scalar => scalarFromValue(TypeF32, workingDataF32, value); + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return this.value.toString(); + } -/** Create an f16 from a numeric value, a JS `number`. */ -export const f16 = (value: number): Scalar => scalarFromValue(TypeF16, workingDataF16, value); + public toString(): string { + return Colors.bold(this.value.toString()); + } +} -/** Create an f32 from a bit representation, a uint32 represented as a JS `number`. */ -export const f32Bits = (bits: number): Scalar => - scalarFromBits(TypeF32, workingDataU32, workingDataF32, bits); +/** Scalar represents all the scalar value types */ +export type Scalar = + | AbstractIntValue + | AbstractFloatValue + | I32Value + | U32Value + | I16Value + | U16Value + | I8Value + | U8Value + | F64Value + | F32Value + | F16Value + | BoolValue; -/** Create an f16 from a bit representation, a uint16 represented as a JS `number`. */ -export const f16Bits = (bits: number): Scalar => - scalarFromBits(TypeF16, workingDataU16, workingDataF16, bits); +export interface ScalarBuilder { + (value: T): Scalar; +} -/** Create an AbstractInt from a numeric value, a JS `bigint`. */ -export const abstractInt = (value: bigint): Scalar => - scalarFromValue(TypeAbstractInt, workingDataI64, value); +export function isScalar(value: object): value is Scalar { + return ( + value instanceof AbstractIntValue || + value instanceof AbstractFloatValue || + value instanceof I32Value || + value instanceof U32Value || + value instanceof I16Value || + value instanceof U16Value || + value instanceof I8Value || + value instanceof U8Value || + value instanceof F64Value || + value instanceof F32Value || + value instanceof F16Value || + value instanceof BoolValue + ); +} -export const abstractIntBits = (bits: bigint): Scalar => - scalarFromBits(TypeAbstractInt, workingDataU64, workingDataI64, bits); +/** Create an AbstractInt from a numeric value, a JS `bigint`. */ +export function abstractInt(value: bigint) { + workingDataI64[0] = value; + return new AbstractIntValue(workingDataI64[0], workingDataU32[0], workingDataU32[1]); +} -/** Create an i32 from a numeric value, a JS `number`. */ -export const i32 = (value: number): Scalar => scalarFromValue(TypeI32, workingDataI32, value); +/** Create an AbstractInt from a bit representation, a uint64 represented as a JS `bigint`. */ +export function abstractIntBits(value: bigint) { + workingDataU64[0] = value; + return new AbstractIntValue(workingDataI64[0], workingDataU32[0], workingDataU32[1]); +} -/** Create an i16 from a numeric value, a JS `number`. */ -export const i16 = (value: number): Scalar => scalarFromValue(TypeI16, workingDataI16, value); +/** Create an AbstractFloat from a numeric value, a JS `number`. */ +export function abstractFloat(value: number) { + workingDataF64[0] = value; + return new AbstractFloatValue(workingDataF64[0], workingDataU32[0], workingDataU32[1]); +} -/** Create an i8 from a numeric value, a JS `number`. */ -export const i8 = (value: number): Scalar => scalarFromValue(TypeI8, workingDataI8, value); +/** Create an i32 from a numeric value, a JS `number`. */ +export function i32(value: number) { + workingDataI32[0] = value; + return new I32Value(workingDataI32[0], workingDataU32[0]); +} /** Create an i32 from a bit representation, a uint32 represented as a JS `number`. */ -export const i32Bits = (bits: number): Scalar => - scalarFromBits(TypeI32, workingDataU32, workingDataI32, bits); +export function i32Bits(bits: number) { + workingDataU32[0] = bits; + return new I32Value(workingDataI32[0], workingDataU32[0]); +} -/** Create an i16 from a bit representation, a uint16 represented as a JS `number`. */ -export const i16Bits = (bits: number): Scalar => - scalarFromBits(TypeI16, workingDataU16, workingDataI16, bits); +/** Create a u32 from a numeric value, a JS `number`. */ +export function u32(value: number) { + workingDataU32[0] = value; + return new U32Value(workingDataU32[0]); +} -/** Create an i8 from a bit representation, a uint8 represented as a JS `number`. */ -export const i8Bits = (bits: number): Scalar => - scalarFromBits(TypeI8, workingDataU8, workingDataI8, bits); +/** Create a u32 from a bit representation, a uint32 represented as a JS `number`. */ +export function u32Bits(bits: number) { + workingDataU32[0] = bits; + return new U32Value(workingDataU32[0]); +} -/** Create a u32 from a numeric value, a JS `number`. */ -export const u32 = (value: number): Scalar => scalarFromValue(TypeU32, workingDataU32, value); +/** Create an i16 from a numeric value, a JS `number`. */ +export function i16(value: number) { + workingDataI16[0] = value; + return new I16Value(value, workingDataU16[0]); +} /** Create a u16 from a numeric value, a JS `number`. */ -export const u16 = (value: number): Scalar => scalarFromValue(TypeU16, workingDataU16, value); +export function u16(value: number) { + workingDataU16[0] = value; + return new U16Value(workingDataU16[0]); +} + +/** Create an i8 from a numeric value, a JS `number`. */ +export function i8(value: number) { + workingDataI8[0] = value; + return new I8Value(value, workingDataU8[0]); +} /** Create a u8 from a numeric value, a JS `number`. */ -export const u8 = (value: number): Scalar => scalarFromValue(TypeU8, workingDataU8, value); +export function u8(value: number) { + workingDataU8[0] = value; + return new U8Value(workingDataU8[0]); +} -/** Create an u32 from a bit representation, a uint32 represented as a JS `number`. */ -export const u32Bits = (bits: number): Scalar => - scalarFromBits(TypeU32, workingDataU32, workingDataU32, bits); +/** Create an f64 from a numeric value, a JS `number`. */ +export function f64(value: number) { + workingDataF64[0] = value; + return new F64Value(value, workingDataU32[0], workingDataU32[1]); +} -/** Create an u16 from a bit representation, a uint16 represented as a JS `number`. */ -export const u16Bits = (bits: number): Scalar => - scalarFromBits(TypeU16, workingDataU16, workingDataU16, bits); +/** Create an f32 from a numeric value, a JS `number`. */ +export function f32(value: number) { + workingDataF32[0] = value; + return new F32Value(value, workingDataU32[0]); +} + +/** Create an f32 from a bit representation, a uint32 represented as a JS `number`. */ +export function f32Bits(bits: number) { + workingDataU32[0] = bits; + return new F32Value(workingDataF32[0], bits); +} -/** Create an u8 from a bit representation, a uint8 represented as a JS `number`. */ -export const u8Bits = (bits: number): Scalar => - scalarFromBits(TypeU8, workingDataU8, workingDataU8, bits); +/** Create an f16 from a numeric value, a JS `number`. */ +export function f16(value: number) { + workingDataF16[0] = value; + return new F16Value(value, workingDataU16[0]); +} + +/** Create an f16 from a bit representation, a uint16 represented as a JS `number`. */ +export function f16Bits(bits: number) { + workingDataU16[0] = bits; + return new F16Value(workingDataF16[0], bits); +} /** Create a boolean value. */ export function bool(value: boolean): Scalar { - // WGSL does not support using 'bool' types directly in storage / uniform - // buffers, so instead we pack booleans in a u32, where 'false' is zero and - // 'true' is any non-zero value. - workingDataU32[0] = value ? 1 : 0; - workingDataU32[1] = 0; - return new Scalar(TypeBool, value, workingDataU32[1], workingDataU32[0]); + return new BoolValue(value); } /** A 'true' literal value */ @@ -1528,7 +1892,7 @@ export function serializeValue(s: BinaryStream, v: Value) { } }; - if (v instanceof Scalar) { + if (isScalar(v)) { s.writeU8(SerializedValueKind.Scalar); serializeScalarKind(s, v.type.kind); serializeScalar(v, v.type.kind); From b9fda017e5f316d9bcc7e11be6aec9216aeaf9f5 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Thu, 7 Mar 2024 16:06:17 +0000 Subject: [PATCH 02/33] Refactor Value and Types Suffix all Value TS-types with 'Value'. Put all Types in a 'Type' namespace. This keeps naming consistent, closer to WGSL, and clears the way for Type.array. --- src/resources/cache/hashes.json | 216 +++++----- src/unittests/conversion.spec.ts | 14 +- .../render_pipeline/sample_mask.spec.ts | 12 +- .../expression/binary/af_addition.spec.ts | 20 +- .../expression/binary/af_comparison.cache.ts | 18 +- .../expression/binary/af_comparison.spec.ts | 44 +- .../expression/binary/af_division.spec.ts | 20 +- .../binary/af_matrix_addition.spec.ts | 8 +- .../af_matrix_matrix_multiplication.spec.ts | 6 +- .../af_matrix_scalar_multiplication.spec.ts | 10 +- .../binary/af_matrix_subtraction.spec.ts | 8 +- .../af_matrix_vector_multiplication.spec.ts | 10 +- .../binary/af_multiplication.spec.ts | 20 +- .../expression/binary/af_remainder.spec.ts | 18 +- .../expression/binary/af_subtraction.spec.ts | 20 +- .../expression/binary/ai_arithmetic.spec.ts | 62 +-- .../expression/binary/ai_comparison.spec.ts | 16 +- .../expression/binary/bitwise.spec.ts | 10 +- .../expression/binary/bitwise_shift.spec.ts | 10 +- .../expression/binary/bool_logical.spec.ts | 18 +- .../expression/binary/f16_addition.spec.ts | 20 +- .../expression/binary/f16_comparison.cache.ts | 30 +- .../expression/binary/f16_comparison.spec.ts | 14 +- .../expression/binary/f16_division.spec.ts | 20 +- .../binary/f16_matrix_addition.spec.ts | 10 +- .../f16_matrix_matrix_multiplication.spec.ts | 10 +- .../f16_matrix_scalar_multiplication.spec.ts | 14 +- .../binary/f16_matrix_subtraction.spec.ts | 10 +- .../f16_matrix_vector_multiplication.spec.ts | 14 +- .../binary/f16_multiplication.spec.ts | 20 +- .../expression/binary/f16_remainder.spec.ts | 20 +- .../expression/binary/f16_subtraction.spec.ts | 20 +- .../expression/binary/f32_addition.spec.ts | 20 +- .../expression/binary/f32_comparison.cache.ts | 30 +- .../expression/binary/f32_comparison.spec.ts | 14 +- .../expression/binary/f32_division.spec.ts | 20 +- .../binary/f32_matrix_addition.spec.ts | 10 +- .../f32_matrix_matrix_multiplication.spec.ts | 10 +- .../f32_matrix_scalar_multiplication.spec.ts | 14 +- .../binary/f32_matrix_subtraction.spec.ts | 10 +- .../f32_matrix_vector_multiplication.spec.ts | 14 +- .../binary/f32_multiplication.spec.ts | 20 +- .../expression/binary/f32_remainder.spec.ts | 20 +- .../expression/binary/f32_subtraction.spec.ts | 20 +- .../expression/binary/i32_arithmetic.spec.ts | 82 ++-- .../expression/binary/i32_comparison.spec.ts | 14 +- .../expression/binary/u32_arithmetic.spec.ts | 82 ++-- .../expression/binary/u32_comparison.spec.ts | 14 +- .../expression/call/builtin/abs.spec.ts | 29 +- .../expression/call/builtin/acos.spec.ts | 8 +- .../expression/call/builtin/acosh.spec.ts | 8 +- .../expression/call/builtin/all.spec.ts | 20 +- .../expression/call/builtin/any.spec.ts | 20 +- .../expression/call/builtin/asin.spec.ts | 8 +- .../expression/call/builtin/asinh.spec.ts | 8 +- .../expression/call/builtin/atan.spec.ts | 8 +- .../expression/call/builtin/atan2.spec.ts | 8 +- .../expression/call/builtin/atanh.spec.ts | 8 +- .../expression/call/builtin/bitcast.cache.ts | 32 +- .../expression/call/builtin/bitcast.spec.ts | 132 ++---- .../expression/call/builtin/ceil.spec.ts | 8 +- .../expression/call/builtin/clamp.cache.ts | 18 +- .../expression/call/builtin/clamp.spec.ts | 29 +- .../expression/call/builtin/cos.spec.ts | 8 +- .../expression/call/builtin/cosh.spec.ts | 8 +- .../call/builtin/countLeadingZeros.spec.ts | 6 +- .../call/builtin/countOneBits.spec.ts | 6 +- .../call/builtin/countTrailingZeros.spec.ts | 6 +- .../expression/call/builtin/cross.spec.ts | 26 +- .../expression/call/builtin/degrees.spec.ts | 12 +- .../call/builtin/determinant.spec.ts | 8 +- .../expression/call/builtin/distance.spec.ts | 62 +-- .../expression/call/builtin/dot.spec.ts | 131 +----- .../call/builtin/dot4I8Packed.spec.ts | 4 +- .../call/builtin/dot4U8Packed.spec.ts | 4 +- .../expression/call/builtin/exp.spec.ts | 8 +- .../expression/call/builtin/exp2.spec.ts | 8 +- .../call/builtin/extractBits.spec.ts | 20 +- .../call/builtin/faceForward.spec.ts | 28 +- .../call/builtin/firstLeadingBit.spec.ts | 6 +- .../call/builtin/firstTrailingBit.spec.ts | 6 +- .../expression/call/builtin/floor.spec.ts | 12 +- .../expression/call/builtin/fma.spec.ts | 12 +- .../expression/call/builtin/fract.spec.ts | 8 +- .../expression/call/builtin/frexp.cache.ts | 10 +- .../expression/call/builtin/frexp.spec.ts | 34 +- .../call/builtin/insertBits.spec.ts | 18 +- .../call/builtin/inversesqrt.spec.ts | 8 +- .../expression/call/builtin/ldexp.spec.ts | 10 +- .../expression/call/builtin/length.spec.ts | 20 +- .../expression/call/builtin/log.spec.ts | 8 +- .../expression/call/builtin/log2.spec.ts | 8 +- .../expression/call/builtin/max.spec.ts | 32 +- .../expression/call/builtin/min.spec.ts | 32 +- .../expression/call/builtin/mix.spec.ts | 80 +--- .../expression/call/builtin/modf.spec.ts | 78 ++-- .../expression/call/builtin/normalize.spec.ts | 16 +- .../call/builtin/pack2x16float.spec.ts | 4 +- .../call/builtin/pack2x16snorm.spec.ts | 12 +- .../call/builtin/pack2x16unorm.spec.ts | 12 +- .../call/builtin/pack4x8snorm.spec.ts | 20 +- .../call/builtin/pack4x8unorm.spec.ts | 20 +- .../expression/call/builtin/pack4xI8.spec.ts | 4 +- .../call/builtin/pack4xI8Clamp.spec.ts | 4 +- .../expression/call/builtin/pack4xU8.spec.ts | 4 +- .../call/builtin/pack4xU8Clamp.spec.ts | 4 +- .../expression/call/builtin/pow.spec.ts | 8 +- .../call/builtin/quantizeToF16.spec.ts | 4 +- .../expression/call/builtin/radians.spec.ts | 12 +- .../expression/call/builtin/reflect.spec.ts | 58 +-- .../expression/call/builtin/refract.spec.ts | 28 +- .../call/builtin/reverseBits.spec.ts | 6 +- .../expression/call/builtin/round.spec.ts | 12 +- .../expression/call/builtin/saturate.spec.ts | 12 +- .../expression/call/builtin/select.spec.ts | 47 +-- .../expression/call/builtin/sign.spec.ts | 22 +- .../expression/call/builtin/sin.spec.ts | 8 +- .../expression/call/builtin/sinh.spec.ts | 8 +- .../call/builtin/smoothstep.spec.ts | 8 +- .../expression/call/builtin/sqrt.spec.ts | 8 +- .../expression/call/builtin/step.spec.ts | 8 +- .../expression/call/builtin/tan.spec.ts | 8 +- .../expression/call/builtin/tanh.spec.ts | 8 +- .../expression/call/builtin/transpose.spec.ts | 16 +- .../expression/call/builtin/trunc.spec.ts | 12 +- .../call/builtin/unpack2x16float.spec.ts | 4 +- .../call/builtin/unpack2x16snorm.spec.ts | 4 +- .../call/builtin/unpack2x16unorm.spec.ts | 4 +- .../call/builtin/unpack4x8snorm.spec.ts | 4 +- .../call/builtin/unpack4x8unorm.spec.ts | 4 +- .../call/builtin/unpack4xI8.spec.ts | 4 +- .../call/builtin/unpack4xU8.spec.ts | 4 +- .../shader/execution/expression/case.ts | 21 +- .../shader/execution/expression/case_cache.ts | 8 +- .../execution/expression/expectation.ts | 8 +- .../shader/execution/expression/expression.ts | 26 +- .../expression/unary/af_arithmetic.spec.ts | 8 +- .../expression/unary/af_assignment.spec.ts | 16 +- .../expression/unary/ai_arithmetic.spec.ts | 4 +- .../expression/unary/ai_assignment.spec.ts | 8 +- .../expression/unary/ai_complement.spec.ts | 6 +- .../expression/unary/bool_conversion.cache.ts | 6 +- .../expression/unary/bool_conversion.spec.ts | 19 +- .../expression/unary/bool_logical.spec.ts | 4 +- .../expression/unary/f16_arithmetic.spec.ts | 4 +- .../expression/unary/f16_conversion.spec.ts | 27 +- .../expression/unary/f32_arithmetic.spec.ts | 4 +- .../expression/unary/f32_conversion.spec.ts | 27 +- .../expression/unary/i32_arithmetic.spec.ts | 4 +- .../expression/unary/i32_complement.spec.ts | 4 +- .../expression/unary/i32_conversion.spec.ts | 12 +- .../expression/unary/u32_complement.spec.ts | 4 +- .../expression/unary/u32_conversion.spec.ts | 14 +- .../expression/call/builtin/abs.spec.ts | 4 +- .../expression/call/builtin/acos.spec.ts | 9 +- .../expression/call/builtin/acosh.spec.ts | 13 +- .../expression/call/builtin/asin.spec.ts | 9 +- .../expression/call/builtin/asinh.spec.ts | 12 +- .../expression/call/builtin/atan.spec.ts | 9 +- .../expression/call/builtin/atan2.spec.ts | 18 +- .../expression/call/builtin/atanh.spec.ts | 9 +- .../expression/call/builtin/ceil.spec.ts | 9 +- .../expression/call/builtin/clamp.spec.ts | 4 +- .../call/builtin/const_override_validation.ts | 3 +- .../expression/call/builtin/cos.spec.ts | 9 +- .../expression/call/builtin/cosh.spec.ts | 12 +- .../expression/call/builtin/degrees.spec.ts | 12 +- .../call/builtin/derivatives.spec.ts | 14 +- .../expression/call/builtin/exp.spec.ts | 12 +- .../expression/call/builtin/exp2.spec.ts | 12 +- .../expression/call/builtin/floor.spec.ts | 9 +- .../call/builtin/inverseSqrt.spec.ts | 12 +- .../expression/call/builtin/length.spec.ts | 20 +- .../expression/call/builtin/log.spec.ts | 9 +- .../expression/call/builtin/log2.spec.ts | 9 +- .../expression/call/builtin/modf.spec.ts | 9 +- .../expression/call/builtin/radians.spec.ts | 9 +- .../expression/call/builtin/round.spec.ts | 9 +- .../expression/call/builtin/saturate.spec.ts | 9 +- .../expression/call/builtin/sign.spec.ts | 9 +- .../expression/call/builtin/sin.spec.ts | 9 +- .../expression/call/builtin/sinh.spec.ts | 12 +- .../expression/call/builtin/sqrt.spec.ts | 12 +- .../expression/call/builtin/tan.spec.ts | 12 +- src/webgpu/util/compare.ts | 27 +- src/webgpu/util/conversion.ts | 387 ++++++++++-------- src/webgpu/util/floating_point.ts | 6 +- 187 files changed, 1628 insertions(+), 1993 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 8332f3962c10..6733022574c8 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "cfc28768", - "webgpu/shader/execution/binary/af_logical.bin": "ae9b9ba0", - "webgpu/shader/execution/binary/af_division.bin": "30839274", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "e43d31a5", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "fbec81c8", - "webgpu/shader/execution/binary/af_multiplication.bin": "19681c0c", - "webgpu/shader/execution/binary/af_remainder.bin": "fb9c916", - "webgpu/shader/execution/binary/af_subtraction.bin": "6f448b72", - "webgpu/shader/execution/binary/f16_addition.bin": "83ac7e89", - "webgpu/shader/execution/binary/f16_logical.bin": "6c9cdfe8", - "webgpu/shader/execution/binary/f16_division.bin": "487729fe", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "54aa39b", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "3a4159f8", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "fceaaf1f", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "92151406", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "d920f7ec", - "webgpu/shader/execution/binary/f16_multiplication.bin": "fc8c2a0f", - "webgpu/shader/execution/binary/f16_remainder.bin": "6e60c5d0", - "webgpu/shader/execution/binary/f16_subtraction.bin": "4277226e", - "webgpu/shader/execution/binary/f32_addition.bin": "5b9c4270", - "webgpu/shader/execution/binary/f32_logical.bin": "32188047", - "webgpu/shader/execution/binary/f32_division.bin": "22edca65", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "62107e07", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "9e5677c9", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "d69476cb", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c429b29b", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "ded0240e", - "webgpu/shader/execution/binary/f32_multiplication.bin": "f088c036", - "webgpu/shader/execution/binary/f32_remainder.bin": "dcb44e72", - "webgpu/shader/execution/binary/f32_subtraction.bin": "dab334aa", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "2b48d275", - "webgpu/shader/execution/binary/i32_comparison.bin": "f2c3cd37", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "7ec7b66", - "webgpu/shader/execution/binary/u32_comparison.bin": "50ab2f61", - "webgpu/shader/execution/abs.bin": "e37fc25a", - "webgpu/shader/execution/acos.bin": "4dc16324", - "webgpu/shader/execution/acosh.bin": "1f999f3c", - "webgpu/shader/execution/asin.bin": "a653cf5b", - "webgpu/shader/execution/asinh.bin": "515e83f7", - "webgpu/shader/execution/atan.bin": "95006a05", - "webgpu/shader/execution/atan2.bin": "3cc4efac", - "webgpu/shader/execution/atanh.bin": "e2985906", - "webgpu/shader/execution/bitcast.bin": "bda67f6d", - "webgpu/shader/execution/ceil.bin": "5d285a4", - "webgpu/shader/execution/clamp.bin": "cd0271cf", - "webgpu/shader/execution/cos.bin": "dfd7be1f", - "webgpu/shader/execution/cosh.bin": "8a9b86a5", - "webgpu/shader/execution/cross.bin": "80fe94ce", - "webgpu/shader/execution/degrees.bin": "d9a6c350", - "webgpu/shader/execution/determinant.bin": "2e19c9bd", - "webgpu/shader/execution/distance.bin": "64daad1a", - "webgpu/shader/execution/dot.bin": "c78ce327", - "webgpu/shader/execution/exp.bin": "c1b5a4fd", - "webgpu/shader/execution/exp2.bin": "63ac077", - "webgpu/shader/execution/faceForward.bin": "92b334a6", - "webgpu/shader/execution/floor.bin": "7e1bf2c3", - "webgpu/shader/execution/fma.bin": "701a86c6", - "webgpu/shader/execution/fract.bin": "c29aadce", - "webgpu/shader/execution/frexp.bin": "5ae6ca4", - "webgpu/shader/execution/inverseSqrt.bin": "9563f18d", - "webgpu/shader/execution/ldexp.bin": "f005b4b5", - "webgpu/shader/execution/length.bin": "ee8e5573", - "webgpu/shader/execution/log.bin": "29c4187b", - "webgpu/shader/execution/log2.bin": "9ce20393", - "webgpu/shader/execution/max.bin": "7516dbff", - "webgpu/shader/execution/min.bin": "75e9d526", - "webgpu/shader/execution/mix.bin": "b224306a", - "webgpu/shader/execution/modf.bin": "ba63df78", - "webgpu/shader/execution/normalize.bin": "9842b756", - "webgpu/shader/execution/pack2x16float.bin": "5dee8700", - "webgpu/shader/execution/pow.bin": "81bac0a9", - "webgpu/shader/execution/quantizeToF16.bin": "6973d05d", - "webgpu/shader/execution/radians.bin": "128a0639", - "webgpu/shader/execution/reflect.bin": "5de2177e", - "webgpu/shader/execution/refract.bin": "de1e1ade", - "webgpu/shader/execution/round.bin": "70a9da36", - "webgpu/shader/execution/saturate.bin": "e8daa151", - "webgpu/shader/execution/sign.bin": "503847e9", - "webgpu/shader/execution/sin.bin": "51df6447", - "webgpu/shader/execution/sinh.bin": "dbc469f3", - "webgpu/shader/execution/smoothstep.bin": "16cdfbe9", - "webgpu/shader/execution/sqrt.bin": "b59d4606", - "webgpu/shader/execution/step.bin": "5df4b04b", - "webgpu/shader/execution/tan.bin": "f1535f0c", - "webgpu/shader/execution/tanh.bin": "6e653c6d", - "webgpu/shader/execution/transpose.bin": "41ff2823", - "webgpu/shader/execution/trunc.bin": "7c3baf7", - "webgpu/shader/execution/unpack2x16float.bin": "f994910", - "webgpu/shader/execution/unpack2x16snorm.bin": "df36ee6f", - "webgpu/shader/execution/unpack2x16unorm.bin": "ac195462", - "webgpu/shader/execution/unpack4x8snorm.bin": "39a4743a", - "webgpu/shader/execution/unpack4x8unorm.bin": "ecae6fe5", - "webgpu/shader/execution/unary/af_arithmetic.bin": "9800df7f", - "webgpu/shader/execution/unary/af_assignment.bin": "85c4d516", - "webgpu/shader/execution/unary/bool_conversion.bin": "85664e85", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "3e0dea1", - "webgpu/shader/execution/unary/f16_conversion.bin": "c1f37dd1", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "f2cd5bd2", - "webgpu/shader/execution/unary/f32_conversion.bin": "b4174de", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "b327298b", - "webgpu/shader/execution/unary/i32_conversion.bin": "ebdc408a", - "webgpu/shader/execution/unary/u32_conversion.bin": "aafe3d79", - "webgpu/shader/execution/unary/ai_assignment.bin": "ef0b469b", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "b22511b9", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "67c3d3b6", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "b72f2c53", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "7f328797", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "b34a4a2b" + "webgpu/shader/execution/binary/af_addition.bin": "45a00f48", + "webgpu/shader/execution/binary/af_logical.bin": "458eba4", + "webgpu/shader/execution/binary/af_division.bin": "ed6916d6", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "ddeaa1d3", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "bdc18f23", + "webgpu/shader/execution/binary/af_multiplication.bin": "e6e11b40", + "webgpu/shader/execution/binary/af_remainder.bin": "61849bd4", + "webgpu/shader/execution/binary/af_subtraction.bin": "6afd0c9a", + "webgpu/shader/execution/binary/f16_addition.bin": "118a69b1", + "webgpu/shader/execution/binary/f16_logical.bin": "53cbe093", + "webgpu/shader/execution/binary/f16_division.bin": "6cf5db74", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "37b3e5b1", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "f9f9c546", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "10c32980", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "4a992ee0", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "3969786a", + "webgpu/shader/execution/binary/f16_multiplication.bin": "27375c0a", + "webgpu/shader/execution/binary/f16_remainder.bin": "1e5d8fc7", + "webgpu/shader/execution/binary/f16_subtraction.bin": "daffd0ed", + "webgpu/shader/execution/binary/f32_addition.bin": "384766d0", + "webgpu/shader/execution/binary/f32_logical.bin": "d4f9fd6a", + "webgpu/shader/execution/binary/f32_division.bin": "760f650f", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "504aac15", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "740e31c4", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "3c5abc3c", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c41fee39", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "43bfea5a", + "webgpu/shader/execution/binary/f32_multiplication.bin": "d6b990a9", + "webgpu/shader/execution/binary/f32_remainder.bin": "53f7d8e9", + "webgpu/shader/execution/binary/f32_subtraction.bin": "f644082", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "8ce49cc7", + "webgpu/shader/execution/binary/i32_comparison.bin": "ac0e960f", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "68560dc0", + "webgpu/shader/execution/binary/u32_comparison.bin": "ec9ec4c6", + "webgpu/shader/execution/abs.bin": "d7043582", + "webgpu/shader/execution/acos.bin": "a7a01d03", + "webgpu/shader/execution/acosh.bin": "3bfd9ebc", + "webgpu/shader/execution/asin.bin": "f91850f1", + "webgpu/shader/execution/asinh.bin": "19169ea3", + "webgpu/shader/execution/atan.bin": "51a04ddb", + "webgpu/shader/execution/atan2.bin": "e732e242", + "webgpu/shader/execution/atanh.bin": "4763b613", + "webgpu/shader/execution/bitcast.bin": "195c00a7", + "webgpu/shader/execution/ceil.bin": "9856b786", + "webgpu/shader/execution/clamp.bin": "5a700a65", + "webgpu/shader/execution/cos.bin": "ff14c921", + "webgpu/shader/execution/cosh.bin": "21c587ad", + "webgpu/shader/execution/cross.bin": "c159771f", + "webgpu/shader/execution/degrees.bin": "b0de92be", + "webgpu/shader/execution/determinant.bin": "83d642d4", + "webgpu/shader/execution/distance.bin": "e65d0cb7", + "webgpu/shader/execution/dot.bin": "dc57a00c", + "webgpu/shader/execution/exp.bin": "f0c6b19", + "webgpu/shader/execution/exp2.bin": "5d3dd4e0", + "webgpu/shader/execution/faceForward.bin": "3979a4de", + "webgpu/shader/execution/floor.bin": "3fecf76d", + "webgpu/shader/execution/fma.bin": "e7fe86b8", + "webgpu/shader/execution/fract.bin": "71caa066", + "webgpu/shader/execution/frexp.bin": "ed72dcec", + "webgpu/shader/execution/inverseSqrt.bin": "383e6e9c", + "webgpu/shader/execution/ldexp.bin": "bedfc1d5", + "webgpu/shader/execution/length.bin": "38e35ab4", + "webgpu/shader/execution/log.bin": "2517404c", + "webgpu/shader/execution/log2.bin": "a833136", + "webgpu/shader/execution/max.bin": "8c2f7c51", + "webgpu/shader/execution/min.bin": "7f732adb", + "webgpu/shader/execution/mix.bin": "982c982c", + "webgpu/shader/execution/modf.bin": "743632fc", + "webgpu/shader/execution/normalize.bin": "52adf424", + "webgpu/shader/execution/pack2x16float.bin": "2c879955", + "webgpu/shader/execution/pow.bin": "ba686c94", + "webgpu/shader/execution/quantizeToF16.bin": "e704252d", + "webgpu/shader/execution/radians.bin": "afe57c6e", + "webgpu/shader/execution/reflect.bin": "332e001e", + "webgpu/shader/execution/refract.bin": "246f6c0b", + "webgpu/shader/execution/round.bin": "e555383f", + "webgpu/shader/execution/saturate.bin": "9dce4047", + "webgpu/shader/execution/sign.bin": "3ef39d2e", + "webgpu/shader/execution/sin.bin": "8546b36c", + "webgpu/shader/execution/sinh.bin": "72ae8d37", + "webgpu/shader/execution/smoothstep.bin": "79eca0b6", + "webgpu/shader/execution/sqrt.bin": "ac8f95e9", + "webgpu/shader/execution/step.bin": "34ce6432", + "webgpu/shader/execution/tan.bin": "928e0e2f", + "webgpu/shader/execution/tanh.bin": "be078de7", + "webgpu/shader/execution/transpose.bin": "2ce22a5b", + "webgpu/shader/execution/trunc.bin": "26115486", + "webgpu/shader/execution/unpack2x16float.bin": "d052cda6", + "webgpu/shader/execution/unpack2x16snorm.bin": "a3ab8e29", + "webgpu/shader/execution/unpack2x16unorm.bin": "f42b9498", + "webgpu/shader/execution/unpack4x8snorm.bin": "5c90b367", + "webgpu/shader/execution/unpack4x8unorm.bin": "ef24abbe", + "webgpu/shader/execution/unary/af_arithmetic.bin": "28b510fa", + "webgpu/shader/execution/unary/af_assignment.bin": "4f4d507a", + "webgpu/shader/execution/unary/bool_conversion.bin": "5cbbd5e2", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "55ff626f", + "webgpu/shader/execution/unary/f16_conversion.bin": "e16712e2", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "672609de", + "webgpu/shader/execution/unary/f32_conversion.bin": "daa3ffb8", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "eecbb027", + "webgpu/shader/execution/unary/i32_conversion.bin": "c3f19a9", + "webgpu/shader/execution/unary/u32_conversion.bin": "b58b1876", + "webgpu/shader/execution/unary/ai_assignment.bin": "326020c6", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "40123e00", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "37ffc69", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "35e08b61", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "6f31c22f", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "7b17ec2a" } \ No newline at end of file diff --git a/src/unittests/conversion.spec.ts b/src/unittests/conversion.spec.ts index 8606aa871794..e144f39288a2 100644 --- a/src/unittests/conversion.spec.ts +++ b/src/unittests/conversion.spec.ts @@ -18,7 +18,7 @@ import { i32, kFloat16Format, kFloat32Format, - Matrix, + MatrixValue, numbersApproximatelyEqual, pack2x16float, pack2x16snorm, @@ -26,14 +26,14 @@ import { pack4x8snorm, pack4x8unorm, packRGB9E5UFloat, - Scalar, + ScalarValue, toMatrix, u32, unpackRGB9E5UFloat, vec2, vec3, vec4, - Vector, + VectorValue, } from '../webgpu/util/conversion.js'; import { UnitTest } from './unit_test.js'; @@ -191,7 +191,7 @@ g.test('floatBitsToULPFromZero,32').fn(t => { }); g.test('scalarWGSL').fn(t => { - const cases: Array<[Scalar, string]> = [ + const cases: Array<[ScalarValue, string]> = [ [f32(0.0), '0.0f'], // The number -0.0 can be remapped to 0.0 when stored in a Scalar // object. It is not possible to guarantee that '-0.0f' will @@ -227,7 +227,7 @@ expect: ${expect}` }); g.test('vectorWGSL').fn(t => { - const cases: Array<[Vector, string]> = [ + const cases: Array<[VectorValue, string]> = [ [vec2(f32(42.0), f32(24.0)), 'vec2(42.0f, 24.0f)'], [vec2(f16Bits(0x5140), f16Bits(0x4e00)), 'vec2(42.0h, 24.0h)'], [vec2(u32(42), u32(24)), 'vec2(42u, 24u)'], @@ -261,7 +261,7 @@ expect: ${expect}` }); g.test('matrixWGSL').fn(t => { - const cases: Array<[Matrix, string]> = [ + const cases: Array<[MatrixValue, string]> = [ [ toMatrix( [ @@ -391,7 +391,7 @@ g.test('constructorMatrix') return [...Array(rows).keys()].map(r => scalar_builder(c * cols + r)); }); - const got = new Matrix(elements); + const got = new MatrixValue(elements); const got_type = got.type; t.expect( got_type.cols === cols, diff --git a/src/webgpu/api/operation/render_pipeline/sample_mask.spec.ts b/src/webgpu/api/operation/render_pipeline/sample_mask.spec.ts index ca4a2479658e..b28e1b381cad 100644 --- a/src/webgpu/api/operation/render_pipeline/sample_mask.spec.ts +++ b/src/webgpu/api/operation/render_pipeline/sample_mask.spec.ts @@ -21,7 +21,7 @@ import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { assert, range } from '../../../../common/util/util.js'; import { GPUTest, TextureTestMixin } from '../../../gpu_test.js'; import { checkElementsPassPredicate, checkElementsEqual } from '../../../util/check_contents.js'; -import { TypeF32, TypeU32 } from '../../../util/conversion.js'; +import { Type } from '../../../util/conversion.js'; import { TexelView } from '../../../util/texture/texel_view.js'; const kColors = [ @@ -438,7 +438,7 @@ class F extends TextureTestMixin(GPUTest) { fragmentShaderOutputMask: number ) { const buffer = this.copy2DTextureToBufferUsingComputePass( - TypeF32, // correspond to 'rgba8unorm' format + Type.f32, // correspond to 'rgba8unorm' format 4, texture.createView(), sampleCount @@ -464,7 +464,7 @@ class F extends TextureTestMixin(GPUTest) { const buffer = this.copy2DTextureToBufferUsingComputePass( // Use f32 as the scalar type for depth (depth24plus, depth32float) // Use u32 as the scalar type for stencil (stencil8) - aspect === 'depth-only' ? TypeF32 : TypeU32, + aspect === 'depth-only' ? Type.f32 : Type.u32, 1, depthStencilTexture.createView({ aspect }), sampleCount @@ -705,7 +705,7 @@ color' <= color. ); const colorBuffer = t.copy2DTextureToBufferUsingComputePass( - TypeF32, // correspond to 'rgba8unorm' format + Type.f32, // correspond to 'rgba8unorm' format 4, color.createView(), sampleCount @@ -717,7 +717,7 @@ color' <= color. colorResultPromises.push(colorResult); const depthBuffer = t.copy2DTextureToBufferUsingComputePass( - TypeF32, // correspond to 'depth24plus-stencil8' format + Type.f32, // correspond to 'depth24plus-stencil8' format 1, depthStencil.createView({ aspect: 'depth-only' }), sampleCount @@ -729,7 +729,7 @@ color' <= color. depthResultPromises.push(depthResult); const stencilBuffer = t.copy2DTextureToBufferUsingComputePass( - TypeU32, // correspond to 'depth24plus-stencil8' format + Type.u32, // correspond to 'depth24plus-stencil8' format 1, depthStencil.createView({ aspect: 'stencil-only' }), sampleCount 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 3b14897c2227..52a07ff328f8 100644 --- a/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for non-matrix AbstractFloat addition expression +Execution Tests for non-matrix abstract-float addition expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_addition.cache.js'; @@ -26,8 +26,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts index 648ea5c0b0b8..e0002664018a 100644 --- a/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts @@ -1,5 +1,5 @@ import { anyOf } from '../../../../util/compare.js'; -import { abstractFloat, bool, Scalar } from '../../../../util/conversion.js'; +import { abstractFloat, bool, ScalarValue } from '../../../../util/conversion.js'; import { flushSubnormalNumberF64, vectorF64Range } from '../../../../util/math.js'; import { Case } from '../case.js'; import { makeCaseCache } from '../case_cache.js'; @@ -11,7 +11,7 @@ import { makeCaseCache } from '../case_cache.js'; function makeCase( lhs: number, rhs: number, - truthFunc: (lhs: Scalar, rhs: Scalar) => boolean + truthFunc: (lhs: ScalarValue, rhs: ScalarValue) => boolean ): Case { // Subnormal float values may be flushed at any time. // https://www.w3.org/TR/WGSL/#floating-point-evaluation @@ -19,7 +19,7 @@ function makeCase( const af_rhs = abstractFloat(rhs); const lhs_options = new Set([af_lhs, abstractFloat(flushSubnormalNumberF64(lhs))]); const rhs_options = new Set([af_rhs, abstractFloat(flushSubnormalNumberF64(rhs))]); - const expected: Array = []; + const expected: Array = []; lhs_options.forEach(l => { rhs_options.forEach(r => { const result = bool(truthFunc(l, r)); @@ -34,7 +34,7 @@ function makeCase( export const d = makeCaseCache('binary/af_logical', { equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -43,7 +43,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, not_equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -52,7 +52,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, less_than: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -61,7 +61,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, less_equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -70,7 +70,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, greater_than: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -79,7 +79,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, greater_equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; diff --git a/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts index cc03ee4367f2..3941e1253969 100644 --- a/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for the AbstractFloat comparison operations +Execution Tests for the abstract-float comparison operations `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeBool } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { d } from './af_comparison.cache.js'; @@ -27,7 +27,14 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('equals'); - await run(t, binary('=='), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run( + t, + binary('=='), + [Type.abstractFloat, Type.abstractFloat], + Type.bool, + t.params, + cases + ); }); g.test('not_equals') @@ -45,7 +52,14 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('not_equals'); - await run(t, binary('!='), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run( + t, + binary('!='), + [Type.abstractFloat, Type.abstractFloat], + Type.bool, + t.params, + cases + ); }); g.test('less_than') @@ -63,7 +77,7 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('less_than'); - await run(t, binary('<'), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.abstractFloat, Type.abstractFloat], Type.bool, t.params, cases); }); g.test('less_equals') @@ -81,7 +95,14 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('less_equals'); - await run(t, binary('<='), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run( + t, + binary('<='), + [Type.abstractFloat, Type.abstractFloat], + Type.bool, + t.params, + cases + ); }); g.test('greater_than') @@ -99,7 +120,7 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('greater_than'); - await run(t, binary('>'), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.abstractFloat, Type.abstractFloat], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -117,5 +138,12 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('greater_equals'); - await run(t, binary('>='), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run( + t, + binary('>='), + [Type.abstractFloat, Type.abstractFloat], + Type.bool, + t.params, + cases + ); }); 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 5080e62264d2..0ebe30b6ccc9 100644 --- a/src/webgpu/shader/execution/expression/binary/af_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_division.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for non-matrix AbstractFloat division expression +Execution Tests for non-matrix abstract-float division expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_division.cache.js'; @@ -26,8 +26,8 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] await run( t, abstractFloatBinary('/'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] await run( t, abstractFloatBinary('/'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('/'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('/'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); 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 7f6019f53127..49c746c53e74 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 @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for matrix AbstractFloat addition expressions +Execution Tests for matrix abstract-float addition expressions `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_addition.cache.js'; @@ -33,8 +33,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeMat(cols, rows, TypeAbstractFloat), TypeMat(cols, rows, TypeAbstractFloat)], - TypeMat(cols, rows, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat), Type.mat(cols, rows, Type.abstractFloat)], + Type.mat(cols, rows, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts index bb7fd668c296..a1aa005d341c 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-matrix AbstractFloat multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_matrix_multiplication.cache.js'; @@ -40,8 +40,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeMat(x_cols, x_rows, TypeAbstractFloat), TypeMat(y_cols, y_rows, TypeAbstractFloat)], - TypeMat(y_cols, x_rows, TypeAbstractFloat), + [Type.mat(x_cols, x_rows, Type.abstractFloat), Type.mat(y_cols, y_rows, Type.abstractFloat)], + Type.mat(y_cols, x_rows, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts index 05afa31275d6..c6faabbc8453 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-scalar and scalar-matrix AbstractFloat multiplication import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_scalar_multiplication.cache.js'; @@ -33,8 +33,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeMat(cols, rows, TypeAbstractFloat), TypeAbstractFloat], - TypeMat(cols, rows, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat), Type.abstractFloat], + Type.mat(cols, rows, Type.abstractFloat), t.params, cases ); @@ -61,8 +61,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeAbstractFloat, TypeMat(cols, rows, TypeAbstractFloat)], - TypeMat(cols, rows, TypeAbstractFloat), + [Type.abstractFloat, Type.mat(cols, rows, Type.abstractFloat)], + Type.mat(cols, rows, Type.abstractFloat), t.params, cases ); 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 4c6d1d423234..9b240fdee903 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 @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for matrix AbstractFloat subtraction expression +Execution Tests for matrix abstract-float subtraction expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_subtraction.cache.js'; @@ -33,8 +33,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeMat(cols, rows, TypeAbstractFloat), TypeMat(cols, rows, TypeAbstractFloat)], - TypeMat(cols, rows, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat), Type.mat(cols, rows, Type.abstractFloat)], + Type.mat(cols, rows, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts index c464c442aa85..5db78f8369f7 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-vector and vector-matrix AbstractFloat multiplication import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_vector_multiplication.cache.js'; @@ -33,8 +33,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeMat(cols, rows, TypeAbstractFloat), TypeVec(cols, TypeAbstractFloat)], - TypeVec(rows, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat), Type.vec(cols, Type.abstractFloat)], + Type.vec(rows, Type.abstractFloat), t.params, cases ); @@ -61,8 +61,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeVec(rows, TypeAbstractFloat), TypeMat(cols, rows, TypeAbstractFloat)], - TypeVec(cols, TypeAbstractFloat), + [Type.vec(rows, Type.abstractFloat), Type.mat(cols, rows, Type.abstractFloat)], + Type.vec(cols, Type.abstractFloat), t.params, cases ); 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 57cf0dbc4768..405de758bd76 100644 --- a/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for non-matrix AbstractFloat multiplication expression +Execution Tests for non-matrix abstract-float multiplication expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_multiplication.cache.js'; @@ -26,8 +26,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); 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 31a0991e02c4..d743f85ed653 100644 --- a/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix abstract float remainder expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_remainder.cache.js'; @@ -26,8 +26,8 @@ Accuracy: Derived from x - y * trunc(x/y) await run( t, abstractFloatBinary('%'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: Derived from x - y * trunc(x/y) await run( t, abstractFloatBinary('%'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('%'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('%'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); 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 72004cfb1cee..2874a744da2e 100644 --- a/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for non-matrix AbstractFloat subtraction expression +Execution Tests for non-matrix abstract-float subtraction expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_subtraction.cache.js'; @@ -26,8 +26,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts index ae8b6ccf483b..ef211af3ed4b 100644 --- a/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts @@ -4,7 +4,7 @@ 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 { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './ai_arithmetic.cache.js'; @@ -29,8 +29,8 @@ Expression: x + y await run( t, abstractIntBinary('+'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -48,9 +48,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`addition_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('+'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('+'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('addition_vector_scalar') @@ -65,9 +65,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('+'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('+'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); g.test('division') @@ -87,8 +87,8 @@ Expression: x / y await run( t, abstractIntBinary('/'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -106,9 +106,9 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`division_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('/'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('/'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('division_vector_scalar') @@ -123,9 +123,9 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`division_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('/'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('/'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); g.test('multiplication') @@ -145,8 +145,8 @@ Expression: x * y await run( t, abstractIntBinary('*'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -164,9 +164,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`multiplication_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('*'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('*'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar') @@ -181,9 +181,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('*'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('*'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); g.test('remainder') @@ -203,8 +203,8 @@ Expression: x % y await run( t, abstractIntBinary('%'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -222,9 +222,9 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`remainder_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('%'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('%'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('remainder_vector_scalar') @@ -239,9 +239,9 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`remainder_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('%'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('%'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); g.test('subtraction') @@ -261,8 +261,8 @@ Expression: x - y await run( t, abstractIntBinary('-'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -280,9 +280,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`subtraction_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('-'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('-'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar') @@ -297,7 +297,7 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('-'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('-'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/ai_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/ai_comparison.spec.ts index 9d00d0769c18..899e651054ef 100644 --- a/src/webgpu/shader/execution/expression/binary/ai_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/ai_comparison.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for the abstract int comparison expressions +Execution Tests for the abstract-int comparison expressions `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeAbstractInt, bool, abstractInt } from '../../../../util/conversion.js'; +import { bool, abstractInt, Type } from '../../../../util/conversion.js'; import { vectorI64Range } from '../../../../util/math.js'; import { Case } from '../case.js'; import { onlyConstInputSource, run } from '../expression.js'; @@ -35,7 +35,7 @@ Expression: x == y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] === v[1])); - await run(t, binary('=='), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('not_equals') @@ -52,7 +52,7 @@ Expression: x != y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] !== v[1])); - await run(t, binary('!='), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('less_than') @@ -69,7 +69,7 @@ Expression: x < y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] < v[1])); - await run(t, binary('<'), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('less_equals') @@ -86,7 +86,7 @@ Expression: x <= y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] <= v[1])); - await run(t, binary('<='), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('greater_than') @@ -103,7 +103,7 @@ Expression: x > y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] > v[1])); - await run(t, binary('>'), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -120,5 +120,5 @@ Expression: x >= y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] >= v[1])); - await run(t, binary('>='), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts b/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts index ead7258635c2..71744b45959b 100644 --- a/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts @@ -9,7 +9,7 @@ import { abstractIntBits, i32, i32Bits, - Scalar, + ScalarValue, scalarType, u32, u32Bits, @@ -27,27 +27,27 @@ export const g = makeTestGroup(GPUTest); interface ScalarImpl { // builder is a mostly a wrapper around type builders like 'i32Bits' that // handles the (number|bigint) type check. - builder: (bits: bigint | number) => Scalar; + builder: (bits: bigint | number) => ScalarValue; size: 32 | 64; } const kScalarImpls = { i32: { - builder: (bits: bigint | number): Scalar => { + builder: (bits: bigint | number): ScalarValue => { assert(typeof bits === 'number'); return i32Bits(bits); }, size: 32, } as ScalarImpl, u32: { - builder: (bits: bigint | number): Scalar => { + builder: (bits: bigint | number): ScalarValue => { assert(typeof bits === 'number'); return u32Bits(bits); }, size: 32, } as ScalarImpl, 'abstract-int': { - builder: (bits: bigint | number): Scalar => { + builder: (bits: bigint | number): ScalarValue => { assert(typeof bits === 'bigint'); return abstractIntBits(bits); }, diff --git a/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts b/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts index cea6b86c602a..e2ed29d3c2ff 100644 --- a/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the bitwise shift binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { i32, scalarType, ScalarType, TypeU32, u32 } from '../../../../util/conversion.js'; +import { i32, scalarType, ScalarType, Type, u32 } from '../../../../util/conversion.js'; import { Case } from '../case.js'; import { allInputSources, run } from '../expression.js'; @@ -194,7 +194,7 @@ Shift left (shifted value is concrete) .fn(async t => { const type = scalarType(t.params.type); const cases = makeShiftLeftConcreteCases(t.params.type, t.params.inputSource, type); - await run(t, binary('<<'), [type, TypeU32], type, t.params, cases); + await run(t, binary('<<'), [type, Type.u32], type, t.params, cases); }); g.test('shift_left_concrete_compound') @@ -215,7 +215,7 @@ Shift left (shifted value is concrete) .fn(async t => { const type = scalarType(t.params.type); const cases = makeShiftLeftConcreteCases(t.params.type, t.params.inputSource, type); - await run(t, compoundBinary('<<='), [type, TypeU32], type, t.params, cases); + await run(t, compoundBinary('<<='), [type, Type.u32], type, t.params, cases); }); function makeShiftRightConcreteCases(inputType: string, inputSource: string, type: ScalarType) { @@ -319,7 +319,7 @@ Shift right (shifted value is concrete) .fn(async t => { const type = scalarType(t.params.type); const cases = makeShiftRightConcreteCases(t.params.type, t.params.inputSource, type); - await run(t, binary('>>'), [type, TypeU32], type, t.params, cases); + await run(t, binary('>>'), [type, Type.u32], type, t.params, cases); }); g.test('shift_right_concrete_compound') @@ -340,5 +340,5 @@ Shift right (shifted value is concrete) .fn(async t => { const type = scalarType(t.params.type); const cases = makeShiftRightConcreteCases(t.params.type, t.params.inputSource, type); - await run(t, compoundBinary('>>='), [type, TypeU32], type, t.params, cases); + await run(t, compoundBinary('>>='), [type, Type.u32], type, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts b/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts index e3aa448fe3c4..0e76f508240c 100644 --- a/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the boolean binary logical expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { bool, TypeBool } from '../../../../util/conversion.js'; +import { bool, Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -33,7 +33,7 @@ Logical "and". Component-wise when T is a vector. Evaluates both e1 and e2. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('&'), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('&'), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('and_compound') @@ -55,7 +55,7 @@ Logical "and". Component-wise when T is a vector. Evaluates both e1 and e2. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, compoundBinary('&='), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, compoundBinary('&='), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('and_short_circuit') @@ -75,7 +75,7 @@ short_circuiting "and". Yields true if both e1 and e2 are true; evaluates e2 onl { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('&&'), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('&&'), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('or') @@ -97,7 +97,7 @@ Logical "or". Component-wise when T is a vector. Evaluates both e1 and e2. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('|'), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('|'), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('or_compound') @@ -119,7 +119,7 @@ Logical "or". Component-wise when T is a vector. Evaluates both e1 and e2. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, compoundBinary('|='), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, compoundBinary('|='), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('or_short_circuit') @@ -139,7 +139,7 @@ short_circuiting "and". Yields true if both e1 and e2 are true; evaluates e2 onl { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('||'), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('||'), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('equals') @@ -161,7 +161,7 @@ Equality. Component-wise when T is a vector. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('=='), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('not_equals') @@ -183,5 +183,5 @@ Equality. Component-wise when T is a vector. { input: [bool(true), bool(true)], expected: bool(false) }, ]; - await run(t, binary('!='), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.bool, Type.bool], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts index 7f94c7e307a9..d9aa44bba0e2 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('+'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('+'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('+'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('+'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('+='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('+='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('+='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts index 92d926a412b9..c0c0d4f8a4a6 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts @@ -1,5 +1,5 @@ import { anyOf } from '../../../../util/compare.js'; -import { bool, f16, Scalar } from '../../../../util/conversion.js'; +import { bool, f16, ScalarValue } from '../../../../util/conversion.js'; import { flushSubnormalNumberF16, vectorF16Range } from '../../../../util/math.js'; import { Case } from '../case.js'; import { makeCaseCache } from '../case_cache.js'; @@ -11,7 +11,7 @@ import { makeCaseCache } from '../case_cache.js'; function makeCase( lhs: number, rhs: number, - truthFunc: (lhs: Scalar, rhs: Scalar) => boolean + truthFunc: (lhs: ScalarValue, rhs: ScalarValue) => boolean ): Case { // Subnormal float values may be flushed at any time. // https://www.w3.org/TR/WGSL/#floating-point-evaluation @@ -19,7 +19,7 @@ function makeCase( const f16_rhs = f16(rhs); const lhs_options = new Set([f16_lhs, f16(flushSubnormalNumberF16(lhs))]); const rhs_options = new Set([f16_rhs, f16(flushSubnormalNumberF16(rhs))]); - const expected: Array = []; + const expected: Array = []; lhs_options.forEach(l => { rhs_options.forEach(r => { const result = bool(truthFunc(l, r)); @@ -34,7 +34,7 @@ function makeCase( export const d = makeCaseCache('binary/f16_logical', { equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -43,7 +43,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -52,7 +52,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, not_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -61,7 +61,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, not_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -70,7 +70,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, less_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -79,7 +79,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, less_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -88,7 +88,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, less_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -97,7 +97,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, less_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -106,7 +106,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, greater_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -115,7 +115,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, greater_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -124,7 +124,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, greater_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; @@ -133,7 +133,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, greater_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; diff --git a/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts index c84080983de5..b978cd3c99fe 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the f16 comparison operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF16 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; @@ -30,7 +30,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'equals_const' : 'equals_non_const' ); - await run(t, binary('=='), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('not_equals') @@ -51,7 +51,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'not_equals_const' : 'not_equals_non_const' ); - await run(t, binary('!='), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('less_than') @@ -72,7 +72,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'less_than_const' : 'less_than_non_const' ); - await run(t, binary('<'), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('less_equals') @@ -93,7 +93,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'less_equals_const' : 'less_equals_non_const' ); - await run(t, binary('<='), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('greater_than') @@ -114,7 +114,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'greater_than_const' : 'greater_than_non_const' ); - await run(t, binary('>'), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -135,5 +135,5 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'greater_equals_const' : 'greater_equals_non_const' ); - await run(t, binary('>='), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.f16, Type.f16], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts index 7dfc13f81a2f..8a155024db98 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 division expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('/'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('/'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('/'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('/'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('/='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('/='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('/'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('/='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('/'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts index daf768a5365c..7c34b0cadd87 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix f16 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -38,8 +38,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeMat(cols, rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('+='), - [TypeMat(cols, rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts index 69c56c60c368..80ca78f7f5bd 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-matrix f16 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -44,8 +44,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(x_cols, x_rows, TypeF16), TypeMat(y_cols, y_rows, TypeF16)], - TypeMat(y_cols, x_rows, TypeF16), + [Type.mat(x_cols, x_rows, Type.f16), Type.mat(y_cols, y_rows, Type.f16)], + Type.mat(y_cols, x_rows, Type.f16), t.params, cases ); @@ -82,8 +82,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeMat(x_cols, x_rows, TypeF16), TypeMat(y_cols, y_rows, TypeF16)], - TypeMat(y_cols, x_rows, TypeF16), + [Type.mat(x_cols, x_rows, Type.f16), Type.mat(y_cols, y_rows, Type.f16)], + Type.mat(y_cols, x_rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts index 338d6d021bb5..aa7087738a5e 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-scalar and scalar-matrix f16 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -40,8 +40,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(cols, rows, TypeF16), TypeF16], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.f16], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -75,8 +75,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeMat(cols, rows, TypeF16), TypeF16], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.f16], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -110,8 +110,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeF16, TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.f16, Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts index 442c31ef82e9..e8e13d902a0e 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix f16 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -38,8 +38,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeMat(cols, rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('-='), - [TypeMat(cols, rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts index 9715fe681e35..557a7cead847 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-vector and vector-matrix f16 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -40,8 +40,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(cols, rows, TypeF16), TypeVec(cols, TypeF16)], - TypeVec(rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.vec(cols, Type.f16)], + Type.vec(rows, Type.f16), t.params, cases ); @@ -75,8 +75,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeVec(rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeVec(cols, TypeF16), + [Type.vec(rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.vec(cols, Type.f16), t.params, cases ); @@ -105,8 +105,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeVec(rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeVec(cols, TypeF16), + [Type.vec(rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.vec(cols, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts index 005a9a1e64af..81339d9266b5 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('*'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('*'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('*'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('*'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('*='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('*='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts index c2f5cd7af11f..0fe1cc53c662 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 remainder expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('%'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('%'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('%'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('%'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('%='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('%='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('%'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('%='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('%'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts index a458885ae8ce..6b29aad6ad78 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('-'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('-'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('-'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('-'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('-='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('-='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('-='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts index 6bd8b0e7bdb7..9a502ae67719 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('+'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('+'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('+'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('+'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('+='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('+='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('+='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts index 4c16fc55f0e5..28fb22e8202a 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts @@ -1,5 +1,5 @@ import { anyOf } from '../../../../util/compare.js'; -import { bool, f32, Scalar } from '../../../../util/conversion.js'; +import { bool, f32, ScalarValue } from '../../../../util/conversion.js'; import { flushSubnormalNumberF32, vectorF32Range } from '../../../../util/math.js'; import { Case } from '../case.js'; import { makeCaseCache } from '../case_cache.js'; @@ -11,7 +11,7 @@ import { makeCaseCache } from '../case_cache.js'; function makeCase( lhs: number, rhs: number, - truthFunc: (lhs: Scalar, rhs: Scalar) => boolean + truthFunc: (lhs: ScalarValue, rhs: ScalarValue) => boolean ): Case { // Subnormal float values may be flushed at any time. // https://www.w3.org/TR/WGSL/#floating-point-evaluation @@ -19,7 +19,7 @@ function makeCase( const f32_rhs = f32(rhs); const lhs_options = new Set([f32_lhs, f32(flushSubnormalNumberF32(lhs))]); const rhs_options = new Set([f32_rhs, f32(flushSubnormalNumberF32(rhs))]); - const expected: Array = []; + const expected: Array = []; lhs_options.forEach(l => { rhs_options.forEach(r => { const result = bool(truthFunc(l, r)); @@ -34,7 +34,7 @@ function makeCase( export const d = makeCaseCache('binary/f32_logical', { equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -43,7 +43,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -52,7 +52,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, not_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -61,7 +61,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, not_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -70,7 +70,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, less_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -79,7 +79,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, less_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -88,7 +88,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, less_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -97,7 +97,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, less_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -106,7 +106,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, greater_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -115,7 +115,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, greater_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -124,7 +124,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, greater_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; @@ -133,7 +133,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, greater_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; diff --git a/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts index e5833a0f9f33..42eb8934a440 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the f32 comparison operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; @@ -27,7 +27,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'equals_const' : 'equals_non_const' ); - await run(t, binary('=='), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('not_equals') @@ -45,7 +45,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'not_equals_const' : 'not_equals_non_const' ); - await run(t, binary('!='), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('less_than') @@ -63,7 +63,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'less_than_const' : 'less_than_non_const' ); - await run(t, binary('<'), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('less_equals') @@ -81,7 +81,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'less_equals_const' : 'less_equals_non_const' ); - await run(t, binary('<='), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('greater_than') @@ -99,7 +99,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'greater_than_const' : 'greater_than_non_const' ); - await run(t, binary('>'), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -117,5 +117,5 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'greater_equals_const' : 'greater_equals_non_const' ); - await run(t, binary('>='), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.f32, Type.f32], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts index 0c8dd293f57d..bdd71e69eb52 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 division expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('/'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('/'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('/'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('/'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('/='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('/='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('/'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('/='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('/'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts index 4fcb4d08927d..06a4ac47cc5b 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix f32 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -35,8 +35,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeMat(cols, rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -65,8 +65,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('+='), - [TypeMat(cols, rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts index ad34d642d26e..1ff7799f4625 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-matrix f32 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -41,8 +41,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(x_cols, x_rows, TypeF32), TypeMat(y_cols, y_rows, TypeF32)], - TypeMat(y_cols, x_rows, TypeF32), + [Type.mat(x_cols, x_rows, Type.f32), Type.mat(y_cols, y_rows, Type.f32)], + Type.mat(y_cols, x_rows, Type.f32), t.params, cases ); @@ -76,8 +76,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeMat(x_cols, x_rows, TypeF32), TypeMat(y_cols, y_rows, TypeF32)], - TypeMat(y_cols, x_rows, TypeF32), + [Type.mat(x_cols, x_rows, Type.f32), Type.mat(y_cols, y_rows, Type.f32)], + Type.mat(y_cols, x_rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts index 0558862d065d..e8771d19eb5d 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-scalar and scalar-matrix f32 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -37,8 +37,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(cols, rows, TypeF32), TypeF32], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.f32], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -69,8 +69,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeMat(cols, rows, TypeF32), TypeF32], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.f32], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -101,8 +101,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeF32, TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.f32, Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts index 8bcf9a47895c..31565ba598d5 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix f32 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -35,8 +35,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeMat(cols, rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -65,8 +65,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('-='), - [TypeMat(cols, rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts index a04a28cfeff3..8cd7ed49fe0a 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-vector and vector-matrix f32 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -37,8 +37,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(cols, rows, TypeF32), TypeVec(cols, TypeF32)], - TypeVec(rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.vec(cols, Type.f32)], + Type.vec(rows, Type.f32), t.params, cases ); @@ -69,8 +69,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeVec(rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeVec(cols, TypeF32), + [Type.vec(rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.vec(cols, Type.f32), t.params, cases ); @@ -96,8 +96,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeVec(rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeVec(cols, TypeF32), + [Type.vec(rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.vec(cols, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts index 77e670795d2d..478ca71ef0fc 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('*'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('*'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('*'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('*'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('*='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('*='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts index b61d3ecc633a..3a9acb02e095 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 remainder expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('%'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('%'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('%'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('%'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('%='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('%='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('%'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('%='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('%'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts index e873c6ebc3b1..55097390e935 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('-'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('-'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('-'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('-'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('-='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('-='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('-='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts index ef201f66689f..ef8ea0044782 100644 --- a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 arithmetic binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeI32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -24,7 +24,7 @@ Expression: x + y ) .fn(async t => { const cases = await d.get('addition'); - await run(t, binary('+'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('+'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('addition_compound') @@ -39,7 +39,7 @@ Expression: x += y ) .fn(async t => { const cases = await d.get('addition'); - await run(t, compoundBinary('+='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('+='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('subtraction') @@ -54,7 +54,7 @@ Expression: x - y ) .fn(async t => { const cases = await d.get('subtraction'); - await run(t, binary('-'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('-'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('subtraction_compound') @@ -69,7 +69,7 @@ Expression: x -= y ) .fn(async t => { const cases = await d.get('subtraction'); - await run(t, compoundBinary('-='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('-='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('multiplication') @@ -84,7 +84,7 @@ Expression: x * y ) .fn(async t => { const cases = await d.get('multiplication'); - await run(t, binary('*'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('*'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('multiplication_compound') @@ -99,7 +99,7 @@ Expression: x *= y ) .fn(async t => { const cases = await d.get('multiplication'); - await run(t, compoundBinary('*='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('*='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('division') @@ -116,7 +116,7 @@ Expression: x / y const cases = await d.get( t.params.inputSource === 'const' ? 'division_const' : 'division_non_const' ); - await run(t, binary('/'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('/'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('division_compound') @@ -133,7 +133,7 @@ Expression: x /= y const cases = await d.get( t.params.inputSource === 'const' ? 'division_const' : 'division_non_const' ); - await run(t, compoundBinary('/='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('/='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('remainder') @@ -150,7 +150,7 @@ Expression: x % y const cases = await d.get( t.params.inputSource === 'const' ? 'remainder_const' : 'remainder_non_const' ); - await run(t, binary('%'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('%'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('remainder_compound') @@ -167,7 +167,7 @@ Expression: x %= y const cases = await d.get( t.params.inputSource === 'const' ? 'remainder_const' : 'remainder_non_const' ); - await run(t, compoundBinary('%='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('%='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('addition_scalar_vector') @@ -182,9 +182,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`addition_scalar_vector${vec_size}`); - await run(t, binary('+'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('+'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('addition_vector_scalar') @@ -199,9 +199,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, binary('+'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('+'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('addition_vector_scalar_compound') @@ -216,9 +216,9 @@ Expression: x += y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, compoundBinary('+='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('+='), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('subtraction_scalar_vector') @@ -233,9 +233,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`subtraction_scalar_vector${vec_size}`); - await run(t, binary('-'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('-'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar') @@ -250,9 +250,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, binary('-'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('-'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar_compound') @@ -267,9 +267,9 @@ Expression: x -= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, compoundBinary('-='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('-='), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('multiplication_scalar_vector') @@ -284,9 +284,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`multiplication_scalar_vector${vec_size}`); - await run(t, binary('*'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('*'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar') @@ -301,9 +301,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, binary('*'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('*'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar_compound') @@ -318,9 +318,9 @@ Expression: x *= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, compoundBinary('*='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('*='), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('division_scalar_vector') @@ -335,10 +335,10 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_scalar_vector${vec_size}_${source}`); - await run(t, binary('/'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('/'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('division_vector_scalar') @@ -353,10 +353,10 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_vector${vec_size}_scalar_${source}`); - await run(t, binary('/'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('/'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('division_vector_scalar_compound') @@ -371,10 +371,10 @@ Expression: x /= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_vector${vec_size}_scalar_${source}`); - await run(t, compoundBinary('/='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('/='), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('remainder_scalar_vector') @@ -389,10 +389,10 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_scalar_vector${vec_size}_${source}`); - await run(t, binary('%'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('%'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('remainder_vector_scalar') @@ -407,10 +407,10 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_vector${vec_size}_scalar_${source}`); - await run(t, binary('%'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('%'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('remainder_vector_scalar_compound') @@ -425,8 +425,8 @@ Expression: x %= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_vector${vec_size}_scalar_${source}`); - await run(t, compoundBinary('%='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('%='), [vec_type, Type.i32], vec_type, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts index c07d09a058ed..9b6566c9a4a8 100644 --- a/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 comparison expressions import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeI32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; @@ -24,7 +24,7 @@ Expression: x == y ) .fn(async t => { const cases = await d.get('equals'); - await run(t, binary('=='), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('not_equals') @@ -39,7 +39,7 @@ Expression: x != y ) .fn(async t => { const cases = await d.get('not_equals'); - await run(t, binary('!='), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('less_than') @@ -54,7 +54,7 @@ Expression: x < y ) .fn(async t => { const cases = await d.get('less_than'); - await run(t, binary('<'), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('less_equals') @@ -69,7 +69,7 @@ Expression: x <= y ) .fn(async t => { const cases = await d.get('less_equal'); - await run(t, binary('<='), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('greater_than') @@ -84,7 +84,7 @@ Expression: x > y ) .fn(async t => { const cases = await d.get('greater_than'); - await run(t, binary('>'), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -99,5 +99,5 @@ Expression: x >= y ) .fn(async t => { const cases = await d.get('greater_equal'); - await run(t, binary('>='), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.i32, Type.i32], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts index 28c25fef8dc9..6df16f303c7f 100644 --- a/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the u32 arithmetic binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeU32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -24,7 +24,7 @@ Expression: x + y ) .fn(async t => { const cases = await d.get('addition'); - await run(t, binary('+'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('+'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('addition_compound') @@ -39,7 +39,7 @@ Expression: x += y ) .fn(async t => { const cases = await d.get('addition'); - await run(t, compoundBinary('+='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('+='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('subtraction') @@ -54,7 +54,7 @@ Expression: x - y ) .fn(async t => { const cases = await d.get('subtraction'); - await run(t, binary('-'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('-'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('subtraction_compound') @@ -69,7 +69,7 @@ Expression: x -= y ) .fn(async t => { const cases = await d.get('subtraction'); - await run(t, compoundBinary('-='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('-='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('multiplication') @@ -84,7 +84,7 @@ Expression: x * y ) .fn(async t => { const cases = await d.get('multiplication'); - await run(t, binary('*'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('*'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('multiplication_compound') @@ -99,7 +99,7 @@ Expression: x *= y ) .fn(async t => { const cases = await d.get('multiplication'); - await run(t, compoundBinary('*='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('*='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('division') @@ -116,7 +116,7 @@ Expression: x / y const cases = await d.get( t.params.inputSource === 'const' ? 'division_const' : 'division_non_const' ); - await run(t, binary('/'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('/'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('division_compound') @@ -133,7 +133,7 @@ Expression: x /= y const cases = await d.get( t.params.inputSource === 'const' ? 'division_const' : 'division_non_const' ); - await run(t, compoundBinary('/='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('/='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('remainder') @@ -150,7 +150,7 @@ Expression: x % y const cases = await d.get( t.params.inputSource === 'const' ? 'remainder_const' : 'remainder_non_const' ); - await run(t, binary('%'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('%'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('remainder_compound') @@ -167,7 +167,7 @@ Expression: x %= y const cases = await d.get( t.params.inputSource === 'const' ? 'remainder_const' : 'remainder_non_const' ); - await run(t, compoundBinary('%='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('%='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('addition_scalar_vector') @@ -182,9 +182,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`addition_scalar_vector${vec_size}`); - await run(t, binary('+'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('+'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('addition_vector_scalar') @@ -199,9 +199,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, binary('+'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('+'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('addition_vector_scalar_compound') @@ -216,9 +216,9 @@ Expression: x += y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, compoundBinary('+='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('+='), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('subtraction_scalar_vector') @@ -233,9 +233,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`subtraction_scalar_vector${vec_size}`); - await run(t, binary('-'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('-'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar') @@ -250,9 +250,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, binary('-'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('-'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar_compound') @@ -267,9 +267,9 @@ Expression: x -= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, compoundBinary('-='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('-='), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('multiplication_scalar_vector') @@ -284,9 +284,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`multiplication_scalar_vector${vec_size}`); - await run(t, binary('*'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('*'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar') @@ -301,9 +301,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, binary('*'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('*'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar_compound') @@ -318,9 +318,9 @@ Expression: x *= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, compoundBinary('*='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('*='), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('division_scalar_vector') @@ -335,10 +335,10 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_scalar_vector${vec_size}_${source}`); - await run(t, binary('/'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('/'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('division_vector_scalar') @@ -353,10 +353,10 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_vector${vec_size}_scalar_${source}`); - await run(t, binary('/'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('/'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('division_vector_scalar_compound') @@ -371,10 +371,10 @@ Expression: x /= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_vector${vec_size}_scalar_${source}`); - await run(t, compoundBinary('/='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('/='), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('remainder_scalar_vector') @@ -389,10 +389,10 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_scalar_vector${vec_size}_${source}`); - await run(t, binary('%'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('%'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('remainder_vector_scalar') @@ -407,10 +407,10 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_vector${vec_size}_scalar_${source}`); - await run(t, binary('%'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('%'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('remainder_vector_scalar_compound') @@ -425,8 +425,8 @@ Expression: x %= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_vector${vec_size}_scalar_${source}`); - await run(t, compoundBinary('%='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('%='), [vec_type, Type.u32], vec_type, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts index fbf34b510394..5bb6767b01e0 100644 --- a/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the u32 comparison expressions import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; @@ -24,7 +24,7 @@ Expression: x == y ) .fn(async t => { const cases = await d.get('equals'); - await run(t, binary('=='), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('not_equals') @@ -39,7 +39,7 @@ Expression: x != y ) .fn(async t => { const cases = await d.get('not_equals'); - await run(t, binary('!='), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('less_than') @@ -54,7 +54,7 @@ Expression: x < y ) .fn(async t => { const cases = await d.get('less_than'); - await run(t, binary('<'), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('less_equals') @@ -69,7 +69,7 @@ Expression: x <= y ) .fn(async t => { const cases = await d.get('less_equal'); - await run(t, binary('<='), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('greater_than') @@ -84,7 +84,7 @@ Expression: x > y ) .fn(async t => { const cases = await d.get('greater_than'); - await run(t, binary('>'), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -99,5 +99,5 @@ Expression: x >= y ) .fn(async t => { const cases = await d.get('greater_equal'); - await run(t, binary('>='), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.u32, Type.u32], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts index c7f6d1d57cae..75d41ab16377 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts +++ b/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 @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 @const fn abs(e: T ) -> T Returns the absolute value of e (e.g. e with a positive sign bit). @@ -18,16 +18,7 @@ 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, - TypeAbstractInt, -} from '../../../../../util/conversion.js'; +import { Type, i32Bits, u32Bits } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { d } from './abs.cache.js'; @@ -45,7 +36,7 @@ g.test('abstract_int') ) .fn(async t => { const cases = await d.get('abstract_int'); - await run(t, abstractIntBuiltin('abs'), [TypeAbstractInt], TypeAbstractInt, t.params, cases); + await run(t, abstractIntBuiltin('abs'), [Type.abstractInt], Type.abstractInt, t.params, cases); }); g.test('u32') @@ -55,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) }, @@ -102,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) }, @@ -158,8 +149,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('abs'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -173,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') @@ -187,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/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts index 1d489e1191b8..7d6b224579a9 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts +++ b/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 @const fn acos(e: T ) -> T Returns the arc cosine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './acos.cache.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts index 737bd5677241..ed34d326de1a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts +++ b/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 @const fn acosh(e: T ) -> T Returns the hyperbolic arc cosine of e. The result is 0 when e < 1. @@ -13,7 +13,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './acosh.cache.js'; @@ -37,7 +37,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') @@ -51,5 +51,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/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts index 9a2938c1d500..74e072703d94 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts index 19ed7d186f7b..43c599e2aa4b 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts index 8a7934936030..5ff882d28a34 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts +++ b/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 @const fn asin(e: T ) -> T Returns the arc sine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './asin.cache.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts index b5f362066b85..21f2584cac98 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts +++ b/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 @const fn asinh(e: T ) -> T Returns the hyperbolic arc sine of e. @@ -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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './asinh.cache.js'; @@ -36,7 +36,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') @@ -50,5 +50,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/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts index 173fb0a0010e..355529eb8dfe 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts +++ b/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 @const fn atan(e: T ) -> T Returns the arc tangent of e. Component-wise when T is a vector. @@ -10,7 +10,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './atan.cache.js'; @@ -40,7 +40,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') @@ -54,5 +54,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/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts index dff1d442f56b..707820c2abdb 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts +++ b/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 @const fn atan2(e1: T ,e2: T ) -> T Returns the arc tangent of e1 over e2. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './atan2.cache.js'; @@ -39,7 +39,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') @@ -53,5 +53,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/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts index 31c14110b662..fe43c2161336 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts +++ b/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 @const fn atanh(e: T ) -> T Returns the hyperbolic arc tangent of e. The result is 0 when abs(e) ≥ 1. @@ -12,7 +12,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './atanh.cache.js'; @@ -36,7 +36,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') @@ -50,5 +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('atanh'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('atanh'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts index a3f2ee35b29b..b64d3692121e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts @@ -2,8 +2,8 @@ import { assert } from '../../../../../../common/util/util.js'; import { Comparator, alwaysPass, anyOf } from '../../../../../util/compare.js'; import { kBit, kValue } from '../../../../../util/constants.js'; import { - Scalar, - Vector, + ScalarValue, + VectorValue, f16, f32, i32, @@ -63,7 +63,7 @@ const f32ZerosInterval: FPInterval = new FPInterval('f32', -0.0, 0.0); const f32FiniteRange: number[] = [...scalarF32Range(), kValue.f32.negative.zero]; const f32RangeWithInfAndNaN: number[] = [...f32FiniteRange, ...f32InfAndNaNInF32]; -// F16 values, finite, Inf/NaN, and zeros. Represented in float and u16. +// 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)); @@ -118,7 +118,7 @@ function u32ToU16x2(u32: number): number[] { /** * @returns a vec2 from an array of two u16, each reinterpreted as f16. */ -function u16x2ToVec2F16(u16x2: number[]): Vector { +function u16x2ToVec2F16(u16x2: number[]): VectorValue { assert(u16x2.length === 2); return toVector(u16x2.map(reinterpretU16AsF16), f16); } @@ -126,7 +126,7 @@ function u16x2ToVec2F16(u16x2: number[]): Vector { /** * @returns a vec4 from an array of four u16, each reinterpreted as f16. */ -function u16x4ToVec4F16(u16x4: number[]): Vector { +function u16x4ToVec4F16(u16x4: number[]): VectorValue { assert(u16x4.length === 4); return toVector(u16x4.map(reinterpretU16AsF16), f16); } @@ -433,7 +433,7 @@ function bitcastVec2F32ToVec4F16Comparator(f32x2: number[]): Comparator { 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)[]; + possibleExpectations: (ScalarValue | FPInterval)[]; isUnbounded: boolean; } @@ -458,8 +458,8 @@ function possible32BitScalarIntervalsFromF16x2( ): ExpectionFor32BitsScalarFromF16x2 { assert(f16x2InU16x2.length === 2); let reinterpretFromU32: (x: number) => number; - let expectationsForValue: (x: number) => Scalar[] | FPInterval[]; - let unboundedExpectations: FPInterval[] | Scalar[]; + let expectationsForValue: (x: number) => ScalarValue[] | FPInterval[]; + let unboundedExpectations: FPInterval[] | ScalarValue[]; if (type === 'u32') { reinterpretFromU32 = (x: number) => x; expectationsForValue = x => [u32(x)]; @@ -498,12 +498,12 @@ function possible32BitScalarIntervalsFromF16x2( return { possibleExpectations: unboundedExpectations, isUnbounded: true }; } const possibleU16Bits = f16x2InU16x2.map(possibleBitsInU16FromFiniteF16InU16); - const possibleExpectations = cartesianProduct(...possibleU16Bits).flatMap( - (possibleBitsU16x2: readonly number[]) => { - assert(possibleBitsU16x2.length === 2); - return expectationsForValue(reinterpretFromU32(u16x2ToU32(possibleBitsU16x2))); - } - ); + const possibleExpectations = cartesianProduct(...possibleU16Bits).flatMap< + ScalarValue | FPInterval + >((possibleBitsU16x2: readonly number[]) => { + assert(possibleBitsU16x2.length === 2); + return expectationsForValue(reinterpretFromU32(u16x2ToU32(possibleBitsU16x2))); + }); return { possibleExpectations, isUnbounded: false }; } @@ -566,7 +566,7 @@ function bitcastVec4F16ToVec2U32Comparator(vec4F16InU16x4: number[]): Comparator } return anyOf( ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map( - e => new Vector(e as Scalar[]) + e => new VectorValue(e as ScalarValue[]) ) ); } @@ -588,7 +588,7 @@ function bitcastVec4F16ToVec2I32Comparator(vec4F16InU16x4: number[]): Comparator } return anyOf( ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map( - e => new Vector(e as Scalar[]) + e => new VectorValue(e as ScalarValue[]) ) ); } diff --git a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts index 4c1a8aa618e5..0ead5af05a5b 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts @@ -11,8 +11,8 @@ 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(e : AbstractInt) -> T -@const @must_use fn bitcast>(e : vecN) -> T +@const @must_use fn bitcast(e : Type.abstractInt) -> T +@const @must_use fn bitcast>(e : vecN) -> T @const @must_use fn bitcast(e: vec2 ) -> T @const @must_use fn bitcast>(e: vec4 ) -> vec2 @@ -26,18 +26,13 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { GPUTest } from '../../../../../gpu_test.js'; import { anyOf } from '../../../../../util/compare.js'; import { - TypeF16, - TypeF32, - TypeI32, - TypeU32, - TypeVec, - TypeAbstractFloat, f32, u32, i32, abstractFloat, uint32ToFloat32, u32Bits, + Type, } from '../../../../../util/conversion.js'; import { FP } from '../../../../../util/floating_point.js'; import { scalarF32Range } from '../../../../../util/math.js'; @@ -76,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') @@ -90,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') @@ -107,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 @@ -122,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') @@ -139,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 @@ -154,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') @@ -171,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 @@ -189,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') @@ -206,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 @@ -231,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 @@ -247,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', t.params), - [TypeI32], - TypeVec(2, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.i32], Type.vec2h, t.params, cases); }); g.test('u32_to_vec2h') @@ -269,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', t.params), - [TypeU32], - TypeVec(2, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.u32], Type.vec2h, t.params, cases); }); g.test('f32_to_vec2h') @@ -291,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', t.params), - [TypeF32], - TypeVec(2, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.f32], Type.vec2h, t.params, cases); }); // f16: vec2<32-bit scalar numeric> to vec4 @@ -314,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', t.params), - [TypeVec(2, TypeI32)], - TypeVec(4, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec4', t.params), [Type.vec2i], Type.vec4h, t.params, cases); }); g.test('vec2u_to_vec4h') @@ -336,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', t.params), - [TypeVec(2, TypeU32)], - TypeVec(4, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec4', t.params), [Type.vec2u], Type.vec4h, t.params, cases); }); g.test('vec2f_to_vec4h') @@ -360,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', t.params), - [TypeVec(2, TypeF32)], - TypeVec(4, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec4', t.params), [Type.vec2f], Type.vec4h, t.params, cases); }); // f16: vec2 to 32-bit scalar numeric @@ -383,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') @@ -398,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') @@ -413,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 to vec2<32-bit scalar numeric> @@ -429,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', t.params), - [TypeVec(4, TypeF16)], - TypeVec(2, TypeI32), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.vec4h], Type.vec2i, t.params, cases); }); g.test('vec4h_to_vec2u') @@ -451,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', t.params), - [TypeVec(4, TypeF16)], - TypeVec(2, TypeU32), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.vec4h], Type.vec2u, t.params, cases); }); g.test('vec4h_to_vec2f') @@ -475,14 +414,7 @@ g.test('vec4h_to_vec2f') ? 'vec4_f16_to_vec2_f32_finite' : 'vec4_f16_inf_nan_to_vec2_f32' ); - await run( - t, - bitcastBuilder('vec2', t.params), - [TypeVec(4, TypeF16)], - TypeVec(2, TypeF32), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.vec4h], Type.vec2f, t.params, cases); }); // Abstract Float @@ -505,7 +437,7 @@ g.test('af_to_f32') }; }); - await run(t, bitcastBuilder('f32', t.params), [TypeAbstractFloat], TypeF32, t.params, cases); + await run(t, bitcastBuilder('f32', t.params), [Type.abstractFloat], Type.f32, t.params, cases); }); g.test('af_to_i32') @@ -549,7 +481,7 @@ g.test('af_to_i32') }; }); - await run(t, bitcastBuilder('i32', t.params), [TypeAbstractFloat], TypeI32, t.params, cases); + await run(t, bitcastBuilder('i32', t.params), [Type.abstractFloat], Type.i32, t.params, cases); }); g.test('af_to_u32') @@ -593,7 +525,7 @@ g.test('af_to_u32') }; }); - await run(t, bitcastBuilder('u32', t.params), [TypeAbstractFloat], TypeU32, t.params, cases); + await run(t, bitcastBuilder('u32', t.params), [Type.abstractFloat], Type.u32, t.params, cases); }); g.test('af_to_vec2f16') @@ -609,8 +541,8 @@ g.test('af_to_vec2f16') await run( t, bitcastBuilder('vec2', t.params), - [TypeAbstractFloat], - TypeVec(2, TypeF16), + [Type.abstractFloat], + Type.vec2h, t.params, cases ); @@ -629,8 +561,8 @@ g.test('vec2af_to_vec4f16') await run( t, bitcastBuilder('vec4', t.params), - [TypeVec(2, TypeAbstractFloat)], - TypeVec(4, TypeF16), + [Type.vec(2, Type.abstractFloat)], + Type.vec4h, t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts index 18509efdffb9..5f6236149e53 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts +++ b/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 @const fn ceil(e: T ) -> T Returns the ceiling of e. Component-wise when T is a vector. @@ -10,7 +10,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -34,7 +34,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') @@ -48,5 +48,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/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts index ef63fb385ba5..909d15e7e752 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts @@ -1,5 +1,5 @@ import { kValue } from '../../../../../util/constants.js'; -import { ScalarType, TypeAbstractInt, TypeI32, TypeU32 } from '../../../../../util/conversion.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'; @@ -59,11 +59,11 @@ function generateAbstractIntegerTestCases(test_values: Array): Array ({ input: [ - TypeAbstractInt.create(e), - TypeAbstractInt.create(low), - TypeAbstractInt.create(high), + Type.abstractInt.create(e), + Type.abstractInt.create(low), + Type.abstractInt.create(high), ], - expected: TypeAbstractInt.create(minBigInt(maxBigInt(e, low), high)), + expected: Type.abstractInt.create(minBigInt(maxBigInt(e, low), high)), })) ) ); @@ -113,16 +113,16 @@ const fp_cases = (['f32', 'f16', 'abstract'] as const) export const d = makeCaseCache('clamp', { u32_non_const: () => { - return generateConcreteIntegerTestCases(u32Values, TypeU32, 'non_const'); + return generateConcreteIntegerTestCases(u32Values, Type.u32, 'non_const'); }, u32_const: () => { - return generateConcreteIntegerTestCases(u32Values, TypeU32, 'const'); + return generateConcreteIntegerTestCases(u32Values, Type.u32, 'const'); }, i32_non_const: () => { - return generateConcreteIntegerTestCases(i32Values, TypeI32, 'non_const'); + return generateConcreteIntegerTestCases(i32Values, Type.i32, 'non_const'); }, i32_const: () => { - return generateConcreteIntegerTestCases(i32Values, TypeI32, 'const'); + return generateConcreteIntegerTestCases(i32Values, Type.i32, 'const'); }, abstract_int: () => { return generateAbstractIntegerTestCases(abstractFloatValues); diff --git a/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts index cc29ceba03f7..0b524bccf0d3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts +++ b/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 @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 @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,14 +15,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - TypeAbstractFloat, - TypeAbstractInt, - TypeF16, - TypeF32, - TypeI32, - TypeU32, -} from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js'; @@ -43,8 +36,8 @@ g.test('abstract_int') await run( t, abstractIntBuiltin('clamp'), - [TypeAbstractInt, TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -58,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') @@ -69,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') @@ -85,8 +78,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('clamp'), - [TypeAbstractFloat, TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -100,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') @@ -114,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/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts index 5eb9081928d1..4e3756834def 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts +++ b/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 @const fn cos(e: T ) -> T Returns the cosine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -39,7 +39,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') @@ -53,5 +53,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/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts index aba7212a06e3..70713a392751 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts +++ b/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 @const fn cosh(e: T ) -> T Returns the hyperbolic cosine of e. Component-wise when T is a vector @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts index cfae4bb6e03b..ea0c38ae58d9 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts index f0be9162856a..1937e0428362 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts index d0b3198f49c4..3392a47810cf 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts index 95b39f45c5b3..cc32537076d1 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts @@ -1,14 +1,14 @@ 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 ,e2: vec3) -> vec3 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -25,8 +25,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('cross'), - [TypeVec(3, TypeAbstractFloat), TypeVec(3, TypeAbstractFloat)], - TypeVec(3, TypeAbstractFloat), + [Type.vec(3, Type.abstractFloat), Type.vec(3, Type.abstractFloat)], + Type.vec(3, Type.abstractFloat), t.params, cases ); @@ -38,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') @@ -57,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/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts index f69a92cf5f45..e8589c99332d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts +++ b/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 @const fn degrees(e1: T ) -> T Converts radians to degrees, approximating e1 × 180 ÷ π. Component-wise when T is a vector @@ -9,7 +9,7 @@ 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -30,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('degrees'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -45,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') @@ -59,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/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts index c628623f4378..8b259eedb33e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts @@ -1,14 +1,14 @@ 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 Returns the determinant of e. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeMat } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -50,5 +50,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/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts index ab63029ca227..0f50cd30d37a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts +++ b/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 @const fn distance(e1: T ,e2: T ) -> f32 Returns the distance between e1 and e2 (e.g. length(e1-e2)). @@ -10,7 +10,7 @@ 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 { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -32,7 +32,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') @@ -43,14 +43,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') @@ -61,14 +54,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') @@ -79,14 +65,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') @@ -98,7 +77,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') @@ -112,14 +91,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') @@ -133,14 +105,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') @@ -154,12 +119,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/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts index 5d27e5044dea..7d39aa0fbe25 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts @@ -1,21 +1,14 @@ 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,e2: vecN) -> T Returns the dot product of e1 and e2. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - TypeAbstractInt, - TypeF16, - TypeF32, - TypeI32, - TypeU32, - TypeVec, -} from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractIntBuiltin, builtin } from './builtin.js'; @@ -32,8 +25,8 @@ g.test('abstract_int_vec2') await run( t, abstractIntBuiltin('dot'), - [TypeVec(2, TypeAbstractInt), TypeVec(2, TypeAbstractInt)], - TypeAbstractInt, + [Type.vec(2, Type.abstractInt), Type.vec(2, Type.abstractInt)], + Type.abstractInt, t.params, cases ); @@ -48,8 +41,8 @@ g.test('abstract_int_vec3') await run( t, abstractIntBuiltin('dot'), - [TypeVec(3, TypeAbstractInt), TypeVec(3, TypeAbstractInt)], - TypeAbstractInt, + [Type.vec(3, Type.abstractInt), Type.vec(3, Type.abstractInt)], + Type.abstractInt, t.params, cases ); @@ -64,8 +57,8 @@ g.test('abstract_int_vec4') await run( t, abstractIntBuiltin('dot'), - [TypeVec(4, TypeAbstractInt), TypeVec(4, TypeAbstractInt)], - TypeAbstractInt, + [Type.vec(4, Type.abstractInt), Type.vec(4, Type.abstractInt)], + Type.abstractInt, t.params, cases ); @@ -77,14 +70,7 @@ g.test('i32_vec2') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('i32_vec2'); - await run( - t, - builtin('dot'), - [TypeVec(2, TypeI32), TypeVec(2, TypeI32)], - TypeI32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec2i, Type.vec2i], Type.i32, t.params, cases); }); g.test('i32_vec3') @@ -93,14 +79,7 @@ g.test('i32_vec3') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('i32_vec3'); - await run( - t, - builtin('dot'), - [TypeVec(3, TypeI32), TypeVec(3, TypeI32)], - TypeI32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec3i, Type.vec3i], Type.i32, t.params, cases); }); g.test('i32_vec4') @@ -109,14 +88,7 @@ g.test('i32_vec4') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('i32_vec4'); - await run( - t, - builtin('dot'), - [TypeVec(4, TypeI32), TypeVec(4, TypeI32)], - TypeI32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec4i, Type.vec4i], Type.i32, t.params, cases); }); g.test('u32_vec2') @@ -125,14 +97,7 @@ g.test('u32_vec2') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('u32_vec2'); - await run( - t, - builtin('dot'), - [TypeVec(2, TypeU32), TypeVec(2, TypeU32)], - TypeU32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec2u, Type.vec2u], Type.u32, t.params, cases); }); g.test('u32_vec3') @@ -141,14 +106,7 @@ g.test('u32_vec3') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('u32_vec3'); - await run( - t, - builtin('dot'), - [TypeVec(3, TypeU32), TypeVec(3, TypeU32)], - TypeU32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec3u, Type.vec3u], Type.u32, t.params, cases); }); g.test('u32_vec4') @@ -157,14 +115,7 @@ g.test('u32_vec4') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('u32_vec4'); - await run( - t, - builtin('dot'), - [TypeVec(4, TypeU32), TypeVec(4, TypeU32)], - TypeU32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec4u, Type.vec4u], Type.u32, t.params, cases); }); g.test('abstract_float') @@ -181,14 +132,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('dot'), - [TypeVec(2, TypeF32), TypeVec(2, TypeF32)], - TypeF32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec2f, Type.vec2f], Type.f32, t.params, cases); }); g.test('f32_vec3') @@ -199,14 +143,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') @@ -217,14 +154,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') @@ -238,14 +168,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') @@ -259,14 +182,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') @@ -280,12 +196,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/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts index dee5290281a8..de537c473e64 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts @@ -9,7 +9,7 @@ the multiply, and then the add operations are done in WGSL i32 with wrapping beh import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeI32, TypeU32, i32, u32 } from '../../../../../util/conversion.js'; +import { Type, i32, u32 } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -70,5 +70,5 @@ g.test('basic') return [makeCase(...(v as [number, number]))]; }); - await run(t, builtin('dot4I8Packed'), [TypeU32, TypeU32], TypeI32, cfg, cases); + await run(t, builtin('dot4I8Packed'), [Type.u32, Type.u32], Type.i32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts index f0dd6fc5081b..a12a3d012302 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts @@ -8,7 +8,7 @@ unsigned integer dot product of these two vectors. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, u32 } from '../../../../../util/conversion.js'; +import { Type, u32 } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -55,5 +55,5 @@ g.test('basic') return [makeCase(...(v as [number, number]))]; }); - await run(t, builtin('dot4U8Packed'), [TypeU32, TypeU32], TypeU32, cfg, cases); + await run(t, builtin('dot4U8Packed'), [Type.u32, Type.u32], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts index ad381df01690..09759ea190ef 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts +++ b/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 @const fn exp(e1: T ) -> T Returns the natural exponentiation of e1 (e.g. e^e1). Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts index cf0e3c3cbe81..f6b00c47ecca 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts +++ b/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 @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,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts index d535bf5d746a..ef04b661bddf 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts index c21541ab8839..fae851439fc9 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts @@ -1,14 +1,14 @@ export const description = ` Execution tests for the 'faceForward' builtin function -T is vecN, vecN, or vecN +T is vecN, vecN, or vecN @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 { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,8 +33,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 ); @@ -51,8 +51,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 ); @@ -69,8 +69,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 ); @@ -90,8 +90,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 ); @@ -111,8 +111,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 ); @@ -132,8 +132,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/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts index 26216563cd04..9248b1e2bf5c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts index 5c65f59d2813..a8dd27ee87b2 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts index 63b0a138a045..26cffe5d10da 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts +++ b/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 @const fn floor(e: T ) -> T Returns the floor of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -30,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('floor'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -45,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') @@ -59,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/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts index 2488603d8077..620792d42007 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts +++ b/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 @const fn fma(e1: T ,e2: T ,e3: T ) -> T Returns e1 * e2 + e3. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -30,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('fma'), - [TypeAbstractFloat, TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -45,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') @@ -59,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/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts index df9e1845273e..d840122b3978 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts +++ b/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 @const fn fract(e: T ) -> T Returns the fractional part of e, computed as e - floor(e). @@ -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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -34,7 +34,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') @@ -48,5 +48,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/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts index 7c107eb729a7..05f8de7f8936 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts @@ -1,5 +1,5 @@ import { skipUndefined } from '../../../../../util/compare.js'; -import { Scalar, Vector, i32, toVector } from '../../../../../util/conversion.js'; +import { ScalarValue, VectorValue, i32, toVector } from '../../../../../util/conversion.js'; import { FP } from '../../../../../util/floating_point.js'; import { frexp } from '../../../../../util/math.js'; import { Case } from '../../case.js'; @@ -8,8 +8,8 @@ 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'): Case { const fp = FP[trait]; - let toInput: (n: readonly number[]) => Scalar | Vector; - let toOutput: (n: readonly number[]) => Scalar | Vector; + 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); @@ -36,8 +36,8 @@ function makeCaseFract(v: number | readonly number[], trait: 'f32' | 'f16'): Cas /* @returns an exp Case for a given scalar or vector input */ function makeCaseExp(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; + 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); diff --git a/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts index 663e345f499a..ee506decd9d0 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts @@ -15,7 +15,7 @@ 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 { TypeF16, TypeF32, TypeI32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, basicExpressionBuilder, run } from '../../expression.js'; import { d } from './frexp.cache.js'; @@ -46,7 +46,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') @@ -64,7 +64,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') @@ -82,7 +82,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') @@ -100,7 +100,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') @@ -118,7 +118,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') @@ -136,7 +136,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') @@ -154,7 +154,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') @@ -172,7 +172,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') @@ -193,7 +193,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') @@ -214,7 +214,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') @@ -235,7 +235,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') @@ -256,7 +256,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') @@ -277,7 +277,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') @@ -298,7 +298,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') @@ -319,7 +319,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') @@ -340,5 +340,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/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts index 1068e76252c2..b3eb65781d59 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts index fb5248ec8ad6..c27c56d4b5dd 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts +++ b/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 @const fn inverseSqrt(e: T ) -> T Returns the reciprocal of sqrt(e). Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts index e2eba8232d43..b06a3ef55086 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts +++ b/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 -K is AbstractInt, i32 +K is Type.abstractInt, i32 I is K or vecN, where I is a scalar if T is a scalar, or a vector when T is a vector @@ -14,7 +14,7 @@ Returns e1 * 2^e2. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeI32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -41,7 +41,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') @@ -55,5 +55,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/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts index e91d5dcdafe5..690bc6152b37 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts +++ b/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 @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,7 +9,7 @@ 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 { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -31,7 +31,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') @@ -42,7 +42,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') @@ -53,7 +53,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') @@ -64,7 +64,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') @@ -76,7 +76,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') @@ -90,7 +90,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') @@ -104,7 +104,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') @@ -118,5 +118,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/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts index 387705320ff9..1d9814679002 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts +++ b/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 @const fn log(e: T ) -> T Returns the natural logarithm of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -39,7 +39,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') @@ -53,5 +53,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/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts index 86a38a60246a..497c66676cc3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts +++ b/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 @const fn log2(e: T ) -> T Returns the base-2 logarithm of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -39,7 +39,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') @@ -53,5 +53,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/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts index 63e3fda3f2b6..ee7cb0d674f5 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts +++ b/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 @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 @const fn max(e1: T ,e2: T) -> T Returns e2 if e1 is less than e2, and e1 otherwise. @@ -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 { - TypeAbstractFloat, - TypeF16, - TypeF32, - TypeI32, - TypeU32, - i32, - u32, - abstractInt, - TypeAbstractInt, -} from '../../../../../util/conversion.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'; @@ -66,8 +56,8 @@ g.test('abstract_int') await run( t, abstractIntBuiltin('max'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -87,7 +77,7 @@ g.test('u32') 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') @@ -104,7 +94,7 @@ g.test('i32') 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') @@ -120,8 +110,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('max'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -135,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') @@ -149,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/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts index 30f411196047..ac636413997d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts +++ b/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 @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 @const fn min(e1: T ,e2: T) -> T Returns e2 if e2 is less than e1, and e1 otherwise. @@ -17,17 +17,7 @@ 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, - TypeI32, - TypeU32, - i32, - u32, - abstractInt, - TypeAbstractInt, -} from '../../../../../util/conversion.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'; @@ -65,8 +55,8 @@ g.test('abstract_int') await run( t, abstractIntBuiltin('min'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -86,7 +76,7 @@ g.test('u32') 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') @@ -103,7 +93,7 @@ g.test('i32') 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') @@ -119,8 +109,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('min'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -134,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') @@ -148,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/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts index d502c639620b..0005ab5c0eeb 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts +++ b/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 @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 @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,7 +16,7 @@ Same as mix(e1,e2,T2(e3)). import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -37,8 +37,8 @@ g.test('abstract_float_matching') await run( t, abstractFloatBuiltin('mix'), - [TypeAbstractFloat, TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -53,8 +53,8 @@ g.test('abstract_float_nonmatching_vec2') await run( t, abstractFloatBuiltin('mix'), - [TypeVec(2, TypeAbstractFloat), TypeVec(2, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(2, TypeAbstractFloat), + [Type.vec(2, Type.abstractFloat), Type.vec(2, Type.abstractFloat), Type.abstractFloat], + Type.vec(2, Type.abstractFloat), t.params, cases ); @@ -69,8 +69,8 @@ g.test('abstract_float_nonmatching_vec3') await run( t, abstractFloatBuiltin('mix'), - [TypeVec(3, TypeAbstractFloat), TypeVec(3, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(3, TypeAbstractFloat), + [Type.vec(3, Type.abstractFloat), Type.vec(3, Type.abstractFloat), Type.abstractFloat], + Type.vec(3, Type.abstractFloat), t.params, cases ); @@ -85,8 +85,8 @@ g.test('abstract_float_nonmatching_vec4') await run( t, abstractFloatBuiltin('mix'), - [TypeVec(4, TypeAbstractFloat), TypeVec(4, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(4, TypeAbstractFloat), + [Type.vec(4, Type.abstractFloat), Type.vec(4, Type.abstractFloat), Type.abstractFloat], + Type.vec(4, Type.abstractFloat), t.params, cases ); @@ -100,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') @@ -111,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') @@ -129,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') @@ -147,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') @@ -168,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') @@ -182,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') @@ -203,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') @@ -224,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/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts index 3e12d4f8696d..6c988008f825 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts +++ b/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 @const fn modf(e:T) -> result_struct Splits the components of |e| into fractional and whole number parts. @@ -18,7 +18,7 @@ Returns the result_struct for the given type. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { abstractFloatShaderBuilder, allInputSources, @@ -67,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') @@ -85,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') @@ -103,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') @@ -121,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') @@ -139,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') @@ -157,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') @@ -175,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') @@ -193,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') @@ -214,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') @@ -235,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') @@ -256,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') @@ -277,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') @@ -298,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') @@ -319,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') @@ -340,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') @@ -361,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') @@ -418,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 ); @@ -443,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 ); @@ -468,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 ); @@ -493,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 ); @@ -518,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 ); @@ -543,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/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts index 4820f5884d69..7aca252464da 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts @@ -1,14 +1,14 @@ 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 ) -> vecN 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 { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -32,7 +32,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') @@ -43,7 +43,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') @@ -54,7 +54,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') @@ -68,7 +68,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') @@ -82,7 +82,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') @@ -96,5 +96,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/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts index efa4041259eb..5ba69934279f 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts @@ -6,7 +6,7 @@ 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 { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -24,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/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts index 6479fe7ce070..1bcca2f73fe2 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts @@ -8,15 +8,7 @@ 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 { Case } from '../../case.js'; import { allInputSources, run } from '../../expression.js'; @@ -52,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/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts index 303c555c1daf..334d1064824c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts @@ -8,15 +8,7 @@ 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 { Case } from '../../case.js'; import { allInputSources, run } from '../../expression.js'; @@ -52,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/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts index b1c2607c940f..fbe362ea45e8 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts @@ -8,16 +8,7 @@ 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 { Case } from '../../case.js'; import { allInputSources, run } from '../../expression.js'; @@ -36,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(4) as [Scalar, Scalar, Scalar, Scalar]; + const vals_f32 = new Array(4) as [ + ScalarValue, + ScalarValue, + ScalarValue, + ScalarValue, + ]; for (const idx in vals) { vals[idx] = quantizeToF32(vals[idx]); vals_f32[idx] = f32(vals[idx]); @@ -57,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/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts index fa67e8f4ae3d..c7d62e722b24 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts @@ -8,16 +8,7 @@ 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 { Case } from '../../case.js'; import { allInputSources, run } from '../../expression.js'; @@ -36,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(4) as [Scalar, Scalar, Scalar, Scalar]; + const vals_f32 = new Array(4) as [ + ScalarValue, + ScalarValue, + ScalarValue, + ScalarValue, + ]; for (const idx in vals) { vals[idx] = quantizeToF32(vals[idx]); vals_f32[idx] = f32(vals[idx]); @@ -57,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/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts index 8caae09eba7c..94aa67f0ae85 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts @@ -8,7 +8,7 @@ Component e[i] of the input is mapped to bits (8 * i) through (8 * (i + 7)) of t import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeI32, TypeU32, TypeVec, u32, toVector, i32 } from '../../../../../util/conversion.js'; +import { u32, toVector, i32, Type } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -65,5 +65,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('pack4xI8'), [TypeVec(4, TypeI32)], TypeU32, cfg, cases); + await run(t, builtin('pack4xI8'), [Type.vec4i], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts index 0ab63d89beef..4968ed1e04de 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts @@ -9,7 +9,7 @@ result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeI32, TypeU32, TypeVec, u32, toVector, i32 } from '../../../../../util/conversion.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'; @@ -69,5 +69,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('pack4xI8Clamp'), [TypeVec(4, TypeI32)], TypeU32, cfg, cases); + await run(t, builtin('pack4xI8Clamp'), [Type.vec4i], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts index 0d4d2223f05d..9d08e88d4432 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts @@ -8,7 +8,7 @@ Component e[i] of the input is mapped to bits (8 * i) through (8 * (i + 7)) of t import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, TypeVec, u32, toVector } from '../../../../../util/conversion.js'; +import { u32, toVector, Type } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -50,5 +50,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('pack4xU8'), [TypeVec(4, TypeU32)], TypeU32, cfg, cases); + await run(t, builtin('pack4xU8'), [Type.vec4u], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts index 0f70cb6f8c87..ffecf9b8779c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts @@ -9,7 +9,7 @@ result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, TypeVec, u32, toVector } from '../../../../../util/conversion.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'; @@ -53,5 +53,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('pack4xU8Clamp'), [TypeVec(4, TypeU32)], TypeU32, cfg, cases); + await run(t, builtin('pack4xU8Clamp'), [Type.vec4u], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts index deea077e7a31..476c591a358e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts +++ b/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 @const fn pow(e1: T ,e2: T ) -> T Returns e1 raised to the power e2. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts index ee34ddd17479..0aa9669e93b3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.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 { TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -26,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/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts index afc7da6d8cdb..a405807ec044 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts +++ b/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 @const fn radians(e1: T ) -> T Converts degrees to radians, approximating e1 * π / 180. @@ -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 { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -31,8 +31,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('radians'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -46,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') @@ -60,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/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts index 0908bf04be5c..9c1a25f22a60 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts +++ b/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, vecN, or vecN +T is vecN, vecN, or vecN @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,7 +9,7 @@ direction e1-2*dot(e2,e1)*e2. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -31,14 +31,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') @@ -49,14 +42,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') @@ -67,14 +53,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') @@ -88,14 +67,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') @@ -109,14 +81,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') @@ -130,12 +95,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/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts index 67eb8ac94658..466c9be7d922 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts +++ b/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 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)). @@ -12,7 +12,7 @@ vector e3*e1- (e3* dot(e2,e1) + sqrt(k)) *e2. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -37,8 +37,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 ); @@ -55,8 +55,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 ); @@ -73,8 +73,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 ); @@ -94,8 +94,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 ); @@ -115,8 +115,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 ); @@ -136,8 +136,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/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts index 6acb35982216..e235e62a5284 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts +++ b/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/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts index 8d215b952d59..eeaf41b38185 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts +++ b/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 @const fn round(e: T) -> T Result is the integer k nearest to e, as a floating point value. @@ -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 { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -33,8 +33,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('round'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -48,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') @@ -62,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/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts index 216ffb734578..79c61e4eec6c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts +++ b/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 @const fn saturate(e: T) -> T Returns clamp(e, 0.0, 1.0). Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -30,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('saturate'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -44,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') @@ -58,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/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts index aa5ee45e84e2..63accbc2d466 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts +++ b/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,10 +25,9 @@ import { vec3, vec4, abstractFloat, - TypeAbstractFloat, - TypeAbstractInt, abstractInt, - Scalar, + ScalarValue, + Type, } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { run, allInputSources } from '../../expression.js'; @@ -51,41 +44,41 @@ type scalarKind = 'b' | 'af' | 'f' | 'h' | 'ai' | 'i' | 'u'; const dataType = { b: { - type: TypeBool, + type: Type.bool, scalar_builder: makeBool, shader_builder: builtin('select'), }, af: { - type: TypeAbstractFloat, + type: Type.abstractFloat, scalar_builder: abstractFloat, shader_builder: abstractFloatBuiltin('select'), }, f: { - type: TypeF32, + type: Type.f32, scalar_builder: f32, shader_builder: builtin('select'), }, h: { - type: TypeF16, + type: Type.f16, scalar_builder: f16, shader_builder: builtin('select'), }, ai: { - type: TypeAbstractInt, + 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): Scalar => { + scalar_builder: (v: number): ScalarValue => { return abstractInt(BigInt(v)); }, shader_builder: abstractIntBuiltin('select'), }, i: { - type: TypeI32, + type: Type.i32, scalar_builder: i32, shader_builder: builtin('select'), }, u: { - type: TypeU32, + type: Type.u32, scalar_builder: u32, shader_builder: builtin('select'), }, @@ -136,21 +129,21 @@ g.test('scalar') ], }, 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 }, @@ -162,7 +155,7 @@ g.test('scalar') await run( t, dataType[t.params.component as scalarKind].shader_builder, - [overload.type, overload.type, TypeBool], + [overload.type, overload.type, Type.bool], overload.type, t.params, overload.cases @@ -205,8 +198,8 @@ g.test('vector') 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) }, @@ -220,8 +213,8 @@ g.test('vector') 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) }, @@ -239,8 +232,8 @@ g.test('vector') 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) }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts index 7872377dc42c..f7d2e3ccfd5a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts +++ b/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 @const fn sign(e: T ) -> T Returns the sign of e. Component-wise when T is a vector. @@ -9,13 +9,7 @@ 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 { - TypeAbstractFloat, - TypeAbstractInt, - TypeF16, - TypeF32, - TypeI32, -} from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js'; @@ -36,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('sign'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -53,7 +47,7 @@ g.test('abstract_int') ) .fn(async t => { const cases = await d.get('abstract_int'); - await run(t, abstractIntBuiltin('sign'), [TypeAbstractInt], TypeAbstractInt, t.params, cases); + await run(t, abstractIntBuiltin('sign'), [Type.abstractInt], Type.abstractInt, t.params, cases); }); g.test('i32') @@ -64,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') @@ -75,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') @@ -89,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/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts index e9420fc0880d..4d649b4db5eb 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts +++ b/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 @const fn sin(e: T ) -> T Returns the sine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -39,7 +39,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') @@ -53,5 +53,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/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts index 0070befa47fc..f59616d9af12 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts +++ b/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 @const fn sinh(e: T ) -> T Returns the hyperbolic sine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts index a32c112b8a94..1404061d02f7 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts +++ b/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 @const fn smoothstep(low: T , high: T , x: T ) -> T Returns the smooth Hermite interpolation between 0 and 1. @@ -11,7 +11,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -35,7 +35,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') @@ -49,5 +49,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/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts index cfd379fea660..ee4197d70f7d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts +++ b/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 @const fn sqrt(e: T ) -> T Returns the square root of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts index c55a48812e9c..caf75e6f623a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts +++ b/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 @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,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts index f86b3c959a16..4ddef19fe168 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts +++ b/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 @const fn tan(e: T ) -> T Returns the tangent of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts index 496c7bcf1b11..19f4ff55318b 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts +++ b/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 @const fn tanh(e: T ) -> T Returns the hyperbolic tangent of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ 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 { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,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') @@ -47,5 +47,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/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts index 90fa226a01e3..7a96906cfa71 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts @@ -1,14 +1,14 @@ 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 ) -> matCxR 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -32,8 +32,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('transpose'), - [TypeMat(cols, rows, TypeAbstractFloat)], - TypeMat(rows, cols, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat)], + Type.mat(rows, cols, Type.abstractFloat), t.params, cases ); @@ -59,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 ); @@ -89,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/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts index 022248ae5b97..9d05709fc686 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts +++ b/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 @const fn trunc(e: T ) -> T Returns the nearest whole number whose absolute value is less than or equal to e. @@ -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 { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -31,8 +31,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('trunc'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -46,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') @@ -60,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/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts index da1819f8ef6e..e145afca50be 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts @@ -7,7 +7,7 @@ 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,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/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts index a88702bcce70..059a5664f9e4 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts @@ -7,7 +7,7 @@ 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,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/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts index 325e0a9735b5..971f384023ef 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts @@ -7,7 +7,7 @@ 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,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/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts index 3b48cca4539f..d6e533621b14 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts @@ -7,7 +7,7 @@ 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,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/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts index 3e57f2592ee5..026043da1a97 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts @@ -7,7 +7,7 @@ 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 { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,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/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts index 6253ad7bebca..4f7e4ed3a70d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts @@ -8,7 +8,7 @@ with sign extension. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeI32, TypeU32, TypeVec, u32, toVector, i32 } from '../../../../../util/conversion.js'; +import { u32, toVector, i32, Type } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -52,5 +52,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('unpack4xI8'), [TypeU32], TypeVec(4, TypeI32), cfg, cases); + await run(t, builtin('unpack4xI8'), [Type.u32], Type.vec4i, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts index 3c13f32fecc6..09849030eb10 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts @@ -8,7 +8,7 @@ with zero extension. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, TypeVec, u32, toVector } from '../../../../../util/conversion.js'; +import { u32, toVector, Type } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -44,5 +44,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('unpack4xU8'), [TypeU32], TypeVec(4, TypeU32), cfg, cases); + await run(t, builtin('unpack4xU8'), [Type.u32], Type.vec4u, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/case.ts b/src/webgpu/shader/execution/expression/case.ts index 436f0a6964ad..063b58da4267 100644 --- a/src/webgpu/shader/execution/expression/case.ts +++ b/src/webgpu/shader/execution/expression/case.ts @@ -1,7 +1,14 @@ import { crc32 } from '../../../../common/util/crc32.js'; import { ROArrayArray } from '../../../../common/util/types.js'; import { assert } from '../../../../common/util/util.js'; -import { abstractInt, i32, ScalarBuilder, u32, Value, Vector } from '../../../util/conversion.js'; +import { + abstractInt, + i32, + ScalarBuilder, + u32, + Value, + VectorValue, +} from '../../../util/conversion.js'; import { cartesianProduct, QuantizeFunc, @@ -103,8 +110,8 @@ function makeScalarVectorBinaryToVectorCase( return undefined; } return { - input: [scalarize(scalar), new Vector(vector.map(scalarize))], - expected: new Vector(result.filter(notUndefined).map(scalarize)), + input: [scalarize(scalar), new VectorValue(vector.map(scalarize))], + expected: new VectorValue(result.filter(notUndefined).map(scalarize)), }; } @@ -154,8 +161,8 @@ function makeVectorScalarBinaryToVectorCase( return undefined; } return { - input: [new Vector(vector.map(scalarize)), scalarize(scalar)], - expected: new Vector(result.filter(notUndefined).map(scalarize)), + input: [new VectorValue(vector.map(scalarize)), scalarize(scalar)], + expected: new VectorValue(result.filter(notUndefined).map(scalarize)), }; } @@ -357,8 +364,8 @@ function makeVectorVectorToScalarCase( return { input: [ - new Vector(param0_quantized.map(scalarize)), - new Vector(param1_quantized.map(scalarize)), + new VectorValue(param0_quantized.map(scalarize)), + new VectorValue(param1_quantized.map(scalarize)), ], expected: scalarize(result), }; diff --git a/src/webgpu/shader/execution/expression/case_cache.ts b/src/webgpu/shader/execution/expression/case_cache.ts index 4321475bf83a..345183c5d506 100644 --- a/src/webgpu/shader/execution/expression/case_cache.ts +++ b/src/webgpu/shader/execution/expression/case_cache.ts @@ -3,11 +3,11 @@ import { unreachable } from '../../../../common/util/util.js'; import BinaryStream from '../../../util/binary_stream.js'; import { deserializeComparator, serializeComparator } from '../../../util/compare.js'; import { - Matrix, + MatrixValue, Value, - Vector, + VectorValue, deserializeValue, - isScalar, + isScalarValue, serializeValue, } from '../../../util/conversion.js'; import { @@ -31,7 +31,7 @@ enum SerializedExpectationKind { /** serializeExpectation() serializes an Expectation to a BinaryStream */ export function serializeExpectation(s: BinaryStream, e: Expectation) { - if (isScalar(e) || e instanceof Vector || e instanceof Matrix) { + if (isScalarValue(e) || e instanceof VectorValue || e instanceof MatrixValue) { s.writeU8(SerializedExpectationKind.Value); serializeValue(s, e); return; diff --git a/src/webgpu/shader/execution/expression/expectation.ts b/src/webgpu/shader/execution/expression/expectation.ts index e56577580c52..d43ab5279116 100644 --- a/src/webgpu/shader/execution/expression/expectation.ts +++ b/src/webgpu/shader/execution/expression/expectation.ts @@ -1,6 +1,6 @@ import { ROArrayArray } from '../../../../common/util/types.js'; import { Comparator, compare } from '../../../util/compare.js'; -import { Matrix, Value, Vector, isScalar } from '../../../util/conversion.js'; +import { MatrixValue, Value, VectorValue, isScalarValue } from '../../../util/conversion.js'; import { FPInterval } from '../../../util/floating_point.js'; export type Expectation = @@ -14,9 +14,9 @@ export type Expectation = export function isComparator(e: Expectation): e is Comparator { return !( e instanceof FPInterval || - isScalar(e) || - e instanceof Vector || - e instanceof Matrix || + isScalarValue(e) || + e instanceof VectorValue || + e instanceof MatrixValue || e instanceof Array ); } diff --git a/src/webgpu/shader/execution/expression/expression.ts b/src/webgpu/shader/execution/expression/expression.ts index c622e6c5fdde..21e1d40eab90 100644 --- a/src/webgpu/shader/execution/expression/expression.ts +++ b/src/webgpu/shader/execution/expression/expression.ts @@ -5,14 +5,12 @@ import { Comparator, ComparatorImpl } from '../../../util/compare.js'; import { kValue } from '../../../util/constants.js'; import { MatrixType, - Scalar, + ScalarValue, ScalarType, Type, - TypeU32, - TypeVec, - Value, - Vector, VectorType, + Value, + VectorValue, isAbstractType, scalarTypeOf, } from '../../../util/conversion.js'; @@ -158,11 +156,11 @@ function storageType(ty: Type): Type { `Custom handling is implemented for 'abstract-float' values` ); if (ty.kind === 'bool') { - return TypeU32; + return Type.u32; } } if (ty instanceof VectorType) { - return TypeVec(ty.width, storageType(ty.elementType) as ScalarType); + return Type.vec(ty.width, storageType(ty.elementType) as ScalarType); } return ty; } @@ -1213,22 +1211,22 @@ function packScalarsToVector( } const packedCases: Array = []; - const packedParameterTypes = parameterTypes.map(p => TypeVec(vectorWidth, p as ScalarType)); - const packedResultType = new VectorType(vectorWidth, resultType); + const packedParameterTypes = parameterTypes.map(p => Type.vec(vectorWidth, p as ScalarType)); + const packedResultType = Type.vec(vectorWidth, resultType); const clampCaseIdx = (idx: number) => Math.min(idx, cases.length - 1); let caseIdx = 0; while (caseIdx < cases.length) { // Construct the vectorized inputs from the scalar cases - const packedInputs = new Array(parameterTypes.length); + const packedInputs = new Array(parameterTypes.length); for (let paramIdx = 0; paramIdx < parameterTypes.length; paramIdx++) { - const inputElements = new Array(vectorWidth); + const inputElements = new Array(vectorWidth); for (let i = 0; i < vectorWidth; i++) { const input = cases[clampCaseIdx(caseIdx + i)].input; - inputElements[i] = (input instanceof Array ? input[paramIdx] : input) as Scalar; + inputElements[i] = (input instanceof Array ? input[paramIdx] : input) as ScalarValue; } - packedInputs[paramIdx] = new Vector(inputElements); + packedInputs[paramIdx] = new VectorValue(inputElements); } // Gather the comparators for the packed cases @@ -1242,7 +1240,7 @@ function packScalarsToVector( const gElements = new Array(vectorWidth); const eElements = new Array(vectorWidth); for (let i = 0; i < vectorWidth; i++) { - const d = cmp_impls[i]((got as Vector).elements[i]); + const d = cmp_impls[i]((got as VectorValue).elements[i]); matched = matched && d.matched; gElements[i] = d.got; eElements[i] = d.expected; diff --git a/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts index 005ddc6448ea..686d4b7c4ac2 100644 --- a/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for AbstractFloat arithmetic unary expression operations +Execution Tests for Type.abstractFloat arithmetic unary expression operations `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_arithmetic.cache.js'; @@ -30,8 +30,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatUnary('-'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases, 1 diff --git a/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts b/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts index 76e96fe80354..001c47e117bd 100644 --- a/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts @@ -4,7 +4,7 @@ Execution Tests for assignment of AbstractFloats import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, abstractFloatShaderBuilder, @@ -35,7 +35,15 @@ testing that extracting abstract floats works .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('abstract'); - await run(t, abstract_assignment(), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases, 1); + await run( + t, + abstract_assignment(), + [Type.abstractFloat], + Type.abstractFloat, + t.params, + cases, + 1 + ); }); g.test('f32') @@ -48,7 +56,7 @@ concretizing to f32 .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('f32'); - await run(t, concrete_assignment(), [TypeAbstractFloat], TypeF32, t.params, cases); + await run(t, concrete_assignment(), [Type.abstractFloat], Type.f32, t.params, cases); }); g.test('f16') @@ -64,5 +72,5 @@ concretizing to f16 .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('f16'); - await run(t, concrete_assignment(), [TypeAbstractFloat], TypeF16, t.params, cases); + await run(t, concrete_assignment(), [Type.abstractFloat], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/ai_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/ai_arithmetic.spec.ts index bc50be4a7b62..625a38b2d5af 100644 --- a/src/webgpu/shader/execution/expression/unary/ai_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/ai_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the abstract integer arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractInt } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './ai_arithmetic.cache.js'; @@ -26,5 +26,5 @@ Expression: -x ) .fn(async t => { const cases = await d.get('negation'); - await run(t, abstractIntUnary('-'), [TypeAbstractInt], TypeAbstractInt, t.params, cases); + await run(t, abstractIntUnary('-'), [Type.abstractInt], Type.abstractInt, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts b/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts index f7915df97adc..fe1ba2d9fc12 100644 --- a/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts @@ -4,7 +4,7 @@ Execution Tests for assignment of AbstractInts import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractInt, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, abstractIntShaderBuilder, @@ -35,7 +35,7 @@ testing that extracting abstract ints works .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('abstract'); - await run(t, abstract_assignment(), [TypeAbstractInt], TypeAbstractInt, t.params, cases, 1); + await run(t, abstract_assignment(), [Type.abstractInt], Type.abstractInt, t.params, cases, 1); }); g.test('i32') @@ -48,7 +48,7 @@ concretizing to i32 .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('i32'); - await run(t, concrete_assignment(), [TypeAbstractInt], TypeI32, t.params, cases); + await run(t, concrete_assignment(), [Type.abstractInt], Type.i32, t.params, cases); }); g.test('u32') @@ -61,5 +61,5 @@ concretizing to u32 .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('u32'); - await run(t, concrete_assignment(), [TypeAbstractInt], TypeU32, t.params, cases); + await run(t, concrete_assignment(), [Type.abstractInt], Type.u32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/ai_complement.spec.ts b/src/webgpu/shader/execution/expression/unary/ai_complement.spec.ts index 919321e207ce..507ae219cb29 100644 --- a/src/webgpu/shader/execution/expression/unary/ai_complement.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/ai_complement.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for the AbstractInt bitwise complement operation +Execution Tests for the Type.abstractInt bitwise complement operation `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { abstractInt, TypeAbstractInt } from '../../../../util/conversion.js'; +import { abstractInt, Type } from '../../../../util/conversion.js'; import { fullI64Range } from '../../../../util/math.js'; import { onlyConstInputSource, run } from '../expression.js'; @@ -28,5 +28,5 @@ Expression: ~x const cases = fullI64Range().map(e => { return { input: abstractInt(e), expected: abstractInt(~e) }; }); - await run(t, abstractIntUnary('~'), [TypeAbstractInt], TypeAbstractInt, t.params, cases); + await run(t, abstractIntUnary('~'), [Type.abstractInt], Type.abstractInt, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts b/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts index f64ec1cd4180..5b16c49c421b 100644 --- a/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts +++ b/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts @@ -1,5 +1,5 @@ import { anyOf } from '../../../../util/compare.js'; -import { Scalar, bool, f16, f32, i32, u32 } from '../../../../util/conversion.js'; +import { ScalarValue, bool, f16, f32, i32, u32 } from '../../../../util/conversion.js'; import { fullI32Range, fullU32Range, @@ -29,7 +29,7 @@ export const d = makeCaseCache('unary/bool_conversion', { }, f32: () => { return scalarF32Range().map(f => { - const expected: Scalar[] = []; + const expected: ScalarValue[] = []; if (f !== 0) { expected.push(bool(true)); } @@ -41,7 +41,7 @@ export const d = makeCaseCache('unary/bool_conversion', { }, f16: () => { return scalarF16Range().map(f => { - const expected: Scalar[] = []; + const expected: ScalarValue[] = []; if (f !== 0) { expected.push(bool(true)); } diff --git a/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts index 5f5bb31397a3..55d01d3eda1f 100644 --- a/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the boolean conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF16, TypeF32, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './bool_conversion.cache.js'; @@ -31,7 +31,14 @@ Identity operation ) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeBool, t.params, cases); + await run( + t, + vectorizeToExpression(t.params.vectorize), + [Type.bool], + Type.bool, + t.params, + cases + ); }); g.test('u32') @@ -49,7 +56,7 @@ The result is false if e is 0, and true otherwise. ) .fn(async t => { const cases = await d.get('u32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeBool, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.bool, t.params, cases); }); g.test('i32') @@ -67,7 +74,7 @@ The result is false if e is 0, and true otherwise. ) .fn(async t => { const cases = await d.get('i32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeBool, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.bool, t.params, cases); }); g.test('f32') @@ -85,7 +92,7 @@ The result is false if e is 0.0 or -0.0, and true otherwise. ) .fn(async t => { const cases = await d.get('f32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeBool, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.bool, t.params, cases); }); g.test('f16') @@ -106,5 +113,5 @@ The result is false if e is 0.0 or -0.0, and true otherwise. }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeBool, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/bool_logical.spec.ts b/src/webgpu/shader/execution/expression/unary/bool_logical.spec.ts index 01eaaab43a51..58d4b9fc0fc3 100644 --- a/src/webgpu/shader/execution/expression/unary/bool_logical.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/bool_logical.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the boolean unary logical expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { bool, TypeBool } from '../../../../util/conversion.js'; +import { bool, Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { unary } from './unary.js'; @@ -29,5 +29,5 @@ Logical negation. The result is true when e is false and false when e is true. C { input: bool(false), expected: bool(true) }, ]; - await run(t, unary('!'), [TypeBool], TypeBool, t.params, cases); + await run(t, unary('!'), [Type.bool], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts index 60e16e645221..813bbf7a64e6 100644 --- a/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the f16 arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { d } from './f16_arithmetic.cache.js'; @@ -28,5 +28,5 @@ Accuracy: Correctly rounded }) .fn(async t => { const cases = await d.get('negation'); - await run(t, unary('-'), [TypeF16], TypeF16, t.params, cases); + await run(t, unary('-'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts index a8da8ff3bd7f..765b36d54cc6 100644 --- a/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts @@ -4,14 +4,7 @@ Execution Tests for the f32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { - TypeBool, - TypeF16, - TypeF32, - TypeI32, - TypeMat, - TypeU32, -} from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './f16_conversion.cache.js'; @@ -46,7 +39,7 @@ The result is 1.0 if e is true and 0.0 otherwise }) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.bool], Type.f16, t.params, cases); }); g.test('u32') @@ -66,7 +59,7 @@ Converted to f16, +/-Inf if out of range }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.f16, t.params, cases); }); g.test('i32') @@ -86,7 +79,7 @@ Converted to f16, +/-Inf if out of range }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'i32_const' : 'i32_non_const'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.f16, t.params, cases); }); g.test('f32') @@ -106,7 +99,7 @@ Correctly rounded to f16 }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.f16, t.params, cases); }); g.test('f32_mat') @@ -132,8 +125,8 @@ g.test('f32_mat') await run( t, matrixExperession(cols, rows), - [TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -156,7 +149,7 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.f16, t.params, cases); }); g.test('f16_mat') @@ -182,8 +175,8 @@ g.test('f16_mat') await run( t, matrixExperession(cols, rows), - [TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts index c60e2c3d51b0..3bb48705e854 100644 --- a/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the f32 arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { d } from './f32_arithmetic.cache.js'; @@ -25,5 +25,5 @@ Accuracy: Correctly rounded ) .fn(async t => { const cases = await d.get('negation'); - await run(t, unary('-'), [TypeF32], TypeF32, t.params, cases); + await run(t, unary('-'), [Type.f32], Type.f32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts index 3e2fbd540ace..464fdee44e79 100644 --- a/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts @@ -4,14 +4,7 @@ Execution Tests for the f32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { - TypeBool, - TypeF16, - TypeF32, - TypeI32, - TypeMat, - TypeU32, -} from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './f32_conversion.cache.js'; @@ -43,7 +36,7 @@ The result is 1.0 if e is true and 0.0 otherwise ) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.bool], Type.f32, t.params, cases); }); g.test('u32') @@ -60,7 +53,7 @@ Converted to f32 ) .fn(async t => { const cases = await d.get('u32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.f32, t.params, cases); }); g.test('i32') @@ -77,7 +70,7 @@ Converted to f32 ) .fn(async t => { const cases = await d.get('i32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.f32, t.params, cases); }); g.test('f32') @@ -94,7 +87,7 @@ Identity operation ) .fn(async t => { const cases = await d.get('f32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.f32, t.params, cases); }); g.test('f32_mat') @@ -117,8 +110,8 @@ g.test('f32_mat') await run( t, matrixExperession(cols, rows), - [TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -141,7 +134,7 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.f32, t.params, cases); }); g.test('f16_mat') @@ -167,8 +160,8 @@ g.test('f16_mat') await run( t, matrixExperession(cols, rows), - [TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts index a6fe77ada165..a7d16a96cd88 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeI32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { d } from './i32_arithmetic.cache.js'; @@ -24,5 +24,5 @@ Expression: -x ) .fn(async t => { const cases = await d.get('negation'); - await run(t, unary('-'), [TypeI32], TypeI32, t.params, cases); + await run(t, unary('-'), [Type.i32], Type.i32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts index dc0ca8b8d066..01e5728d70d1 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 bitwise complement operation import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { i32, TypeI32 } from '../../../../util/conversion.js'; +import { i32, Type } from '../../../../util/conversion.js'; import { fullI32Range } from '../../../../util/math.js'; import { allInputSources, run } from '../expression.js'; @@ -26,5 +26,5 @@ Expression: ~x const cases = fullI32Range().map(e => { return { input: i32(e), expected: i32(~e) }; }); - await run(t, unary('~'), [TypeI32], TypeI32, t.params, cases); + await run(t, unary('~'), [Type.i32], Type.i32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts index 4ad3c4e7424f..606253b59e5c 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF16, TypeF32, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './i32_conversion.cache.js'; @@ -31,7 +31,7 @@ The result is 1u if e is true and 0u otherwise ) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.bool], Type.i32, t.params, cases); }); g.test('u32') @@ -48,7 +48,7 @@ Reinterpretation of bits ) .fn(async t => { const cases = await d.get('u32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.i32, t.params, cases); }); g.test('i32') @@ -65,7 +65,7 @@ Identity operation ) .fn(async t => { const cases = await d.get('i32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.i32, t.params, cases); }); g.test('f32') @@ -82,7 +82,7 @@ e is converted to i32, rounding towards zero ) .fn(async t => { const cases = await d.get('f32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.i32, t.params, cases); }); g.test('f16') @@ -102,5 +102,5 @@ e is converted to u32, rounding towards zero }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.i32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts b/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts index 6052ac008f0e..74251a32c62b 100644 --- a/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the u32 bitwise complement operation import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeU32, u32 } from '../../../../util/conversion.js'; +import { Type, u32 } from '../../../../util/conversion.js'; import { fullU32Range } from '../../../../util/math.js'; import { allInputSources, run } from '../expression.js'; @@ -26,5 +26,5 @@ Expression: ~x const cases = fullU32Range().map(e => { return { input: u32(e), expected: u32(~e) }; }); - await run(t, unary('~'), [TypeU32], TypeU32, t.params, cases); + await run(t, unary('~'), [Type.u32], Type.u32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts index d684824362cf..330f713dca5e 100644 --- a/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the u32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF16, TypeF32, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './u32_conversion.cache.js'; @@ -31,7 +31,7 @@ The result is 1u if e is true and 0u otherwise ) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.bool], Type.u32, t.params, cases); }); g.test('u32') @@ -48,7 +48,7 @@ Identity operation ) .fn(async t => { const cases = await d.get('u32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.u32, t.params, cases); }); g.test('i32') @@ -65,7 +65,7 @@ Reinterpretation of bits ) .fn(async t => { const cases = await d.get('i32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.u32, t.params, cases); }); g.test('f32') @@ -82,7 +82,7 @@ e is converted to u32, rounding towards zero ) .fn(async t => { const cases = await d.get('f32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.u32, t.params, cases); }); g.test('f16') @@ -102,14 +102,14 @@ e is converted to u32, rounding towards zero }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.u32, t.params, cases); }); g.test('abstract_int') .specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function') .desc( ` -u32(e), where e is an AbstractInt +u32(e), where e is an Type.abstractInt Identity operation if the e can be represented in u32, otherwise it produces a shader-creation error ` diff --git a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts index 183fa296248f..f7084b5d07d4 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts @@ -5,7 +5,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; -import { TypeF16, elementType, kAllScalarsAndVectors } from '../../../../../util/conversion.js'; +import { Type, elementType, kAllScalarsAndVectors } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; import { @@ -34,7 +34,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts index cabdf6b659d6..13e3492a5146 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -48,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -67,7 +66,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec }); // f32 is included here to confirm that validation is failing due to a type issue and not something else. -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -81,7 +80,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts index 3526b45f32ad..8e60459676f6 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts @@ -6,13 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, - TypeAbstractInt, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -50,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -59,7 +56,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.acosh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -84,8 +81,8 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, - [type.create(type === TypeAbstractInt ? 1n : 1)], + /* expectedResult */ type === Type.f32, + [type.create(type === Type.abstractInt ? 1n : 1)], 'constant' ); }); diff --git a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts index 3b7cdf8b8668..fef4540f4304 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -48,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -66,7 +65,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -80,7 +79,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts index c205ea2d6ecb..a029e38d02f3 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, + Type, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { linearRange, linearRangeBigInt } from '../../../../../util/math.js'; @@ -52,7 +50,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -61,7 +59,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.asinh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -72,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -86,7 +84,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts index 3b7dad4c4059..e8f325be5b94 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -47,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -63,7 +62,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -77,7 +76,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts index 4c61b331e2ad..712695ed0eb4 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts @@ -6,13 +6,11 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, - Vector, - VectorType, + VectorValue, elementType, kFloatScalarsAndVectors, kConcreteIntegerScalarsAndVectors, + Type, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -56,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -75,7 +73,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument_y') .desc( @@ -86,11 +84,11 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() .params(u => u.combine('type', keysOf(kIntegerArgumentTypes))) .fn(t => { const yTy = kIntegerArgumentTypes[t.params.type]; - const xTy = yTy instanceof Vector ? new VectorType(yTy.size, TypeF32) : TypeF32; + const xTy = yTy instanceof VectorValue ? Type.vec(yTy.size, Type.f32) : Type.f32; validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ yTy === TypeF32, + /* expectedResult */ yTy === Type.f32, [yTy.create(1), xTy.create(1)], 'constant' ); @@ -105,11 +103,11 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() .params(u => u.combine('type', keysOf(kIntegerArgumentTypes))) .fn(t => { const xTy = kIntegerArgumentTypes[t.params.type]; - const yTy = xTy instanceof Vector ? new VectorType(xTy.size, TypeF32) : TypeF32; + const yTy = xTy instanceof VectorValue ? Type.vec(xTy.size, Type.f32) : Type.f32; validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ xTy === TypeF32, + /* expectedResult */ xTy === Type.f32, [yTy.create(1), xTy.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts index 1d31ed88db28..460977c78878 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -48,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -66,7 +65,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -80,7 +79,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts index 09018d12a3a1..02fdbf70fc98 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts index 6342189ad643..5e8b3f3fe6ef 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts @@ -6,7 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, + Type, elementType, kFloatScalarsAndVectors, kConcreteIntegerScalarsAndVectors, @@ -44,7 +44,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('high', u => fullRangeForType(kValuesTypes[u.type], 4)) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts b/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts index 62924453f13a..440673c99265 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts @@ -2,7 +2,6 @@ import { assert, unreachable } from '../../../../../../common/util/util.js'; import { kValue } from '../../../../../util/constants.js'; import { Type, - TypeF16, Value, elementType, elementsOf, @@ -156,7 +155,7 @@ export function validateConstOrOverrideBuiltinEval( stage: ConstantOrOverrideStage ) { const elTys = args.map(arg => elementType(arg.type)!); - const enables = elTys.some(ty => ty === TypeF16) ? 'enable f16;' : ''; + const enables = elTys.some(ty => ty === Type.f16) ? 'enable f16;' : ''; switch (stage) { case 'constant': { diff --git a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts index d010cf499276..09b807c1039d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -47,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -61,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -75,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts index 6a7fcddba232..ffb01b707671 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -42,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -51,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.cosh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -62,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -76,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts index 2805b97b1597..35c7ff3c9062 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -42,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -51,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input const expectedResult = isRepresentable( (Number(t.params.value) * 180) / Math.PI, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -62,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -76,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts index cf5a707ba7c7..dd0a3fb595a8 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts @@ -5,9 +5,7 @@ Validation tests for derivative builtins. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, - TypeMat, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConcreteF16ScalarsAndVectors, @@ -98,10 +96,10 @@ fn foo() { // The list of invalid argument types to test, with an f32 control case. const kArgumentTypes = objectsToRecord([ - TypeF32, + Type.f32, ...kConcreteIntegerScalarsAndVectors, ...kConcreteF16ScalarsAndVectors, - TypeMat(2, 2, TypeF32), + Type.mat2x2f, ]); g.test('invalid_argument_types') @@ -115,17 +113,17 @@ Derivative builtins only accept f32 scalar and vector types. u.combine('type', keysOf(kArgumentTypes)).combine('call', ['', ...kDerivativeBuiltins]) ) .beforeAllSubcases(t => { - if (elementType(kArgumentTypes[t.params.type]) === TypeF16) { + if (elementType(kArgumentTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) .fn(t => { const type = kArgumentTypes[t.params.type]; const code = ` -${elementType(kArgumentTypes[t.params.type]) === TypeF16 ? 'enable f16;' : ''} +${elementType(kArgumentTypes[t.params.type]) === Type.f16 ? 'enable f16;' : ''} fn foo() { let x: ${type.toString()} = ${t.params.call}(${type.create(1).wgsl()}); }`; - t.expectCompileResult(kArgumentTypes[t.params.type] === TypeF32 || t.params.call === '', code); + t.expectCompileResult(kArgumentTypes[t.params.type] === Type.f32 || t.params.call === '', code); }); diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts index 6168fafb2336..edac1c0c529d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts @@ -7,9 +7,7 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { kValue } from '../../../../../util/constants.js'; import { - TypeF16, - TypeF32, - TypeAbstractFloat, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -72,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => valueForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -81,7 +79,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.exp(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -92,7 +90,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -106,7 +104,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts index 686cb3f69570..4f961e4259bc 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts @@ -7,9 +7,7 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { kValue } from '../../../../../util/constants.js'; import { - TypeF16, - TypeF32, - TypeAbstractFloat, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -72,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => valueForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -81,7 +79,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.pow(2, Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -92,7 +90,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -106,7 +104,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts index 25dd950dc602..f075b5e25c1b 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts index a192bc098cd9..4ab15948664b 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts @@ -6,11 +6,9 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, - TypeAbstractFloat, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -49,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -60,7 +58,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input isRepresentable( 1 / Math.sqrt(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -71,7 +69,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -85,7 +83,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts index 3fca4fec2cc8..620f09837329 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts @@ -7,15 +7,13 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { ScalarType, - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalar, kConvertableToFloatVec2, kConvertableToFloatVec3, kConvertableToFloatVec4, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -57,12 +55,12 @@ function calculate( isIntermediateRepresentable: isRepresentable( squareSum, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ), isResultRepresentable: isRepresentable( result, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ), result, }; @@ -86,7 +84,7 @@ the input scalar value always compiles without error .expand('value', u => fullRangeForType(kScalarTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kScalarTypes[t.params.type]) === TypeF16) { + if (elementType(kScalarTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -122,7 +120,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec2Types[t.params.type]) === TypeF16) { + if (elementType(kVec2Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -158,7 +156,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec3Types[t.params.type]) === TypeF16) { + if (elementType(kVec3Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -195,7 +193,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec4Types[t.params.type]) === TypeF16) { + if (elementType(kVec4Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -210,7 +208,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -224,7 +222,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts index b4cf923a50ce..18ea19c3e341 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts index 066fb652499b..b3a6c338c595 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts index 3c32e3e37495..3f0b4a6064ab 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts index 320bde062098..f7f60ec8f478 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts index 654729cadb4e..821fc0f3c382 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -52,7 +51,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input }) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -67,7 +66,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -81,7 +80,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts index 709a006ea8bd..354248e205ba 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts index 2a4560576ca5..238bb51d8bc7 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kFloatScalarsAndVectors, kConcreteSignedIntegerScalarsAndVectors, @@ -44,7 +43,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -60,7 +59,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input }); const kUnsignedIntegerArgumentTypes = objectsToRecord([ - TypeF32, + Type.f32, ...kConcreteUnsignedIntegerScalarsAndVectors, ]); @@ -76,7 +75,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts index 2cd4ee002a3f..a408323e3a82 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -47,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -61,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -75,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts index 45550d5c088f..8f5a4c0c60aa 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -42,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -51,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.sinh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -62,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -76,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts index 125a9d20de62..ff72a63379bc 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -49,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -60,7 +58,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input isRepresentable( Math.sqrt(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -71,7 +69,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -85,7 +83,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts index 38b0b204059c..ed5286781d56 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts @@ -6,11 +6,9 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, - TypeAbstractFloat, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { fpTraitsFor } from '../../../../../util/floating_point.js'; @@ -49,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -57,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const type = kValuesTypes[t.params.type]; const fp = fpTraitsFor( // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); const smallestPositive = fp.constants().positive.min; const v = fp.quantize(Number(t.params.value)); @@ -71,7 +69,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -85,7 +83,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/util/compare.ts b/src/webgpu/util/compare.ts index d9cc693184f1..7cb71ca8de5a 100644 --- a/src/webgpu/util/compare.ts +++ b/src/webgpu/util/compare.ts @@ -8,7 +8,14 @@ import { import { Expectation, toComparator } from '../shader/execution/expression/expectation.js'; import BinaryStream from './binary_stream.js'; -import { isFloatValue, isScalar, Matrix, Scalar, Value, Vector } from './conversion.js'; +import { + isFloatValue, + isScalarValue, + MatrixValue, + ScalarValue, + Value, + VectorValue, +} from './conversion.js'; import { FPInterval } from './floating_point.js'; /** Comparison describes the result of a Comparator function. */ @@ -98,9 +105,9 @@ function compareValue(got: Value, expected: Value): Comparison { } } - if (isScalar(got)) { + if (isScalarValue(got)) { const g = got; - const e = expected as Scalar; + const e = expected as ScalarValue; const isFloat = g.type.kind === 'f64' || g.type.kind === 'f32' || g.type.kind === 'f16'; const matched = (isFloat && (g.value as number) === (e.value as number)) || (!isFloat && g.value === e.value); @@ -111,8 +118,8 @@ function compareValue(got: Value, expected: Value): Comparison { }; } - if (got instanceof Vector) { - const e = expected as Vector; + if (got instanceof VectorValue) { + const e = expected as VectorValue; const gLen = got.elements.length; const eLen = e.elements.length; let matched = gLen === eLen; @@ -130,8 +137,8 @@ function compareValue(got: Value, expected: Value): Comparison { }; } - if (got instanceof Matrix) { - const e = expected as Matrix; + if (got instanceof MatrixValue) { + const e = expected as MatrixValue; const gCols = got.type.cols; const eCols = e.type.cols; const gRows = got.type.rows; @@ -175,7 +182,7 @@ function compareInterval(got: Value, expected: FPInterval): Comparison { } } - if (isScalar(got)) { + if (isScalarValue(got)) { const g = got.value as number; const matched = expected.contains(g); return { @@ -197,7 +204,7 @@ function compareInterval(got: Value, expected: FPInterval): Comparison { */ function compareVector(got: Value, expected: FPInterval[]): Comparison { // Check got type - if (!(got instanceof Vector)) { + if (!(got instanceof VectorValue)) { return { matched: false, got: `${Colors.red((typeof got).toString())}(${got})`, @@ -262,7 +269,7 @@ function convertArrayToString(m: T[]): string { */ function compareMatrix(got: Value, expected: FPInterval[][]): Comparison { // Check got type - if (!(got instanceof Matrix)) { + if (!(got instanceof MatrixValue)) { return { matched: false, got: `${Colors.red((typeof got).toString())}(${got})`, diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 31ef330d50a5..2b9e7989bcdb 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -596,9 +596,13 @@ export type ScalarKind = export class ScalarType { readonly kind: ScalarKind; // The named type readonly _size: number; // In bytes - readonly read: (buf: Uint8Array, offset: number) => Scalar; // reads a scalar from a buffer + readonly read: (buf: Uint8Array, offset: number) => ScalarValue; // reads a scalar from a buffer - constructor(kind: ScalarKind, size: number, read: (buf: Uint8Array, offset: number) => Scalar) { + constructor( + kind: ScalarKind, + size: number, + read: (buf: Uint8Array, offset: number) => ScalarValue + ) { this.kind = kind; this._size = size; this.read = read; @@ -612,8 +616,8 @@ export class ScalarType { return this._size; } - /** Constructs a Scalar of this type with `value` */ - public create(value: number | bigint): Scalar { + /** Constructs a ScalarValue of this type with `value` */ + public create(value: number | bigint): ScalarValue { switch (typeof value) { case 'number': switch (this.kind) { @@ -659,6 +663,17 @@ export class VectorType { readonly width: number; // Number of elements in the vector readonly elementType: ScalarType; // Element type + static create(width: number, elementType: ScalarType): VectorType { + const key = `${elementType.toString()} ${width}}`; + let ty = vectorTypes.get(key); + if (ty !== undefined) { + return ty; + } + ty = new VectorType(width, elementType); + vectorTypes.set(key, ty); + return ty; + } + constructor(width: number, elementType: ScalarType) { this.width = width; this.elementType = elementType; @@ -668,13 +683,13 @@ export class VectorType { * @returns a vector constructed from the values read from the buffer at the * given byte offset */ - public read(buf: Uint8Array, offset: number): Vector { - const elements: Array = []; + public read(buf: Uint8Array, offset: number): VectorValue { + const elements: Array = []; for (let i = 0; i < this.width; i++) { elements[i] = this.elementType.read(buf, offset); offset += this.elementType.size; } - return new Vector(elements); + return new VectorValue(elements); } public toString(): string { @@ -686,36 +701,36 @@ export class VectorType { } /** Constructs a Vector of this type with the given values */ - public create(value: (number | bigint) | readonly (number | bigint)[]): Vector { + public create(value: (number | bigint) | readonly (number | bigint)[]): VectorValue { if (value instanceof Array) { assert(value.length === this.width); } else { value = Array(this.width).fill(value); } - return new Vector(value.map(v => this.elementType.create(v))); + return new VectorValue(value.map(v => this.elementType.create(v))); } } // Maps a string representation of a vector type to vector type. const vectorTypes = new Map(); -export function TypeVec(width: number, elementType: ScalarType): VectorType { - const key = `${elementType.toString()} ${width}}`; - let ty = vectorTypes.get(key); - if (ty !== undefined) { - return ty; - } - ty = new VectorType(width, elementType); - vectorTypes.set(key, ty); - return ty; -} - /** MatrixType describes the type of WGSL Matrix. */ export class MatrixType { readonly cols: number; // Number of columns in the Matrix readonly rows: number; // Number of elements per column in the Matrix readonly elementType: ScalarType; // Element type + static create(cols: number, rows: number, elementType: ScalarType): MatrixType { + const key = `${elementType.toString()} ${cols} ${rows}`; + let ty = matrixTypes.get(key); + if (ty !== undefined) { + return ty; + } + ty = new MatrixType(cols, rows, elementType); + matrixTypes.set(key, ty); + return ty; + } + constructor(cols: number, rows: number, elementType: ScalarType) { this.cols = cols; this.rows = rows; @@ -732,8 +747,8 @@ export class MatrixType { * @returns a Matrix constructed from the values read from the buffer at the * given byte offset */ - public read(buf: Uint8Array, offset: number): Matrix { - const elements: Scalar[][] = [...Array(this.cols)].map(_ => [...Array(this.rows)]); + public read(buf: Uint8Array, offset: number): MatrixValue { + const elements: ScalarValue[][] = [...Array(this.cols)].map(_ => [...Array(this.rows)]); for (let c = 0; c < this.cols; c++) { for (let r = 0; r < this.rows; r++) { elements[c][r] = this.elementType.read(buf, offset); @@ -745,7 +760,7 @@ export class MatrixType { offset += this.elementType.size; } } - return new Matrix(elements); + return new MatrixValue(elements); } public toString(): string { @@ -753,7 +768,7 @@ export class MatrixType { } /** Constructs a Matrix of this type with the given values */ - public create(value: (number | bigint) | readonly (number | bigint)[]): Matrix { + public create(value: (number | bigint) | readonly (number | bigint)[]): MatrixValue { if (value instanceof Array) { assert(value.length === this.cols * this.rows); } else { @@ -764,27 +779,13 @@ export class MatrixType { const start = i * this.rows; columns.push(value.slice(start, start + this.rows)); } - return new Matrix(columns.map(c => c.map(v => this.elementType.create(v)))); + return new MatrixValue(columns.map(c => c.map(v => this.elementType.create(v)))); } } // Maps a string representation of a Matrix type to Matrix type. const matrixTypes = new Map(); -export function TypeMat(cols: number, rows: number, elementType: ScalarType): MatrixType { - const key = `${elementType.toString()} ${cols} ${rows}`; - let ty = matrixTypes.get(key); - if (ty !== undefined) { - return ty; - } - ty = new MatrixType(cols, rows, elementType); - matrixTypes.set(key, ty); - return ty; -} - -/** Type is a ScalarType, VectorType, or MatrixType. */ -export type Type = ScalarType | VectorType | MatrixType; - /** ArrayElementType infers the element type of the indexable type A */ type ArrayElementType = A extends { [index: number]: infer T } ? T : never; @@ -800,74 +801,128 @@ function valueFromBytes( return workingDataOut[0] as ArrayElementType; } -export const TypeAbstractInt: ScalarType = new ScalarType( - 'abstract-int', - 8, - (buf: Uint8Array, offset: number) => abstractInt(valueFromBytes(workingDataI64, buf, offset)) +const abstractIntType = new ScalarType('abstract-int', 8, (buf: Uint8Array, offset: number) => + abstractInt(valueFromBytes(workingDataI64, buf, offset)) ); -export const TypeI32: ScalarType = new ScalarType('i32', 4, (buf: Uint8Array, offset: number) => +const i32Type = new ScalarType('i32', 4, (buf: Uint8Array, offset: number) => i32(valueFromBytes(workingDataI32, buf, offset)) ); -export const TypeU32: ScalarType = new ScalarType('u32', 4, (buf: Uint8Array, offset: number) => +const u32Type = new ScalarType('u32', 4, (buf: Uint8Array, offset: number) => u32(valueFromBytes(workingDataU32, buf, offset)) ); -export const TypeI16: ScalarType = new ScalarType('i16', 2, (buf: Uint8Array, offset: number) => +const i16Type = new ScalarType('i16', 2, (buf: Uint8Array, offset: number) => i16(valueFromBytes(workingDataI16, buf, offset)) ); -export const TypeU16: ScalarType = new ScalarType('u16', 2, (buf: Uint8Array, offset: number) => +const u16Type = new ScalarType('u16', 2, (buf: Uint8Array, offset: number) => u16(valueFromBytes(workingDataU16, buf, offset)) ); -export const TypeI8: ScalarType = new ScalarType('i8', 1, (buf: Uint8Array, offset: number) => +const i8Type = new ScalarType('i8', 1, (buf: Uint8Array, offset: number) => i8(valueFromBytes(workingDataI8, buf, offset)) ); -export const TypeU8: ScalarType = new ScalarType('u8', 1, (buf: Uint8Array, offset: number) => +const u8Type = new ScalarType('u8', 1, (buf: Uint8Array, offset: number) => u8(valueFromBytes(workingDataU8, buf, offset)) ); -export const TypeAbstractFloat: ScalarType = new ScalarType( - 'abstract-float', - 8, - (buf: Uint8Array, offset: number) => abstractFloat(valueFromBytes(workingDataF64, buf, offset)) +const abstractFloatType = new ScalarType('abstract-float', 8, (buf: Uint8Array, offset: number) => + abstractFloat(valueFromBytes(workingDataF64, buf, offset)) ); -export const TypeF64: ScalarType = new ScalarType('f64', 8, (buf: Uint8Array, offset: number) => +const f64Type = new ScalarType('f64', 8, (buf: Uint8Array, offset: number) => f64(valueFromBytes(workingDataF64, buf, offset)) ); -export const TypeF32: ScalarType = new ScalarType('f32', 4, (buf: Uint8Array, offset: number) => +const f32Type = new ScalarType('f32', 4, (buf: Uint8Array, offset: number) => f32(valueFromBytes(workingDataF32, buf, offset)) ); -export const TypeF16: ScalarType = new ScalarType('f16', 2, (buf: Uint8Array, offset: number) => +const f16Type = new ScalarType('f16', 2, (buf: Uint8Array, offset: number) => f16Bits(valueFromBytes(workingDataU16, buf, offset)) ); -export const TypeBool: ScalarType = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => +const boolType = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => bool(valueFromBytes(workingDataU32, buf, offset) !== 0) ); +/** Type is a ScalarType, VectorType, or MatrixType. */ +export type Type = ScalarType | VectorType | MatrixType; + +/** Type holds pre-declared Types along with helper constructor functions. */ +export const Type = { + abstractInt: abstractIntType, + i32: i32Type, + u32: u32Type, + i16: i16Type, + u16: u16Type, + i8: i8Type, + u8: u8Type, + + abstractFloat: abstractFloatType, + f64: f64Type, + f32: f32Type, + f16: f16Type, + + bool: boolType, + + vec: (width: number, elementType: ScalarType) => VectorType.create(width, elementType), + + vec2i: VectorType.create(2, i32Type), + vec2u: VectorType.create(2, u32Type), + vec2f: VectorType.create(2, f32Type), + vec2h: VectorType.create(2, f16Type), + vec3i: VectorType.create(3, i32Type), + vec3u: VectorType.create(3, u32Type), + vec3f: VectorType.create(3, f32Type), + vec3h: VectorType.create(3, f16Type), + vec4i: VectorType.create(4, i32Type), + vec4u: VectorType.create(4, u32Type), + vec4f: VectorType.create(4, f32Type), + vec4h: VectorType.create(4, f16Type), + + mat: (cols: number, rows: number, elementType: ScalarType) => + MatrixType.create(cols, rows, elementType), + + mat2x2f: MatrixType.create(2, 2, f32Type), + mat2x2h: MatrixType.create(2, 2, f16Type), + mat3x2f: MatrixType.create(3, 2, f32Type), + mat3x2h: MatrixType.create(3, 2, f16Type), + mat4x2f: MatrixType.create(4, 2, f32Type), + mat4x2h: MatrixType.create(4, 2, f16Type), + mat2x3f: MatrixType.create(2, 3, f32Type), + mat2x3h: MatrixType.create(2, 3, f16Type), + mat3x3f: MatrixType.create(3, 3, f32Type), + mat3x3h: MatrixType.create(3, 3, f16Type), + mat4x3f: MatrixType.create(4, 3, f32Type), + mat4x3h: MatrixType.create(4, 3, f16Type), + mat2x4f: MatrixType.create(2, 4, f32Type), + mat2x4h: MatrixType.create(2, 4, f16Type), + mat3x4f: MatrixType.create(3, 4, f32Type), + mat3x4h: MatrixType.create(3, 4, f16Type), + mat4x4f: MatrixType.create(4, 4, f32Type), + mat4x4h: MatrixType.create(4, 4, f16Type), +}; + /** @returns the ScalarType from the ScalarKind */ export function scalarType(kind: ScalarKind): ScalarType { switch (kind) { case 'abstract-float': - return TypeAbstractFloat; + return Type.abstractFloat; case 'f64': - return TypeF64; + return Type.f64; case 'f32': - return TypeF32; + return Type.f32; case 'f16': - return TypeF16; + return Type.f16; case 'u32': - return TypeU32; + return Type.u32; case 'u16': - return TypeU16; + return Type.u16; case 'u8': - return TypeU8; + return Type.u8; case 'abstract-int': - return TypeAbstractInt; + return Type.abstractInt; case 'i32': - return TypeI32; + return Type.i32; case 'i16': - return TypeI16; + return Type.i16; case 'i8': - return TypeI8; + return Type.i8; case 'bool': - return TypeBool; + return Type.bool; } } @@ -886,14 +941,14 @@ export function numElementsOf(ty: Type): number { } /** @returns the scalar elements of the given Value */ -export function elementsOf(value: Value): Scalar[] { - if (isScalar(value)) { +export function elementsOf(value: Value): ScalarValue[] { + if (isScalarValue(value)) { return [value]; } - if (value instanceof Vector) { + if (value instanceof VectorValue) { return value.elements; } - if (value instanceof Matrix) { + if (value instanceof MatrixValue) { return value.elements.flat(); } throw new Error(`unhandled value ${value}`); @@ -935,7 +990,7 @@ export class AbstractIntValue { readonly value: bigint; // The abstract-integer value readonly bitsLow: number; // The low 32 bits of the abstract-integer value. readonly bitsHigh: number; // The high 32 bits of the abstract-integer value. - readonly type = TypeAbstractInt; // The type of the value. + readonly type = Type.abstractInt; // The type of the value. public constructor(value: bigint, bitsLow: number, bitsHigh: number) { this.value = value; @@ -977,7 +1032,7 @@ export class AbstractFloatValue { readonly value: number; // The f32 value readonly bitsLow: number; // The low 32 bits of the abstract-float value. readonly bitsHigh: number; // The high 32 bits of the abstract-float value. - readonly type = TypeAbstractFloat; // The type of the value. + readonly type = Type.abstractFloat; // The type of the value. public constructor(value: number, bitsLow: number, bitsHigh: number) { this.value = value; @@ -1023,7 +1078,7 @@ export class AbstractFloatValue { export class I32Value { readonly value: number; // The i32 value readonly bits: number; // The i32 value, bitcast to a 32-bit integer. - readonly type = TypeI32; // The type of the value. + readonly type = Type.i32; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1055,7 +1110,7 @@ export class I32Value { /** Class that encapsulates a single u32 value. */ export class U32Value { readonly value: number; // The u32 value - readonly type = TypeU32; // The type of the value. + readonly type = Type.u32; // The type of the value. public constructor(value: number) { this.value = value; @@ -1090,7 +1145,7 @@ export class U32Value { export class I16Value { readonly value: number; // The i16 value readonly bits: number; // The i16 value, bitcast to a 16-bit integer. - readonly type = TypeI16; // The type of the value. + readonly type = Type.i16; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1125,7 +1180,7 @@ export class I16Value { */ export class U16Value { readonly value: number; // The u16 value - readonly type = TypeU16; // The type of the value. + readonly type = Type.u16; // The type of the value. public constructor(value: number) { this.value = value; @@ -1161,7 +1216,7 @@ export class U16Value { export class I8Value { readonly value: number; // The i8 value readonly bits: number; // The i8 value, bitcast to a 8-bit integer. - readonly type = TypeI8; // The type of the value. + readonly type = Type.i8; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1196,7 +1251,7 @@ export class I8Value { */ export class U8Value { readonly value: number; // The u8 value - readonly type = TypeU8; // The type of the value. + readonly type = Type.u8; // The type of the value. public constructor(value: number) { this.value = value; @@ -1233,7 +1288,7 @@ export class F64Value { readonly value: number; // The f32 value readonly bitsLow: number; // The low 32 bits of the abstract-float value. readonly bitsHigh: number; // The high 32 bits of the abstract-float value. - readonly type = TypeF64; // The type of the value. + readonly type = Type.f64; // The type of the value. public constructor(value: number, bitsLow: number, bitsHigh: number) { this.value = value; @@ -1280,7 +1335,7 @@ export class F64Value { export class F32Value { readonly value: number; // The f32 value readonly bits: number; // The f32 value, bitcast to a 32-bit integer. - readonly type = TypeF32; // The type of the value. + readonly type = Type.f32; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1324,7 +1379,7 @@ export class F32Value { export class F16Value { readonly value: number; // The f16 value readonly bits: number; // The f16 value, bitcast to a 16-bit integer. - readonly type = TypeF16; // The type of the value. + readonly type = Type.f16; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1366,7 +1421,7 @@ export class F16Value { /** Class that encapsulates a single bool value. */ export class BoolValue { readonly value: boolean; // The bool value - readonly type = TypeBool; // The type of the value. + readonly type = Type.bool; // The type of the value. public constructor(value: boolean) { this.value = value; @@ -1392,7 +1447,7 @@ export class BoolValue { } /** Scalar represents all the scalar value types */ -export type Scalar = +export type ScalarValue = | AbstractIntValue | AbstractFloatValue | I32Value @@ -1407,10 +1462,10 @@ export type Scalar = | BoolValue; export interface ScalarBuilder { - (value: T): Scalar; + (value: T): ScalarValue; } -export function isScalar(value: object): value is Scalar { +export function isScalarValue(value: object): value is ScalarValue { return ( value instanceof AbstractIntValue || value instanceof AbstractFloatValue || @@ -1472,7 +1527,7 @@ export function u32Bits(bits: number) { /** Create an i16 from a numeric value, a JS `number`. */ export function i16(value: number) { workingDataI16[0] = value; - return new I16Value(value, workingDataU16[0]); + return new I16Value(workingDataI16[0], workingDataU16[0]); } /** Create a u16 from a numeric value, a JS `number`. */ @@ -1484,7 +1539,7 @@ export function u16(value: number) { /** Create an i8 from a numeric value, a JS `number`. */ export function i8(value: number) { workingDataI8[0] = value; - return new I8Value(value, workingDataU8[0]); + return new I8Value(workingDataI8[0], workingDataU8[0]); } /** Create a u8 from a numeric value, a JS `number`. */ @@ -1496,19 +1551,19 @@ export function u8(value: number) { /** Create an f64 from a numeric value, a JS `number`. */ export function f64(value: number) { workingDataF64[0] = value; - return new F64Value(value, workingDataU32[0], workingDataU32[1]); + return new F64Value(workingDataF64[0], workingDataU32[0], workingDataU32[1]); } /** Create an f32 from a numeric value, a JS `number`. */ export function f32(value: number) { workingDataF32[0] = value; - return new F32Value(value, workingDataU32[0]); + return new F32Value(workingDataF32[0], workingDataU32[0]); } /** Create an f32 from a bit representation, a uint32 represented as a JS `number`. */ export function f32Bits(bits: number) { workingDataU32[0] = bits; - return new F32Value(workingDataF32[0], bits); + return new F32Value(workingDataF32[0], workingDataU32[0]); } /** Create an f16 from a numeric value, a JS `number`. */ @@ -1520,11 +1575,11 @@ export function f16(value: number) { /** Create an f16 from a bit representation, a uint16 represented as a JS `number`. */ export function f16Bits(bits: number) { workingDataU16[0] = bits; - return new F16Value(workingDataF16[0], bits); + return new F16Value(workingDataF16[0], workingDataU16[0]); } /** Create a boolean value. */ -export function bool(value: boolean): Scalar { +export function bool(value: boolean): ScalarValue { return new BoolValue(value); } @@ -1537,11 +1592,11 @@ export const False = bool(false); /** * Class that encapsulates a vector value. */ -export class Vector { - readonly elements: Array; +export class VectorValue { + readonly elements: Array; readonly type: VectorType; - public constructor(elements: Array) { + public constructor(elements: Array) { if (elements.length < 2 || elements.length > 4) { throw new Error(`vector element count must be between 2 and 4, got ${elements.length}`); } @@ -1555,7 +1610,7 @@ export class Vector { } } this.elements = elements; - this.type = TypeVec(elements.length, elements[0].type); + this.type = VectorType.create(elements.length, elements[0].type); } /** @@ -1604,18 +1659,18 @@ export class Vector { } /** Helper for constructing a new two-element vector with the provided values */ -export function vec2(x: Scalar, y: Scalar) { - return new Vector([x, y]); +export function vec2(x: ScalarValue, y: ScalarValue) { + return new VectorValue([x, y]); } /** Helper for constructing a new three-element vector with the provided values */ -export function vec3(x: Scalar, y: Scalar, z: Scalar) { - return new Vector([x, y, z]); +export function vec3(x: ScalarValue, y: ScalarValue, z: ScalarValue) { + return new VectorValue([x, y, z]); } /** Helper for constructing a new four-element vector with the provided values */ -export function vec4(x: Scalar, y: Scalar, z: Scalar, w: Scalar) { - return new Vector([x, y, z, w]); +export function vec4(x: ScalarValue, y: ScalarValue, z: ScalarValue, w: ScalarValue) { + return new VectorValue([x, y, z, w]); } /** @@ -1624,7 +1679,7 @@ export function vec4(x: Scalar, y: Scalar, z: Scalar, w: Scalar) { * @param v array of numbers to be converted, must contain 2, 3 or 4 elements * @param op function to convert from number to Scalar, e.g. 'f32` */ -export function toVector(v: readonly number[], op: (n: number) => Scalar): Vector { +export function toVector(v: readonly number[], op: (n: number) => ScalarValue): VectorValue { switch (v.length) { case 2: return vec2(op(v[0]), op(v[1])); @@ -1639,11 +1694,11 @@ export function toVector(v: readonly number[], op: (n: number) => Scalar): Vecto /** * Class that encapsulates a Matrix value. */ -export class Matrix { - readonly elements: Scalar[][]; +export class MatrixValue { + readonly elements: ScalarValue[][]; readonly type: MatrixType; - public constructor(elements: Array>) { + public constructor(elements: Array>) { const num_cols = elements.length; if (num_cols < 2 || num_cols > 4) { throw new Error(`matrix cols count must be between 2 and 4, got ${num_cols}`); @@ -1664,7 +1719,7 @@ export class Matrix { } this.elements = elements; - this.type = TypeMat(num_cols, num_rows, elem_type); + this.type = MatrixType.create(num_cols, num_rows, elem_type); } /** @@ -1706,35 +1761,37 @@ export class Matrix { * be of the same length. All Arrays must have 2, 3, or 4 elements. * @param op function to convert from number to Scalar, e.g. 'f32` */ -export function toMatrix(m: ROArrayArray, op: (n: number) => Scalar): Matrix { +export function toMatrix(m: ROArrayArray, op: (n: number) => ScalarValue): MatrixValue { const cols = m.length; const rows = m[0].length; - const elements: Scalar[][] = [...Array(cols)].map(_ => [...Array(rows)]); + const elements: ScalarValue[][] = [...Array(cols)].map(_ => [ + ...Array(rows), + ]); for (let i = 0; i < cols; i++) { for (let j = 0; j < rows; j++) { elements[i][j] = op(m[i][j]); } } - return new Matrix(elements); + return new MatrixValue(elements); } -/** Value is a Scalar or Vector value. */ -export type Value = Scalar | Vector | Matrix; +/** Value is a Scalar, Vector or Matrix value. */ +export type Value = ScalarValue | VectorValue | MatrixValue; -export type SerializedValueScalar = { +export type SerializedScalarValue = { kind: 'scalar'; type: ScalarKind; value: boolean | number; }; -export type SerializedValueVector = { +export type SerializedVectorValue = { kind: 'vector'; type: ScalarKind; value: boolean[] | readonly number[]; }; -export type SerializedValueMatrix = { +export type SerializedMatrixValue = { kind: 'matrix'; type: ScalarKind; value: ROArrayArray; @@ -1839,7 +1896,7 @@ enum SerializedValueKind { /** serializeValue() serializes a Value to a BinaryStream */ export function serializeValue(s: BinaryStream, v: Value) { - const serializeScalar = (scalar: Scalar, kind: ScalarKind) => { + const serializeScalar = (scalar: ScalarValue, kind: ScalarKind) => { switch (typeof scalar.value) { case 'number': switch (kind) { @@ -1892,13 +1949,13 @@ export function serializeValue(s: BinaryStream, v: Value) { } }; - if (isScalar(v)) { + if (isScalarValue(v)) { s.writeU8(SerializedValueKind.Scalar); serializeScalarKind(s, v.type.kind); serializeScalar(v, v.type.kind); return; } - if (v instanceof Vector) { + if (v instanceof VectorValue) { s.writeU8(SerializedValueKind.Vector); serializeScalarKind(s, v.type.elementType.kind); s.writeU8(v.type.width); @@ -1907,7 +1964,7 @@ export function serializeValue(s: BinaryStream, v: Value) { } return; } - if (v instanceof Matrix) { + if (v instanceof MatrixValue) { s.writeU8(SerializedValueKind.Matrix); serializeScalarKind(s, v.type.elementType.kind); s.writeU8(v.type.cols); @@ -1960,23 +2017,23 @@ export function deserializeValue(s: BinaryStream): Value { return deserializeScalar(scalarKind); case SerializedValueKind.Vector: { const width = s.readU8(); - const scalars = new Array(width); + const scalars = new Array(width); for (let i = 0; i < width; i++) { scalars[i] = deserializeScalar(scalarKind); } - return new Vector(scalars); + return new VectorValue(scalars); } case SerializedValueKind.Matrix: { const numCols = s.readU8(); const numRows = s.readU8(); - const columns = new Array(numCols); + const columns = new Array(numCols); for (let c = 0; c < numCols; c++) { - columns[c] = new Array(numRows); + columns[c] = new Array(numRows); for (let i = 0; i < numRows; i++) { columns[c][i] = deserializeScalar(scalarKind); } } - return new Matrix(columns); + return new MatrixValue(columns); } default: unreachable(`invalid serialized value kind: ${valueKind}`); @@ -2015,35 +2072,23 @@ export function isFloatType(ty: Type): boolean { } /// All floating-point scalar types -const kFloatScalars = [TypeAbstractFloat, TypeF32, TypeF16] as const; +const kFloatScalars = [Type.abstractFloat, Type.f32, Type.f16] as const; /// All floating-point vec2 types -const kFloatVec2 = [ - TypeVec(2, TypeAbstractFloat), - TypeVec(2, TypeF32), - TypeVec(2, TypeF16), -] as const; +const kFloatVec2 = [Type.vec(2, Type.abstractFloat), Type.vec2f, Type.vec2h] as const; /// All floating-point vec3 types -const kFloatVec3 = [ - TypeVec(3, TypeAbstractFloat), - TypeVec(3, TypeF32), - TypeVec(3, TypeF16), -] as const; +const kFloatVec3 = [Type.vec(3, Type.abstractFloat), Type.vec3f, Type.vec3h] as const; /// All floating-point vec4 types -const kFloatVec4 = [ - TypeVec(4, TypeAbstractFloat), - TypeVec(4, TypeF32), - TypeVec(4, TypeF16), -] as const; +const kFloatVec4 = [Type.vec(4, Type.abstractFloat), Type.vec4f, Type.vec4h] as const; /// All f16 floating-point scalar and vector types export const kConcreteF16ScalarsAndVectors = [ - TypeF16, - TypeVec(2, TypeF16), - TypeVec(3, TypeF16), - TypeVec(4, TypeF16), + Type.f16, + Type.vec2h, + Type.vec3h, + Type.vec4h, ] as const; /// All floating-point scalar and vector types @@ -2063,50 +2108,50 @@ export const kFloatScalarsAndVectors = [ /// All concrete integer scalar and vector types export const kConcreteIntegerScalarsAndVectors = [ - TypeI32, - TypeVec(2, TypeI32), - TypeVec(3, TypeI32), - TypeVec(4, TypeI32), - TypeU32, - TypeVec(2, TypeU32), - TypeVec(3, TypeU32), - TypeVec(4, TypeU32), + Type.i32, + Type.vec2i, + Type.vec3i, + Type.vec4i, + Type.u32, + Type.vec2u, + Type.vec3u, + Type.vec4u, ] as const; /// All signed integer scalar and vector types export const kConcreteSignedIntegerScalarsAndVectors = [ - TypeI32, - TypeVec(2, TypeI32), - TypeVec(3, TypeI32), - TypeVec(4, TypeI32), + Type.i32, + Type.vec2i, + Type.vec3i, + Type.vec4i, ] as const; /// All unsigned integer scalar and vector types export const kConcreteUnsignedIntegerScalarsAndVectors = [ - TypeU32, - TypeVec(2, TypeU32), - TypeVec(3, TypeU32), - TypeVec(4, TypeU32), + Type.u32, + Type.vec2u, + Type.vec3u, + Type.vec4u, ] as const; /// All types which are convertable to floating-point scalar types. -export const kConvertableToFloatScalar = [TypeAbstractInt, ...kFloatScalars] as const; +export const kConvertableToFloatScalar = [Type.abstractInt, ...kFloatScalars] as const; /// All types which are convertable to floating-point vector 2 types. -export const kConvertableToFloatVec2 = [TypeVec(2, TypeAbstractInt), ...kFloatVec2] as const; +export const kConvertableToFloatVec2 = [Type.vec(2, Type.abstractInt), ...kFloatVec2] as const; /// All types which are convertable to floating-point vector 3 types. -export const kConvertableToFloatVec3 = [TypeVec(3, TypeAbstractInt), ...kFloatVec3] as const; +export const kConvertableToFloatVec3 = [Type.vec(3, Type.abstractInt), ...kFloatVec3] as const; /// All types which are convertable to floating-point vector 4 types. -export const kConvertableToFloatVec4 = [TypeVec(4, TypeAbstractInt), ...kFloatVec4] as const; +export const kConvertableToFloatVec4 = [Type.vec(4, Type.abstractInt), ...kFloatVec4] as const; /// All types which are convertable to floating-point scalar or vector types. export const kConvertableToFloatScalarsAndVectors = [ - TypeAbstractInt, - TypeVec(2, TypeAbstractInt), - TypeVec(3, TypeAbstractInt), - TypeVec(4, TypeAbstractInt), + Type.abstractInt, + Type.vec(2, Type.abstractInt), + Type.vec(3, Type.abstractInt), + Type.vec(4, Type.abstractInt), ...kFloatScalarsAndVectors, ] as const; diff --git a/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index ea7bb838d4ca..9015eca362eb 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -12,7 +12,7 @@ import { f16, f32, isFloatType, - Scalar, + ScalarValue, ScalarType, toMatrix, toVector, @@ -1073,8 +1073,8 @@ export abstract class FPTraits { public abstract readonly flushSubnormal: (n: number) => number; /** @returns 1 * ULP: (number) */ public abstract readonly oneULP: (target: number, mode?: FlushMode) => number; - /** @returns a builder for converting numbers to Scalars */ - public abstract readonly scalarBuilder: (n: number) => Scalar; + /** @returns a builder for converting numbers to ScalarsValues */ + public abstract readonly scalarBuilder: (n: number) => ScalarValue; /** @returns a range of scalars for testing */ public abstract scalarRange(): readonly number[]; /** @returns a reduced range of scalars for testing */ From 3e45aee0b16dc724a79a0feb0490e2ddb06c9f0d Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Thu, 7 Mar 2024 11:00:27 +0000 Subject: [PATCH 03/33] Add ArrayType / ArrayValue Not currently used by anything --- src/resources/cache/hashes.json | 216 +++++++++--------- .../expression/call/builtin/abs.spec.ts | 4 +- .../expression/call/builtin/acos.spec.ts | 4 +- .../expression/call/builtin/acosh.spec.ts | 6 +- .../expression/call/builtin/asin.spec.ts | 4 +- .../expression/call/builtin/asinh.spec.ts | 6 +- .../expression/call/builtin/atan.spec.ts | 4 +- .../expression/call/builtin/atan2.spec.ts | 6 +- .../expression/call/builtin/atanh.spec.ts | 4 +- .../expression/call/builtin/ceil.spec.ts | 4 +- .../expression/call/builtin/clamp.spec.ts | 4 +- .../call/builtin/const_override_validation.ts | 15 +- .../expression/call/builtin/cos.spec.ts | 4 +- .../expression/call/builtin/cosh.spec.ts | 6 +- .../expression/call/builtin/degrees.spec.ts | 6 +- .../call/builtin/derivatives.spec.ts | 6 +- .../expression/call/builtin/exp.spec.ts | 6 +- .../expression/call/builtin/exp2.spec.ts | 6 +- .../expression/call/builtin/floor.spec.ts | 4 +- .../call/builtin/inverseSqrt.spec.ts | 6 +- .../expression/call/builtin/length.spec.ts | 20 +- .../expression/call/builtin/log.spec.ts | 4 +- .../expression/call/builtin/log2.spec.ts | 4 +- .../expression/call/builtin/modf.spec.ts | 4 +- .../expression/call/builtin/radians.spec.ts | 4 +- .../expression/call/builtin/round.spec.ts | 8 +- .../expression/call/builtin/saturate.spec.ts | 4 +- .../expression/call/builtin/sign.spec.ts | 4 +- .../expression/call/builtin/sin.spec.ts | 4 +- .../expression/call/builtin/sinh.spec.ts | 6 +- .../expression/call/builtin/sqrt.spec.ts | 6 +- .../expression/call/builtin/tan.spec.ts | 6 +- src/webgpu/util/conversion.ts | 184 +++++++++++++-- 33 files changed, 360 insertions(+), 219 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 6733022574c8..6c9518de081e 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "45a00f48", - "webgpu/shader/execution/binary/af_logical.bin": "458eba4", - "webgpu/shader/execution/binary/af_division.bin": "ed6916d6", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "ddeaa1d3", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "bdc18f23", - "webgpu/shader/execution/binary/af_multiplication.bin": "e6e11b40", - "webgpu/shader/execution/binary/af_remainder.bin": "61849bd4", - "webgpu/shader/execution/binary/af_subtraction.bin": "6afd0c9a", - "webgpu/shader/execution/binary/f16_addition.bin": "118a69b1", - "webgpu/shader/execution/binary/f16_logical.bin": "53cbe093", - "webgpu/shader/execution/binary/f16_division.bin": "6cf5db74", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "37b3e5b1", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "f9f9c546", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "10c32980", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "4a992ee0", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "3969786a", - "webgpu/shader/execution/binary/f16_multiplication.bin": "27375c0a", - "webgpu/shader/execution/binary/f16_remainder.bin": "1e5d8fc7", - "webgpu/shader/execution/binary/f16_subtraction.bin": "daffd0ed", - "webgpu/shader/execution/binary/f32_addition.bin": "384766d0", - "webgpu/shader/execution/binary/f32_logical.bin": "d4f9fd6a", - "webgpu/shader/execution/binary/f32_division.bin": "760f650f", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "504aac15", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "740e31c4", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "3c5abc3c", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c41fee39", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "43bfea5a", - "webgpu/shader/execution/binary/f32_multiplication.bin": "d6b990a9", - "webgpu/shader/execution/binary/f32_remainder.bin": "53f7d8e9", - "webgpu/shader/execution/binary/f32_subtraction.bin": "f644082", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "8ce49cc7", - "webgpu/shader/execution/binary/i32_comparison.bin": "ac0e960f", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "68560dc0", - "webgpu/shader/execution/binary/u32_comparison.bin": "ec9ec4c6", - "webgpu/shader/execution/abs.bin": "d7043582", - "webgpu/shader/execution/acos.bin": "a7a01d03", - "webgpu/shader/execution/acosh.bin": "3bfd9ebc", - "webgpu/shader/execution/asin.bin": "f91850f1", - "webgpu/shader/execution/asinh.bin": "19169ea3", - "webgpu/shader/execution/atan.bin": "51a04ddb", - "webgpu/shader/execution/atan2.bin": "e732e242", - "webgpu/shader/execution/atanh.bin": "4763b613", - "webgpu/shader/execution/bitcast.bin": "195c00a7", - "webgpu/shader/execution/ceil.bin": "9856b786", - "webgpu/shader/execution/clamp.bin": "5a700a65", - "webgpu/shader/execution/cos.bin": "ff14c921", - "webgpu/shader/execution/cosh.bin": "21c587ad", - "webgpu/shader/execution/cross.bin": "c159771f", - "webgpu/shader/execution/degrees.bin": "b0de92be", - "webgpu/shader/execution/determinant.bin": "83d642d4", - "webgpu/shader/execution/distance.bin": "e65d0cb7", - "webgpu/shader/execution/dot.bin": "dc57a00c", - "webgpu/shader/execution/exp.bin": "f0c6b19", - "webgpu/shader/execution/exp2.bin": "5d3dd4e0", - "webgpu/shader/execution/faceForward.bin": "3979a4de", - "webgpu/shader/execution/floor.bin": "3fecf76d", - "webgpu/shader/execution/fma.bin": "e7fe86b8", - "webgpu/shader/execution/fract.bin": "71caa066", - "webgpu/shader/execution/frexp.bin": "ed72dcec", - "webgpu/shader/execution/inverseSqrt.bin": "383e6e9c", - "webgpu/shader/execution/ldexp.bin": "bedfc1d5", - "webgpu/shader/execution/length.bin": "38e35ab4", - "webgpu/shader/execution/log.bin": "2517404c", - "webgpu/shader/execution/log2.bin": "a833136", - "webgpu/shader/execution/max.bin": "8c2f7c51", - "webgpu/shader/execution/min.bin": "7f732adb", - "webgpu/shader/execution/mix.bin": "982c982c", - "webgpu/shader/execution/modf.bin": "743632fc", - "webgpu/shader/execution/normalize.bin": "52adf424", - "webgpu/shader/execution/pack2x16float.bin": "2c879955", - "webgpu/shader/execution/pow.bin": "ba686c94", - "webgpu/shader/execution/quantizeToF16.bin": "e704252d", - "webgpu/shader/execution/radians.bin": "afe57c6e", - "webgpu/shader/execution/reflect.bin": "332e001e", - "webgpu/shader/execution/refract.bin": "246f6c0b", - "webgpu/shader/execution/round.bin": "e555383f", - "webgpu/shader/execution/saturate.bin": "9dce4047", - "webgpu/shader/execution/sign.bin": "3ef39d2e", - "webgpu/shader/execution/sin.bin": "8546b36c", - "webgpu/shader/execution/sinh.bin": "72ae8d37", - "webgpu/shader/execution/smoothstep.bin": "79eca0b6", - "webgpu/shader/execution/sqrt.bin": "ac8f95e9", - "webgpu/shader/execution/step.bin": "34ce6432", - "webgpu/shader/execution/tan.bin": "928e0e2f", - "webgpu/shader/execution/tanh.bin": "be078de7", - "webgpu/shader/execution/transpose.bin": "2ce22a5b", - "webgpu/shader/execution/trunc.bin": "26115486", - "webgpu/shader/execution/unpack2x16float.bin": "d052cda6", - "webgpu/shader/execution/unpack2x16snorm.bin": "a3ab8e29", - "webgpu/shader/execution/unpack2x16unorm.bin": "f42b9498", - "webgpu/shader/execution/unpack4x8snorm.bin": "5c90b367", - "webgpu/shader/execution/unpack4x8unorm.bin": "ef24abbe", - "webgpu/shader/execution/unary/af_arithmetic.bin": "28b510fa", - "webgpu/shader/execution/unary/af_assignment.bin": "4f4d507a", - "webgpu/shader/execution/unary/bool_conversion.bin": "5cbbd5e2", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "55ff626f", - "webgpu/shader/execution/unary/f16_conversion.bin": "e16712e2", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "672609de", - "webgpu/shader/execution/unary/f32_conversion.bin": "daa3ffb8", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "eecbb027", - "webgpu/shader/execution/unary/i32_conversion.bin": "c3f19a9", - "webgpu/shader/execution/unary/u32_conversion.bin": "b58b1876", - "webgpu/shader/execution/unary/ai_assignment.bin": "326020c6", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "40123e00", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "37ffc69", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "35e08b61", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "6f31c22f", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "7b17ec2a" + "webgpu/shader/execution/binary/af_addition.bin": "25dcfcce", + "webgpu/shader/execution/binary/af_logical.bin": "409d37cf", + "webgpu/shader/execution/binary/af_division.bin": "bfcb23bd", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "cefea21a", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "624d816d", + "webgpu/shader/execution/binary/af_multiplication.bin": "34077746", + "webgpu/shader/execution/binary/af_remainder.bin": "9eddf765", + "webgpu/shader/execution/binary/af_subtraction.bin": "629bbf4", + "webgpu/shader/execution/binary/f16_addition.bin": "e3ff1263", + "webgpu/shader/execution/binary/f16_logical.bin": "99c0a0ed", + "webgpu/shader/execution/binary/f16_division.bin": "c0f3376", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "3054016e", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "9cb6bbb9", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "82f32c1d", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "29e9e0c7", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "109260c1", + "webgpu/shader/execution/binary/f16_multiplication.bin": "39287c38", + "webgpu/shader/execution/binary/f16_remainder.bin": "98b48517", + "webgpu/shader/execution/binary/f16_subtraction.bin": "a4975dc4", + "webgpu/shader/execution/binary/f32_addition.bin": "b0e6fddd", + "webgpu/shader/execution/binary/f32_logical.bin": "71a0f0be", + "webgpu/shader/execution/binary/f32_division.bin": "29a37cf9", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "76e48aa9", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "37f3a30b", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "f00c4cd8", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "25553482", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "26750bd3", + "webgpu/shader/execution/binary/f32_multiplication.bin": "68a7516b", + "webgpu/shader/execution/binary/f32_remainder.bin": "fb22b625", + "webgpu/shader/execution/binary/f32_subtraction.bin": "2e26b05f", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "9256ccab", + "webgpu/shader/execution/binary/i32_comparison.bin": "c813abfa", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "498f877e", + "webgpu/shader/execution/binary/u32_comparison.bin": "d69e1738", + "webgpu/shader/execution/abs.bin": "a6b6fed1", + "webgpu/shader/execution/acos.bin": "2a8f33b0", + "webgpu/shader/execution/acosh.bin": "3b39f532", + "webgpu/shader/execution/asin.bin": "94c67af3", + "webgpu/shader/execution/asinh.bin": "34c8547a", + "webgpu/shader/execution/atan.bin": "c5f98dae", + "webgpu/shader/execution/atan2.bin": "5c666ada", + "webgpu/shader/execution/atanh.bin": "5efbba8b", + "webgpu/shader/execution/bitcast.bin": "e90abd26", + "webgpu/shader/execution/ceil.bin": "fe43b603", + "webgpu/shader/execution/clamp.bin": "f4ec869a", + "webgpu/shader/execution/cos.bin": "78f30673", + "webgpu/shader/execution/cosh.bin": "15c18b89", + "webgpu/shader/execution/cross.bin": "72823897", + "webgpu/shader/execution/degrees.bin": "3b06fea4", + "webgpu/shader/execution/determinant.bin": "f0f66549", + "webgpu/shader/execution/distance.bin": "593c41fb", + "webgpu/shader/execution/dot.bin": "3eee146b", + "webgpu/shader/execution/exp.bin": "7f926769", + "webgpu/shader/execution/exp2.bin": "715c82a", + "webgpu/shader/execution/faceForward.bin": "8a365384", + "webgpu/shader/execution/floor.bin": "7b9a6254", + "webgpu/shader/execution/fma.bin": "88549fc7", + "webgpu/shader/execution/fract.bin": "e4eff2f9", + "webgpu/shader/execution/frexp.bin": "18a53421", + "webgpu/shader/execution/inverseSqrt.bin": "71016a37", + "webgpu/shader/execution/ldexp.bin": "450c6068", + "webgpu/shader/execution/length.bin": "62190368", + "webgpu/shader/execution/log.bin": "c4b1985b", + "webgpu/shader/execution/log2.bin": "28ccd982", + "webgpu/shader/execution/max.bin": "ba573f18", + "webgpu/shader/execution/min.bin": "5a834580", + "webgpu/shader/execution/mix.bin": "bf942d40", + "webgpu/shader/execution/modf.bin": "a8de3f36", + "webgpu/shader/execution/normalize.bin": "4bf84190", + "webgpu/shader/execution/pack2x16float.bin": "cc42ef45", + "webgpu/shader/execution/pow.bin": "6e8d08d1", + "webgpu/shader/execution/quantizeToF16.bin": "8c615e93", + "webgpu/shader/execution/radians.bin": "624ff571", + "webgpu/shader/execution/reflect.bin": "d0d624ae", + "webgpu/shader/execution/refract.bin": "3dd68359", + "webgpu/shader/execution/round.bin": "502c36a8", + "webgpu/shader/execution/saturate.bin": "be4cf88f", + "webgpu/shader/execution/sign.bin": "91310555", + "webgpu/shader/execution/sin.bin": "441d6be3", + "webgpu/shader/execution/sinh.bin": "4ebaa2e7", + "webgpu/shader/execution/smoothstep.bin": "6298e644", + "webgpu/shader/execution/sqrt.bin": "3cf64df1", + "webgpu/shader/execution/step.bin": "90148c99", + "webgpu/shader/execution/tan.bin": "e753749c", + "webgpu/shader/execution/tanh.bin": "62c99641", + "webgpu/shader/execution/transpose.bin": "2a4448c6", + "webgpu/shader/execution/trunc.bin": "ed9a0d22", + "webgpu/shader/execution/unpack2x16float.bin": "e81297f7", + "webgpu/shader/execution/unpack2x16snorm.bin": "513f9a8b", + "webgpu/shader/execution/unpack2x16unorm.bin": "8b56d0ce", + "webgpu/shader/execution/unpack4x8snorm.bin": "51af8a63", + "webgpu/shader/execution/unpack4x8unorm.bin": "302cf4a6", + "webgpu/shader/execution/unary/af_arithmetic.bin": "cd4618d", + "webgpu/shader/execution/unary/af_assignment.bin": "7d5de0f1", + "webgpu/shader/execution/unary/bool_conversion.bin": "401fb8c5", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "ad86d013", + "webgpu/shader/execution/unary/f16_conversion.bin": "86e35a85", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "fe97afd1", + "webgpu/shader/execution/unary/f32_conversion.bin": "97730c3c", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "69f783bb", + "webgpu/shader/execution/unary/i32_conversion.bin": "4fec061e", + "webgpu/shader/execution/unary/u32_conversion.bin": "1a298dea", + "webgpu/shader/execution/unary/ai_assignment.bin": "1c2edca2", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "657f2fb7", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "216769d9", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "544f4363", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "14212e77", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "57f19c15" } \ No newline at end of file diff --git a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts index f7084b5d07d4..3fc9eeb22c3d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts @@ -5,7 +5,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; -import { Type, elementType, kAllScalarsAndVectors } from '../../../../../util/conversion.js'; +import { Type, kAllScalarsAndVectors, scalarTypeOf } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; import { @@ -34,7 +34,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts index 13e3492a5146..9b6438aaec65 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts index 8e60459676f6..c3644befc9ba 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -56,7 +56,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.acosh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts index fef4540f4304..bff34f104dee 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts index a029e38d02f3..cec4b73c6094 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts @@ -6,9 +6,9 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, Type, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -50,7 +50,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -59,7 +59,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.asinh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts index e8f325be5b94..76bafbbcca8a 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -46,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts index 712695ed0eb4..1a6776a018fa 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { VectorValue, - elementType, kFloatScalarsAndVectors, kConcreteIntegerScalarsAndVectors, + scalarTypeOf, Type, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -54,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -62,7 +62,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const type = kValuesTypes[t.params.type]; const expectedResult = isRepresentable( Math.abs(Math.atan2(Number(t.params.x), Number(t.params.y))), - elementType(type) + scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts index 460977c78878..1a9184a7416c 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts index 02fdbf70fc98..951958d02c12 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts index 5e8b3f3fe6ef..9a23328d650f 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kFloatScalarsAndVectors, kConcreteIntegerScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -44,7 +44,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('high', u => fullRangeForType(kValuesTypes[u.type], 4)) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts b/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts index 440673c99265..6c46bfb0a054 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts @@ -3,9 +3,10 @@ import { kValue } from '../../../../../util/constants.js'; import { Type, Value, - elementType, - elementsOf, + elementTypeOf, isAbstractType, + scalarElementsOf, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { scalarF16Range, @@ -22,7 +23,7 @@ export function rangeForType( bigint_range: readonly bigint[] ): (type: Type) => readonly (number | bigint)[] { return (type: Type): readonly (number | bigint)[] => { - switch (elementType(type).kind) { + switch (scalarTypeOf(type).kind) { case 'abstract-float': case 'f32': case 'f16': @@ -131,7 +132,7 @@ export type ConstantOrOverrideStage = 'constant' | 'override'; * @returns true if evaluation stage `stage` supports expressions of type @p. */ export function stageSupportsType(stage: ConstantOrOverrideStage, type: Type) { - if (stage === 'override' && isAbstractType(elementType(type)!)) { + if (stage === 'override' && isAbstractType(elementTypeOf(type)!)) { // Abstract numerics are concretized before being used in an override expression. return false; } @@ -154,7 +155,7 @@ export function validateConstOrOverrideBuiltinEval( args: Value[], stage: ConstantOrOverrideStage ) { - const elTys = args.map(arg => elementType(arg.type)!); + const elTys = args.map(arg => elementTypeOf(arg.type)!); const enables = elTys.some(ty => ty === Type.f16) ? 'enable f16;' : ''; switch (stage) { @@ -174,7 +175,7 @@ const v = ${builtin}(${args.map(arg => arg.wgsl()).join(', ')});` let numOverrides = 0; for (const arg of args) { const argOverrides: string[] = []; - for (const el of elementsOf(arg)) { + for (const el of scalarElementsOf(arg)) { const name = `o${numOverrides++}`; overrideDecls.push(`override ${name} : ${el.type};`); argOverrides.push(name); @@ -200,7 +201,7 @@ export function fullRangeForType(type: Type, count?: number): readonly (number | if (count === undefined) { count = 25; } - switch (elementType(type)?.kind) { + switch (scalarTypeOf(type)?.kind) { case 'abstract-float': return scalarF64Range({ pos_sub: Math.ceil((count * 1) / 5), diff --git a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts index 09b807c1039d..81d283abb299 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -46,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts index ffb01b707671..4178f5760d87 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -40,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -49,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.cosh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts index 35c7ff3c9062..f1cba6a6aef8 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -40,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -49,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input const expectedResult = isRepresentable( (Number(t.params.value) * 180) / Math.PI, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts index dd0a3fb595a8..54620ce17904 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts @@ -6,9 +6,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConcreteF16ScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -113,14 +113,14 @@ Derivative builtins only accept f32 scalar and vector types. u.combine('type', keysOf(kArgumentTypes)).combine('call', ['', ...kDerivativeBuiltins]) ) .beforeAllSubcases(t => { - if (elementType(kArgumentTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kArgumentTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) .fn(t => { const type = kArgumentTypes[t.params.type]; const code = ` -${elementType(kArgumentTypes[t.params.type]) === Type.f16 ? 'enable f16;' : ''} +${scalarTypeOf(kArgumentTypes[t.params.type]) === Type.f16 ? 'enable f16;' : ''} fn foo() { let x: ${type.toString()} = ${t.params.call}(${type.create(1).wgsl()}); diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts index edac1c0c529d..dcbd5b79e51d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts @@ -8,9 +8,9 @@ import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tabl import { kValue } from '../../../../../util/constants.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -70,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => valueForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -79,7 +79,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.exp(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts index 4f961e4259bc..0144abeefb04 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts @@ -8,9 +8,9 @@ import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tabl import { kValue } from '../../../../../util/constants.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -70,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => valueForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -79,7 +79,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.pow(2, Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts index f075b5e25c1b..b65fafc74466 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts index 4ab15948664b..6310bfe25fee 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -58,7 +58,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input isRepresentable( 1 / Math.sqrt(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts index 620f09837329..003ad6811d72 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts @@ -8,12 +8,12 @@ import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tabl import { ScalarType, Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalar, kConvertableToFloatVec2, kConvertableToFloatVec3, kConvertableToFloatVec4, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -55,12 +55,12 @@ function calculate( isIntermediateRepresentable: isRepresentable( squareSum, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ), isResultRepresentable: isRepresentable( result, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ), result, }; @@ -84,7 +84,7 @@ the input scalar value always compiles without error .expand('value', u => fullRangeForType(kScalarTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kScalarTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kScalarTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -116,11 +116,11 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .beginSubcases() .expand('x', u => fullRangeForType(kVec2Types[u.type], 5)) .expand('y', u => fullRangeForType(kVec2Types[u.type], 5)) - .expand('_result', u => [calculate([u.x, u.y], elementType(kVec2Types[u.type]))]) + .expand('_result', u => [calculate([u.x, u.y], scalarTypeOf(kVec2Types[u.type]))]) .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec2Types[t.params.type]) === Type.f16) { + if (scalarTypeOf(kVec2Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -152,11 +152,11 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .expand('x', u => fullRangeForType(kVec3Types[u.type], 4)) .expand('y', u => fullRangeForType(kVec3Types[u.type], 4)) .expand('z', u => fullRangeForType(kVec3Types[u.type], 4)) - .expand('_result', u => [calculate([u.x, u.y, u.z], elementType(kVec3Types[u.type]))]) + .expand('_result', u => [calculate([u.x, u.y, u.z], scalarTypeOf(kVec3Types[u.type]))]) .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec3Types[t.params.type]) === Type.f16) { + if (scalarTypeOf(kVec3Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -189,11 +189,11 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .expand('y', u => fullRangeForType(kVec4Types[u.type], 3)) .expand('z', u => fullRangeForType(kVec4Types[u.type], 3)) .expand('w', u => fullRangeForType(kVec4Types[u.type], 3)) - .expand('_result', u => [calculate([u.x, u.y, u.z, u.w], elementType(kVec4Types[u.type]))]) + .expand('_result', u => [calculate([u.x, u.y, u.z, u.w], scalarTypeOf(kVec4Types[u.type]))]) .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec4Types[t.params.type]) === Type.f16) { + if (scalarTypeOf(kVec4Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts index 18ea19c3e341..6d755304f2ff 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts index b3a6c338c595..0dfe64153651 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts index 3f0b4a6064ab..2a90fa878ea6 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts index f7f60ec8f478..8689bc3dbbe8 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts index 821fc0f3c382..dae7482c18ce 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { fpTraitsFor } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,10 +39,10 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .filter(u => stageSupportsType(u.stage, kValuesTypes[u.type])) .beginSubcases() .expand('value', u => { - if (elementType(kValuesTypes[u.type]).kind === 'abstract-int') { + if (scalarTypeOf(kValuesTypes[u.type]).kind === 'abstract-int') { return fullRangeForType(kValuesTypes[u.type]); } else { - const constants = fpTraitsFor(elementType(kValuesTypes[u.type])).constants(); + const constants = fpTraitsFor(scalarTypeOf(kValuesTypes[u.type])).constants(); return unique(fullRangeForType(kValuesTypes[u.type]), [ constants.negative.min + 0.1, constants.positive.max - 0.1, @@ -51,7 +51,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input }) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts index 354248e205ba..cbd1b3f369ff 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts index 238bb51d8bc7..f8e00ab9486d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts @@ -7,10 +7,10 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kFloatScalarsAndVectors, kConcreteSignedIntegerScalarsAndVectors, kConcreteUnsignedIntegerScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -43,7 +43,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts index a408323e3a82..3c807d094c5c 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -46,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts index 8f5a4c0c60aa..1983d0e1a7e4 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -40,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -49,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.sinh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts index ff72a63379bc..7e983adf188c 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -58,7 +58,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input isRepresentable( Math.sqrt(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts index ed5286781d56..097c2b374d95 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { fpTraitsFor } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const type = kValuesTypes[t.params.type]; const fp = fpTraitsFor( // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); const smallestPositive = fp.constants().positive.min; const v = fp.quantize(Number(t.params.value)); diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 2b9e7989bcdb..a44f34cedfeb 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -616,6 +616,10 @@ export class ScalarType { return this._size; } + public get alignment(): number { + return this._size; + } + /** Constructs a ScalarValue of this type with `value` */ public create(value: number | bigint): ScalarValue { switch (typeof value) { @@ -663,14 +667,17 @@ export class VectorType { readonly width: number; // Number of elements in the vector readonly elementType: ScalarType; // Element type + // Maps a string representation of a vector type to vector type. + private static instances = new Map(); + static create(width: number, elementType: ScalarType): VectorType { const key = `${elementType.toString()} ${width}}`; - let ty = vectorTypes.get(key); + let ty = this.instances.get(key); if (ty !== undefined) { return ty; } ty = new VectorType(width, elementType); - vectorTypes.set(key, ty); + this.instances.set(key, ty); return ty; } @@ -700,6 +707,14 @@ export class VectorType { return this.elementType.size * this.width; } + public get alignment(): number { + return VectorType.alignmentOf(this.width, this.elementType); + } + + public static alignmentOf(width: number, elementType: ScalarType) { + return elementType.size * (width === 3 ? 4 : width); + } + /** Constructs a Vector of this type with the given values */ public create(value: (number | bigint) | readonly (number | bigint)[]): VectorValue { if (value instanceof Array) { @@ -711,23 +726,23 @@ export class VectorType { } } -// Maps a string representation of a vector type to vector type. -const vectorTypes = new Map(); - /** MatrixType describes the type of WGSL Matrix. */ export class MatrixType { readonly cols: number; // Number of columns in the Matrix readonly rows: number; // Number of elements per column in the Matrix readonly elementType: ScalarType; // Element type + // Maps a string representation of a Matrix type to Matrix type. + private static instances = new Map(); + static create(cols: number, rows: number, elementType: ScalarType): MatrixType { const key = `${elementType.toString()} ${cols} ${rows}`; - let ty = matrixTypes.get(key); + let ty = this.instances.get(key); if (ty !== undefined) { return ty; } ty = new MatrixType(cols, rows, elementType); - matrixTypes.set(key, ty); + this.instances.set(key, ty); return ty; } @@ -767,6 +782,14 @@ export class MatrixType { return `mat${this.cols}x${this.rows}<${this.elementType}>`; } + public get size(): number { + return VectorType.alignmentOf(this.rows, this.elementType) * this.cols; + } + + public get alignment(): number { + return VectorType.alignmentOf(this.rows, this.elementType); + } + /** Constructs a Matrix of this type with the given values */ public create(value: (number | bigint) | readonly (number | bigint)[]): MatrixValue { if (value instanceof Array) { @@ -783,8 +806,55 @@ export class MatrixType { } } -// Maps a string representation of a Matrix type to Matrix type. -const matrixTypes = new Map(); +/** ArrayType describes the type of WGSL Array. */ +export class ArrayType { + readonly count: number; // Number of elements in the array + readonly elementType: Type; // Element type + + // Maps a string representation of a array type to array type. + private static instances = new Map(); + + static create(count: number, elementType: Type): ArrayType { + const key = `${elementType.toString()} ${count}`; + let ty = this.instances.get(key); + if (ty !== undefined) { + return ty; + } + ty = new ArrayType(count, elementType); + this.instances.set(key, ty); + return ty; + } + + constructor(count: number, elementType: Type) { + this.count = count; + this.elementType = elementType; + } + + /** + * @returns a array constructed from the values read from the buffer at the + * given byte offset + */ + public read(buf: Uint8Array, offset: number): ArrayValue { + const elements: Array = []; + for (let i = 0; i < this.count; i++) { + elements[i] = this.elementType.read(buf, offset); + offset += this.elementType.size; + } + return new ArrayValue(elements); + } + + public toString(): string { + return `array<${this.elementType}, ${this.count}>`; + } + + public get size(): number { + return this.elementType.alignment * this.count; + } + + public get alignment(): number { + return this.elementType.alignment; + } +} /** ArrayElementType infers the element type of the indexable type A */ type ArrayElementType = A extends { [index: number]: infer T } ? T : never; @@ -838,8 +908,8 @@ const boolType = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => bool(valueFromBytes(workingDataU32, buf, offset) !== 0) ); -/** Type is a ScalarType, VectorType, or MatrixType. */ -export type Type = ScalarType | VectorType | MatrixType; +/** Type is a ScalarType, VectorType, MatrixType or ArrayType. */ +export type Type = ScalarType | VectorType | MatrixType | ArrayType; /** Type holds pre-declared Types along with helper constructor functions. */ export const Type = { @@ -894,6 +964,8 @@ export const Type = { mat3x4h: MatrixType.create(3, 4, f16Type), mat4x4f: MatrixType.create(4, 4, f32Type), mat4x4h: MatrixType.create(4, 4, f16Type), + + array: (count: number, elementType: Type) => ArrayType.create(count, elementType), }; /** @returns the ScalarType from the ScalarKind */ @@ -937,11 +1009,31 @@ export function numElementsOf(ty: Type): number { if (ty instanceof MatrixType) { return ty.cols * ty.rows; } + if (ty instanceof ArrayType) { + return ty.count; + } throw new Error(`unhandled type ${ty}`); } /** @returns the scalar elements of the given Value */ -export function elementsOf(value: Value): ScalarValue[] { +export function elementsOf(value: Value): Value[] { + if (isScalarValue(value)) { + return [value]; + } + if (value instanceof VectorValue) { + return value.elements; + } + if (value instanceof MatrixValue) { + return value.elements.flat(); + } + if (value instanceof ArrayValue) { + return value.elements; + } + throw new Error(`unhandled value ${value}`); +} + +/** @returns the scalar elements of the given Value */ +export function scalarElementsOf(value: Value): ScalarValue[] { if (isScalarValue(value)) { return [value]; } @@ -951,9 +1043,20 @@ export function elementsOf(value: Value): ScalarValue[] { if (value instanceof MatrixValue) { return value.elements.flat(); } + if (value instanceof ArrayValue) { + return value.elements.map(els => scalarElementsOf(els)).flat(); + } throw new Error(`unhandled value ${value}`); } +/** @returns the inner element type of the given type */ +export function elementTypeOf(t: Type) { + if (t instanceof ScalarType) { + return t; + } + return t.elementType; +} + /** @returns the scalar (element) type of the given Type */ export function scalarTypeOf(ty: Type): ScalarType { if (ty instanceof ScalarType) { @@ -965,6 +1068,9 @@ export function scalarTypeOf(ty: Type): ScalarType { if (ty instanceof MatrixType) { return ty.elementType; } + if (ty instanceof ArrayType) { + return scalarTypeOf(ty.elementType); + } throw new Error(`unhandled type ${ty}`); } @@ -1754,6 +1860,48 @@ export class MatrixValue { } } +/** + * Class that encapsulates an Array value. + */ +export class ArrayValue { + readonly elements: Value[]; + readonly type: ArrayType; + + public constructor(elements: Array) { + const elem_type = elements[0].type; + if (!elements.every(c => elements.every(r => objectEquals(r.type, elem_type)))) { + throw new Error(`cannot mix array element types`); + } + + this.elements = elements; + this.type = ArrayType.create(elements.length, elem_type); + } + + /** + * Copies the matrix value to the Uint8Array buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the byte offset within buffer + */ + public copyTo(buffer: Uint8Array, offset: number) { + for (const element of this.elements) { + element.copyTo(buffer, offset); + offset += this.type.elementType.size; + } + } + + /** + * @returns the WGSL representation of this matrix value + */ + public wgsl(): string { + const els = this.elements.map(r => r.wgsl()).join(', '); + return `${this.type}(${els})`; + } + + public toString(): string { + return this.wgsl(); + } +} + /** * Helper for constructing Matrices from arrays of numbers * @@ -1776,8 +1924,8 @@ export function toMatrix(m: ROArrayArray, op: (n: number) => ScalarValue return new MatrixValue(elements); } -/** Value is a Scalar, Vector or Matrix value. */ -export type Value = ScalarValue | VectorValue | MatrixValue; +/** Value is a Scalar, Vector, Matrix or Array value. */ +export type Value = ScalarValue | VectorValue | MatrixValue | ArrayValue; export type SerializedScalarValue = { kind: 'scalar'; @@ -2160,11 +2308,3 @@ export const kAllScalarsAndVectors = [ ...kConvertableToFloatScalarsAndVectors, ...kConcreteIntegerScalarsAndVectors, ] as const; - -/** @returns the inner element type of the given type */ -export function elementType(t: ScalarType | VectorType | MatrixType) { - if (t instanceof ScalarType) { - return t; - } - return t.elementType; -} From 0a68bf71fb07b7ecd117d84d919fbaf81e302403 Mon Sep 17 00:00:00 2001 From: Greggman Date: Thu, 7 Mar 2024 20:59:20 -0800 Subject: [PATCH 04/33] Add AdapterLimitsGPUTest that sets all limits to the adapter's (#3466) Note: I'm not sure how I refactored device_pool.ts is good but, the old code created one initial device with no descriptor the first time in acquire. It then created another of the type actually wanted by the user. That no longer works becauase the adapter is passed in and so if the first requestDevice succeeds, then the adapter has been used and can't be used for the second one. Also, assuming this change is approved, I can (and should?) probably refactor LimitsTest to use this vs how it's doing it now. That would end up using cached devices. --- .../api/operation/rendering/draw.spec.ts | 4 +- src/webgpu/gpu_test.ts | 90 +++++++++++++++++-- src/webgpu/util/device_pool.ts | 32 ++++--- 3 files changed, 104 insertions(+), 22 deletions(-) diff --git a/src/webgpu/api/operation/rendering/draw.spec.ts b/src/webgpu/api/operation/rendering/draw.spec.ts index 6ed4be08fd24..fc8fdf2224d1 100644 --- a/src/webgpu/api/operation/rendering/draw.spec.ts +++ b/src/webgpu/api/operation/rendering/draw.spec.ts @@ -11,10 +11,10 @@ import { TypedArrayBufferView, TypedArrayBufferViewConstructor, } from '../../../../common/util/util.js'; -import { GPUTest, TextureTestMixin } from '../../../gpu_test.js'; +import { AdapterLimitsGPUTest, TextureTestMixin } from '../../../gpu_test.js'; import { PerPixelComparison } from '../../../util/texture/texture_ok.js'; -class DrawTest extends TextureTestMixin(GPUTest) { +class DrawTest extends TextureTestMixin(AdapterLimitsGPUTest) { checkTriangleDraw(opts: { firstIndex: number | undefined; count: number; diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts index 1f021218d290..a2ae8f1b901f 100644 --- a/src/webgpu/gpu_test.ts +++ b/src/webgpu/gpu_test.ts @@ -93,11 +93,47 @@ export function initUncanonicalizedDeviceDescriptor( } } +/** + * Gets the adapter limits as a standard JavaScript object. + */ +function getAdapterLimitsAsDeviceRequiredLimits(adapter: GPUAdapter) { + const requiredLimits: Record = {}; + const adapterLimits = adapter.limits as unknown as Record; + for (const key in adapter.limits) { + requiredLimits[key] = adapterLimits[key]; + } + return requiredLimits; +} + +/** + * Conditionally applies adapter limits to device descriptor + * but does not overwrite existing requested limits. + */ +function conditionallyApplyAdapterLimitsToDeviceDescriptor( + adapter: GPUAdapter, + useAdapterLimits: boolean, + descriptor: UncanonicalizedDeviceDescriptor | undefined +): UncanonicalizedDeviceDescriptor { + return { + ...(descriptor || {}), + requiredLimits: { + ...(useAdapterLimits && getAdapterLimitsAsDeviceRequiredLimits(adapter)), + ...(descriptor?.requiredLimits || {}), + }, + }; +} + export class GPUTestSubcaseBatchState extends SubcaseBatchState { /** Provider for default device. */ private provider: Promise | undefined; /** Provider for mismatched device. */ private mismatchedProvider: Promise | undefined; + /** True if device should be created with adapter limits */ + private useAdapterLimits = false; + + constructor(recorder: TestCaseRecorder, params: TestParams) { + super(recorder, params); + } override async postInit(): Promise { // Skip all subcases if there's no device. @@ -123,6 +159,14 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState { return this.provider; } + useAdapterLimitsForDevice() { + assert( + this.provider === undefined, + 'useAdapterLimits must be called before getting the device' + ); + this.useAdapterLimits = true; + } + get isCompatibility() { return globalTestConfig.compatibility; } @@ -140,10 +184,18 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState { */ selectDeviceOrSkipTestCase(descriptor: DeviceSelectionDescriptor): void { assert(this.provider === undefined, "Can't selectDeviceOrSkipTestCase() multiple times"); - this.provider = devicePool.acquire( - this.recorder, - initUncanonicalizedDeviceDescriptor(descriptor) - ); + this.provider = devicePool + .requestAdapter(this.recorder) + .then(adapter => + devicePool.acquire( + adapter, + conditionallyApplyAdapterLimitsToDeviceDescriptor( + adapter, + this.useAdapterLimits, + initUncanonicalizedDeviceDescriptor(descriptor) + ) + ) + ); // Suppress uncaught promise rejection (we'll catch it later). this.provider.catch(() => {}); } @@ -201,10 +253,18 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState { "Can't selectMismatchedDeviceOrSkipTestCase() multiple times" ); - this.mismatchedProvider = mismatchedDevicePool.acquire( - this.recorder, - initUncanonicalizedDeviceDescriptor(descriptor) - ); + this.mismatchedProvider = mismatchedDevicePool + .requestAdapter(this.recorder) + .then(adapter => + mismatchedDevicePool.acquire( + adapter, + conditionallyApplyAdapterLimitsToDeviceDescriptor( + adapter, + this.useAdapterLimits, + initUncanonicalizedDeviceDescriptor(descriptor) + ) + ) + ); // Suppress uncaught promise rejection (we'll catch it later). this.mismatchedProvider.catch(() => {}); } @@ -1203,6 +1263,20 @@ export class GPUTest extends GPUTestBase { } } +/** + * A version of GPUTest that requires the adapter limits. + */ +export class AdapterLimitsGPUTest extends GPUTest { + public static override MakeSharedState( + recorder: TestCaseRecorder, + params: TestParams + ): GPUTestSubcaseBatchState { + const state = new GPUTestSubcaseBatchState(recorder, params); + state.useAdapterLimitsForDevice(); + return state; + } +} + /** * Texture expectation mixin can be applied on top of GPUTest to add texture * related expectation helpers. diff --git a/src/webgpu/util/device_pool.ts b/src/webgpu/util/device_pool.ts index 4e45aac76bb8..d0d1e6c0b6b5 100644 --- a/src/webgpu/util/device_pool.ts +++ b/src/webgpu/util/device_pool.ts @@ -24,16 +24,27 @@ export class TestOOMedShouldAttemptGC extends Error {} export class DevicePool { private holders: 'uninitialized' | 'failed' | DescriptorToHolderMap = 'uninitialized'; + async requestAdapter(recorder: TestCaseRecorder) { + const gpu = getGPU(recorder); + const adapter = await gpu.requestAdapter(); + assert(adapter !== null, 'requestAdapter returned null'); + return adapter; + } + /** Acquire a device from the pool and begin the error scopes. */ async acquire( - recorder: TestCaseRecorder, + adapter: GPUAdapter, descriptor?: UncanonicalizedDeviceDescriptor ): Promise { - let errorMessage = ''; + let holder; if (this.holders === 'uninitialized') { this.holders = new DescriptorToHolderMap(); + } + + let errorMessage = ''; + if (this.holders !== 'failed') { try { - await this.holders.getOrCreate(recorder, undefined); + holder = await this.holders.getOrCreate(adapter, descriptor); } catch (ex) { this.holders = 'failed'; if (ex instanceof Error) { @@ -46,9 +57,7 @@ export class DevicePool { this.holders !== 'failed', `WebGPU device failed to initialize${errorMessage}; not retrying` ); - - const holder = await this.holders.getOrCreate(recorder, descriptor); - + assert(!!holder); assert(holder.state === 'free', 'Device was in use on DevicePool.acquire'); holder.state = 'acquired'; holder.beginTestScope(); @@ -138,7 +147,7 @@ class DescriptorToHolderMap { * Throws SkipTestCase if devices with this descriptor are unsupported. */ async getOrCreate( - recorder: TestCaseRecorder, + adapter: GPUAdapter, uncanonicalizedDescriptor: UncanonicalizedDeviceDescriptor | undefined ): Promise { const [descriptor, key] = canonicalizeDescriptor(uncanonicalizedDescriptor); @@ -163,7 +172,7 @@ class DescriptorToHolderMap { // No existing item was found; add a new one. let value; try { - value = await DeviceHolder.create(recorder, descriptor); + value = await DeviceHolder.create(adapter, descriptor); } catch (ex) { if (ex instanceof FeaturesNotSupported) { this.unsupported.add(key); @@ -298,15 +307,14 @@ class DeviceHolder implements DeviceProvider { // Gets a device and creates a DeviceHolder. // If the device is lost, DeviceHolder.lost gets set. static async create( - recorder: TestCaseRecorder, + adapter: GPUAdapter, descriptor: CanonicalDeviceDescriptor | undefined ): Promise { - const gpu = getGPU(recorder); - const adapter = await gpu.requestAdapter(); - assert(adapter !== null, 'requestAdapter returned null'); + assert(adapter !== null, 'requestAdapter is null'); if (!supportsFeature(adapter, descriptor)) { throw new FeaturesNotSupported('One or more features are not supported'); } + const device = await adapter.requestDevice(descriptor); assert(device !== null, 'requestDevice returned null'); From e6ef0b84a7120260d8c7e89a008d3e46856f5ea2 Mon Sep 17 00:00:00 2001 From: Alan Baker Date: Mon, 4 Mar 2024 14:34:42 -0500 Subject: [PATCH 05/33] Shader validation tests for atomics * type validation tests * address space * type * invalid operations * builtin function tests * parameterizations --- src/webgpu/listing_meta.json | 4 + .../expression/call/builtin/atomics.spec.ts | 145 ++++++++++++++++-- .../shader/validation/types/atomics.spec.ts | 145 ++++++++++++++++++ 3 files changed, 281 insertions(+), 13 deletions(-) create mode 100644 src/webgpu/shader/validation/types/atomics.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 065f3f8e9fda..2723d211ab7d 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1833,6 +1833,7 @@ "webgpu:shader,validation,expression,call,builtin,atan:values:*": { "subcaseMS": 0.335 }, "webgpu:shader,validation,expression,call,builtin,atanh:integer_argument:*": { "subcaseMS": 0.912 }, "webgpu:shader,validation,expression,call,builtin,atanh:values:*": { "subcaseMS": 0.231 }, + "webgpu:shader,validation,expression,call,builtin,atomics:atomic_parameterization:*": { "subcaseMS": 1.346 }, "webgpu:shader,validation,expression,call,builtin,atomics:stage:*": { "subcaseMS": 1.346 }, "webgpu:shader,validation,expression,call,builtin,barriers:no_return_value:*": { "subcaseMS": 1.500 }, "webgpu:shader,validation,expression,call,builtin,barriers:only_in_compute:*": { "subcaseMS": 1.500 }, @@ -2138,6 +2139,9 @@ "webgpu:shader,validation,types,alias:no_indirect_recursion_via_struct_attribute:*": { "subcaseMS": 1.584 }, "webgpu:shader,validation,types,alias:no_indirect_recursion_via_struct_member:*": { "subcaseMS": 1.000 }, "webgpu:shader,validation,types,alias:no_indirect_recursion_via_vector_element:*": { "subcaseMS": 1.050 }, + "webgpu:shader,validation,types,atomics:address_space:*": { "subcaseMS": 1.050 }, + "webgpu:shader,validation,types,atomics:invalid_operations:*": { "subcaseMS": 1.050 }, + "webgpu:shader,validation,types,atomics:type:*": { "subcaseMS": 1.050 }, "webgpu:shader,validation,types,struct:no_direct_recursion:*": { "subcaseMS": 0.951 }, "webgpu:shader,validation,types,struct:no_indirect_recursion:*": { "subcaseMS": 0.901 }, "webgpu:shader,validation,types,struct:no_indirect_recursion_via_array_element:*": { "subcaseMS": 0.901 }, diff --git a/src/webgpu/shader/validation/expression/call/builtin/atomics.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atomics.spec.ts index 57c5aae61334..788b044fcf63 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atomics.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atomics.spec.ts @@ -8,18 +8,44 @@ import { ShaderValidationTest } from '../../../shader_validation_test.js'; export const g = makeTestGroup(ShaderValidationTest); -const kAtomicOps = { - add: { src: 'atomicAdd(&a,1)' }, - sub: { src: 'atomicSub(&a,1)' }, - max: { src: 'atomicMax(&a,1)' }, - min: { src: 'atomicMin(&a,1)' }, - and: { src: 'atomicAnd(&a,1)' }, - or: { src: 'atomicOr(&a,1)' }, - xor: { src: 'atomicXor(&a,1)' }, - load: { src: 'atomicLoad(&a)' }, - store: { src: 'atomicStore(&a,1)' }, - exchange: { src: 'atomicExchange(&a,1)' }, - compareexchangeweak: { src: 'atomicCompareExchangeWeak(&a,1,1)' }, +interface stringToString { + (a: string): string; +} + +const kAtomicOps: Record = { + add: (a: string): string => { + return `atomicAdd(${a},1)`; + }, + sub: (a: string): string => { + return `atomicSub(${a},1)`; + }, + max: (a: string): string => { + return `atomicMax(${a},1)`; + }, + min: (a: string): string => { + return `atomicMin(${a},1)`; + }, + and: (a: string): string => { + return `atomicAnd(${a},1)`; + }, + or: (a: string): string => { + return `atomicOr(${a},1)`; + }, + xor: (a: string): string => { + return `atomicXor(${a},1)`; + }, + load: (a: string): string => { + return `atomicLoad(${a})`; + }, + store: (a: string): string => { + return `atomicStore(${a},1)`; + }, + exchange: (a: string): string => { + return `atomicExchange(${a},1)`; + }, + compareexchangeweak: (a: string): string => { + return `atomicCompareExchangeWeak(${a},1,1)`; + }, }; g.test('stage') @@ -35,7 +61,7 @@ Atomic built-in functions must not be used in a vertex shader stage. .combine('atomicOp', keysOf(kAtomicOps)) ) .fn(t => { - const atomicOp = kAtomicOps[t.params.atomicOp].src; + const atomicOp = kAtomicOps[t.params.atomicOp](`&a`); let code = ` @group(0) @binding(0) var a: atomic; `; @@ -68,3 +94,96 @@ Atomic built-in functions must not be used in a vertex shader stage. const pass = t.params.stage !== 'vertex'; t.expectCompileResult(pass, code); }); + +function generateAtomicCode( + type: string, + access: string, + aspace: string, + style: string, + op: string +): string { + let moduleVar = ``; + let functionVar = ``; + let param = ``; + let aParam = ``; + if (style === 'var') { + aParam = `&a`; + switch (aspace) { + case 'storage': + moduleVar = `@group(0) @binding(0) var a : atomic<${type}>;\n`; + break; + case 'workgroup': + moduleVar = `var a : atomic<${type}>;\n`; + break; + case 'uniform': + moduleVar = `@group(0) @binding(0) var a : atomic<${type}>;\n`; + break; + case 'private': + moduleVar = `var a : atomic<${type}>;\n`; + break; + case 'function': + functionVar = `var a : atomic<${type}>;\n`; + break; + default: + break; + } + } else { + const aspaceParam = aspace === 'storage' ? `, ${access}` : ``; + param = `p : ptr<${aspace}, atomic<${type}>${aspaceParam}>`; + aParam = `p`; + } + + return ` +${moduleVar} +fn foo(${param}) { + ${functionVar} + ${kAtomicOps[op](aParam)}; +} +`; +} + +g.test('atomic_parameterization') + .desc('Tests the valid atomic parameters') + .params(u => + u + .combine('op', keysOf(kAtomicOps)) + .beginSubcases() + .combine('aspace', ['storage', 'workgroup', 'private', 'uniform', 'function'] as const) + .combine('access', ['read', 'read_write'] as const) + .combine('type', ['i32', 'u32'] as const) + .combine('style', ['param', 'var'] as const) + .filter(t => { + switch (t.aspace) { + case 'uniform': + return t.style === 'param' && t.access === 'read'; + case 'workgroup': + return t.access === 'read_write'; + case 'function': + case 'private': + return t.style === 'param' && t.access === 'read_write'; + default: + return true; + } + }) + ) + .fn(t => { + if ( + t.params.style === 'param' && + !(t.params.aspace === 'function' || t.params.aspace === 'private') + ) { + t.skipIfLanguageFeatureNotSupported('unrestricted_pointer_parameters'); + } + + const aspaceOK = t.params.aspace === 'storage' || t.params.aspace === 'workgroup'; + const accessOK = t.params.access === 'read_write'; + t.expectCompileResult( + aspaceOK && accessOK, + generateAtomicCode( + t.params.type, + t.params.access, + t.params.aspace, + t.params.style, + t.params.op + ) + ); + }); diff --git a/src/webgpu/shader/validation/types/atomics.spec.ts b/src/webgpu/shader/validation/types/atomics.spec.ts new file mode 100644 index 000000000000..36c37176e8af --- /dev/null +++ b/src/webgpu/shader/validation/types/atomics.spec.ts @@ -0,0 +1,145 @@ +export const description = ` +Validation tests for atomic types + +Tests covered: +* Base type +* Address spaces +* Invalid operations (non-exhaustive) + +Note: valid operations (e.g. atomic built-in functions) are tested in the builtin tests. +`; + +import { makeTestGroup } from '../../../../common/framework/test_group.js'; +import { keysOf } from '../../../../common/util/data_tables.js'; +import { ShaderValidationTest } from '../shader_validation_test.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +g.test('type') + .desc('Test of the underlying atomic data type') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#atomic-types') + .params(u => + u.combine('type', [ + 'u32', + 'i32', + 'f32', + 'f16', + 'bool', + 'vec2u', + 'vec3i', + 'vec4f', + 'mat2x2f', + 'R', + 'S', + 'array', + 'array', + 'array', + 'array', + 'atomic', + 'atomic', + ] as const) + ) + .beforeAllSubcases(t => { + if (t.params.type === 'f16') { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(t => { + const code = ` +struct S { + x : u32 +} +struct T { + x : i32 +} +struct R { + x : f32 +} + +struct Test { + x : atomic<${t.params.type}> +} +`; + + const expect = t.params.type === 'u32' || t.params.type === 'i32'; + t.expectCompileResult(expect, code); + }); + +g.test('address_space') + .desc('Test allowed address spaces for atomics') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#atomic-types') + .params(u => + u + .combine('aspace', [ + 'storage', + 'workgroup', + 'storage-ro', + 'uniform', + 'private', + 'function', + 'function-let', + ] as const) + .beginSubcases() + .combine('type', ['i32', 'u32'] as const) + ) + .fn(t => { + let moduleVar = ``; + let functionVar = ''; + switch (t.params.aspace) { + case 'storage-ro': + moduleVar = `@group(0) @binding(0) var x : atomic<${t.params.type}>;\n`; + break; + case 'storage': + moduleVar = `@group(0) @binding(0) var x : atomic<${t.params.type}>;\n`; + break; + case 'uniform': + moduleVar = `@group(0) @binding(0) var x : atomic<${t.params.type}>;\n`; + break; + case 'workgroup': + case 'private': + moduleVar = `var<${t.params.aspace}> x : atomic<${t.params.type}>;\n`; + break; + case 'function': + functionVar = `var x : atomic<${t.params.type}>;\n`; + break; + case 'function-let': + functionVar = `let x : atomic<${t.params.type}>;\n`; + break; + } + const code = ` +${moduleVar} + +fn foo() { + ${functionVar} +} +`; + + const expect = t.params.aspace === 'storage' || t.params.aspace === 'workgroup'; + t.expectCompileResult(expect, code); + }); + +const kInvalidOperations = { + add: `a1 + a2`, + load: `a1`, + store: `a1 = 1u`, + deref: `*a1 = 1u`, + equality: `a1 == a2`, + abs: `abs(a1)`, + address_abs: `abs(&a1)`, +}; + +g.test('invalid_operations') + .desc('Tests that a selection of invalid operations are invalid') + .params(u => u.combine('op', keysOf(kInvalidOperations))) + .fn(t => { + const code = ` +var a1 : atomic; +var a2 : atomic; + +fn foo() { + let x : u32 = ${kInvalidOperations[t.params.op]}; +} +`; + + t.expectCompileResult(false, code); + }); From 1d5dd491559566e0e8c6fa9bf3c1c8495c6b8cb8 Mon Sep 17 00:00:00 2001 From: Alan Baker Date: Tue, 5 Mar 2024 10:04:01 -0500 Subject: [PATCH 06/33] More atomic validation * Data type validation * Return type validation --- src/webgpu/listing_meta.json | 2 + .../expression/call/builtin/atomics.spec.ts | 90 +++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 2723d211ab7d..327ba644120d 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1834,6 +1834,8 @@ "webgpu:shader,validation,expression,call,builtin,atanh:integer_argument:*": { "subcaseMS": 0.912 }, "webgpu:shader,validation,expression,call,builtin,atanh:values:*": { "subcaseMS": 0.231 }, "webgpu:shader,validation,expression,call,builtin,atomics:atomic_parameterization:*": { "subcaseMS": 1.346 }, + "webgpu:shader,validation,expression,call,builtin,atomics:data_parameters:*": { "subcaseMS": 38.382 }, + "webgpu:shader,validation,expression,call,builtin,atomics:return_types:*": { "subcaseMS": 28.021 }, "webgpu:shader,validation,expression,call,builtin,atomics:stage:*": { "subcaseMS": 1.346 }, "webgpu:shader,validation,expression,call,builtin,barriers:no_return_value:*": { "subcaseMS": 1.500 }, "webgpu:shader,validation,expression,call,builtin,barriers:only_in_compute:*": { "subcaseMS": 1.500 }, diff --git a/src/webgpu/shader/validation/expression/call/builtin/atomics.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atomics.spec.ts index 788b044fcf63..fdb85664d254 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atomics.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atomics.spec.ts @@ -187,3 +187,93 @@ g.test('atomic_parameterization') ) ); }); + +g.test('data_parameters') + .desc('Validates that data parameters must match atomic type (or be implicitly convertible)') + .params(u => + u + .combine('op', [ + 'atomicStore', + 'atomicAdd', + 'atomicSub', + 'atomicMax', + 'atomicMin', + 'atomicAnd', + 'atomicOr', + 'atomicXor', + 'atomicExchange', + 'atomicCompareExchangeWeak1', + 'atomicCompareExchangeWeak2', + ] as const) + .beginSubcases() + .combine('atomicType', ['i32', 'u32'] as const) + .combine('dataType', ['i32', 'u32', 'f32', 'AbstractInt'] as const) + ) + .fn(t => { + let dataValue = ''; + switch (t.params.dataType) { + case 'i32': + dataValue = '1i'; + break; + case 'u32': + dataValue = '1u'; + break; + case 'f32': + dataValue = '1f'; + break; + case 'AbstractInt': + dataValue = '1'; + break; + } + let op = ''; + switch (t.params.op) { + case 'atomicCompareExchangeWeak1': + op = `atomicCompareExchangeWeak(&a, ${dataValue}, 1)`; + break; + case 'atomicCompareExchangeWeak2': + op = `atomicCompareExchangeWeak(&a, 1, ${dataValue})`; + break; + default: + op = `${t.params.op}(&a, ${dataValue})`; + break; + } + const code = ` +var a : atomic<${t.params.atomicType}>; +fn foo() { + ${op}; +} +`; + + const expect = t.params.atomicType === t.params.dataType || t.params.dataType === 'AbstractInt'; + t.expectCompileResult(expect, code); + }); + +g.test('return_types') + .desc('Validates return types of atomics') + .params(u => + u + .combine('op', keysOf(kAtomicOps)) + .beginSubcases() + .combine('atomicType', ['i32', 'u32'] as const) + .combine('returnType', ['i32', 'u32', 'f32'] as const) + ) + .fn(t => { + let op = `${kAtomicOps[t.params.op]('&a')}`; + switch (t.params.op) { + case 'compareexchangeweak': + op = `let tmp : ${t.params.returnType} = ${op}.old_value`; + break; + default: + op = `let tmp : ${t.params.returnType} = ${op}`; + break; + } + const code = ` +var a : atomic<${t.params.atomicType}>; +fn foo() { + ${op}; +} +`; + + const expect = t.params.atomicType === t.params.returnType && t.params.op !== 'store'; + t.expectCompileResult(expect, code); + }); From 8a03f77ef28366f60a8a01f0e6adf1de5519023f Mon Sep 17 00:00:00 2001 From: James Price Date: Fri, 8 Mar 2024 16:17:36 -0500 Subject: [PATCH 07/33] Rename kAllScalarsAndVectors to kAllNumericScalarsAndVectors (#3470) Add a new kAllScalarsAndVectors that includes bool types. --- src/resources/cache/hashes.json | 216 +++++++++--------- .../expression/call/builtin/abs.spec.ts | 8 +- src/webgpu/util/conversion.ts | 13 +- 3 files changed, 125 insertions(+), 112 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 6c9518de081e..bf2eca5789b8 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "25dcfcce", - "webgpu/shader/execution/binary/af_logical.bin": "409d37cf", - "webgpu/shader/execution/binary/af_division.bin": "bfcb23bd", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "cefea21a", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "624d816d", - "webgpu/shader/execution/binary/af_multiplication.bin": "34077746", - "webgpu/shader/execution/binary/af_remainder.bin": "9eddf765", - "webgpu/shader/execution/binary/af_subtraction.bin": "629bbf4", - "webgpu/shader/execution/binary/f16_addition.bin": "e3ff1263", - "webgpu/shader/execution/binary/f16_logical.bin": "99c0a0ed", - "webgpu/shader/execution/binary/f16_division.bin": "c0f3376", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "3054016e", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "9cb6bbb9", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "82f32c1d", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "29e9e0c7", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "109260c1", - "webgpu/shader/execution/binary/f16_multiplication.bin": "39287c38", - "webgpu/shader/execution/binary/f16_remainder.bin": "98b48517", - "webgpu/shader/execution/binary/f16_subtraction.bin": "a4975dc4", - "webgpu/shader/execution/binary/f32_addition.bin": "b0e6fddd", - "webgpu/shader/execution/binary/f32_logical.bin": "71a0f0be", - "webgpu/shader/execution/binary/f32_division.bin": "29a37cf9", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "76e48aa9", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "37f3a30b", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "f00c4cd8", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "25553482", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "26750bd3", - "webgpu/shader/execution/binary/f32_multiplication.bin": "68a7516b", - "webgpu/shader/execution/binary/f32_remainder.bin": "fb22b625", - "webgpu/shader/execution/binary/f32_subtraction.bin": "2e26b05f", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "9256ccab", - "webgpu/shader/execution/binary/i32_comparison.bin": "c813abfa", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "498f877e", - "webgpu/shader/execution/binary/u32_comparison.bin": "d69e1738", - "webgpu/shader/execution/abs.bin": "a6b6fed1", - "webgpu/shader/execution/acos.bin": "2a8f33b0", - "webgpu/shader/execution/acosh.bin": "3b39f532", - "webgpu/shader/execution/asin.bin": "94c67af3", - "webgpu/shader/execution/asinh.bin": "34c8547a", - "webgpu/shader/execution/atan.bin": "c5f98dae", - "webgpu/shader/execution/atan2.bin": "5c666ada", - "webgpu/shader/execution/atanh.bin": "5efbba8b", - "webgpu/shader/execution/bitcast.bin": "e90abd26", - "webgpu/shader/execution/ceil.bin": "fe43b603", - "webgpu/shader/execution/clamp.bin": "f4ec869a", - "webgpu/shader/execution/cos.bin": "78f30673", - "webgpu/shader/execution/cosh.bin": "15c18b89", - "webgpu/shader/execution/cross.bin": "72823897", - "webgpu/shader/execution/degrees.bin": "3b06fea4", - "webgpu/shader/execution/determinant.bin": "f0f66549", - "webgpu/shader/execution/distance.bin": "593c41fb", - "webgpu/shader/execution/dot.bin": "3eee146b", - "webgpu/shader/execution/exp.bin": "7f926769", - "webgpu/shader/execution/exp2.bin": "715c82a", - "webgpu/shader/execution/faceForward.bin": "8a365384", - "webgpu/shader/execution/floor.bin": "7b9a6254", - "webgpu/shader/execution/fma.bin": "88549fc7", - "webgpu/shader/execution/fract.bin": "e4eff2f9", - "webgpu/shader/execution/frexp.bin": "18a53421", - "webgpu/shader/execution/inverseSqrt.bin": "71016a37", - "webgpu/shader/execution/ldexp.bin": "450c6068", - "webgpu/shader/execution/length.bin": "62190368", - "webgpu/shader/execution/log.bin": "c4b1985b", - "webgpu/shader/execution/log2.bin": "28ccd982", - "webgpu/shader/execution/max.bin": "ba573f18", - "webgpu/shader/execution/min.bin": "5a834580", - "webgpu/shader/execution/mix.bin": "bf942d40", - "webgpu/shader/execution/modf.bin": "a8de3f36", - "webgpu/shader/execution/normalize.bin": "4bf84190", - "webgpu/shader/execution/pack2x16float.bin": "cc42ef45", - "webgpu/shader/execution/pow.bin": "6e8d08d1", - "webgpu/shader/execution/quantizeToF16.bin": "8c615e93", - "webgpu/shader/execution/radians.bin": "624ff571", - "webgpu/shader/execution/reflect.bin": "d0d624ae", - "webgpu/shader/execution/refract.bin": "3dd68359", - "webgpu/shader/execution/round.bin": "502c36a8", - "webgpu/shader/execution/saturate.bin": "be4cf88f", - "webgpu/shader/execution/sign.bin": "91310555", - "webgpu/shader/execution/sin.bin": "441d6be3", - "webgpu/shader/execution/sinh.bin": "4ebaa2e7", - "webgpu/shader/execution/smoothstep.bin": "6298e644", - "webgpu/shader/execution/sqrt.bin": "3cf64df1", - "webgpu/shader/execution/step.bin": "90148c99", - "webgpu/shader/execution/tan.bin": "e753749c", - "webgpu/shader/execution/tanh.bin": "62c99641", - "webgpu/shader/execution/transpose.bin": "2a4448c6", - "webgpu/shader/execution/trunc.bin": "ed9a0d22", - "webgpu/shader/execution/unpack2x16float.bin": "e81297f7", - "webgpu/shader/execution/unpack2x16snorm.bin": "513f9a8b", - "webgpu/shader/execution/unpack2x16unorm.bin": "8b56d0ce", - "webgpu/shader/execution/unpack4x8snorm.bin": "51af8a63", - "webgpu/shader/execution/unpack4x8unorm.bin": "302cf4a6", - "webgpu/shader/execution/unary/af_arithmetic.bin": "cd4618d", - "webgpu/shader/execution/unary/af_assignment.bin": "7d5de0f1", - "webgpu/shader/execution/unary/bool_conversion.bin": "401fb8c5", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "ad86d013", - "webgpu/shader/execution/unary/f16_conversion.bin": "86e35a85", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "fe97afd1", - "webgpu/shader/execution/unary/f32_conversion.bin": "97730c3c", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "69f783bb", - "webgpu/shader/execution/unary/i32_conversion.bin": "4fec061e", - "webgpu/shader/execution/unary/u32_conversion.bin": "1a298dea", - "webgpu/shader/execution/unary/ai_assignment.bin": "1c2edca2", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "657f2fb7", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "216769d9", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "544f4363", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "14212e77", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "57f19c15" + "webgpu/shader/execution/binary/af_addition.bin": "590ce5d0", + "webgpu/shader/execution/binary/af_logical.bin": "4269127d", + "webgpu/shader/execution/binary/af_division.bin": "d4ff5475", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "9593eb85", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "aa460371", + "webgpu/shader/execution/binary/af_multiplication.bin": "5eafc30c", + "webgpu/shader/execution/binary/af_remainder.bin": "75cfdd3", + "webgpu/shader/execution/binary/af_subtraction.bin": "5acd5252", + "webgpu/shader/execution/binary/f16_addition.bin": "b3b843b9", + "webgpu/shader/execution/binary/f16_logical.bin": "36a51091", + "webgpu/shader/execution/binary/f16_division.bin": "67fc610", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "d7b16cc0", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "aae9227", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "d148fe2c", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "7fdc67ab", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "6c6f0373", + "webgpu/shader/execution/binary/f16_multiplication.bin": "d4cba123", + "webgpu/shader/execution/binary/f16_remainder.bin": "39b2f3d1", + "webgpu/shader/execution/binary/f16_subtraction.bin": "eca69567", + "webgpu/shader/execution/binary/f32_addition.bin": "bc6520f4", + "webgpu/shader/execution/binary/f32_logical.bin": "e1d07173", + "webgpu/shader/execution/binary/f32_division.bin": "b9b4bace", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "c1ff4f67", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "12ce1afc", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "728004ae", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "7203414c", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "60bd62d5", + "webgpu/shader/execution/binary/f32_multiplication.bin": "763a40d3", + "webgpu/shader/execution/binary/f32_remainder.bin": "a14fb18d", + "webgpu/shader/execution/binary/f32_subtraction.bin": "8bf897d8", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "936d712", + "webgpu/shader/execution/binary/i32_comparison.bin": "5db8f3b0", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "415ac05e", + "webgpu/shader/execution/binary/u32_comparison.bin": "f90d60f3", + "webgpu/shader/execution/abs.bin": "ab026917", + "webgpu/shader/execution/acos.bin": "ebec4fe2", + "webgpu/shader/execution/acosh.bin": "538a0f85", + "webgpu/shader/execution/asin.bin": "8f34001e", + "webgpu/shader/execution/asinh.bin": "f6474f7e", + "webgpu/shader/execution/atan.bin": "9f57357c", + "webgpu/shader/execution/atan2.bin": "c3c9ab97", + "webgpu/shader/execution/atanh.bin": "b8c67df8", + "webgpu/shader/execution/bitcast.bin": "54c7c36a", + "webgpu/shader/execution/ceil.bin": "b60fd415", + "webgpu/shader/execution/clamp.bin": "ae2291cc", + "webgpu/shader/execution/cos.bin": "c1c8d701", + "webgpu/shader/execution/cosh.bin": "cce8eabb", + "webgpu/shader/execution/cross.bin": "385fe441", + "webgpu/shader/execution/degrees.bin": "f0fb9a6d", + "webgpu/shader/execution/determinant.bin": "7a7750f3", + "webgpu/shader/execution/distance.bin": "939b3544", + "webgpu/shader/execution/dot.bin": "f1fc309e", + "webgpu/shader/execution/exp.bin": "d49bb232", + "webgpu/shader/execution/exp2.bin": "88b69575", + "webgpu/shader/execution/faceForward.bin": "f5b0b400", + "webgpu/shader/execution/floor.bin": "41edc029", + "webgpu/shader/execution/fma.bin": "2e5b7503", + "webgpu/shader/execution/fract.bin": "dd5251b2", + "webgpu/shader/execution/frexp.bin": "43ded4af", + "webgpu/shader/execution/inverseSqrt.bin": "f6f44cf2", + "webgpu/shader/execution/ldexp.bin": "3ea94e77", + "webgpu/shader/execution/length.bin": "2296407f", + "webgpu/shader/execution/log.bin": "1d09cc4", + "webgpu/shader/execution/log2.bin": "e000def9", + "webgpu/shader/execution/max.bin": "18ccab7a", + "webgpu/shader/execution/min.bin": "ab321644", + "webgpu/shader/execution/mix.bin": "5563295f", + "webgpu/shader/execution/modf.bin": "774fe500", + "webgpu/shader/execution/normalize.bin": "35783a23", + "webgpu/shader/execution/pack2x16float.bin": "54f11627", + "webgpu/shader/execution/pow.bin": "7421e698", + "webgpu/shader/execution/quantizeToF16.bin": "7f112f83", + "webgpu/shader/execution/radians.bin": "98a95e1b", + "webgpu/shader/execution/reflect.bin": "3f180462", + "webgpu/shader/execution/refract.bin": "b08230a3", + "webgpu/shader/execution/round.bin": "476b7078", + "webgpu/shader/execution/saturate.bin": "d2b2a9bc", + "webgpu/shader/execution/sign.bin": "b70cde44", + "webgpu/shader/execution/sin.bin": "26beb35a", + "webgpu/shader/execution/sinh.bin": "7c561ea2", + "webgpu/shader/execution/smoothstep.bin": "c889c68a", + "webgpu/shader/execution/sqrt.bin": "dbda0613", + "webgpu/shader/execution/step.bin": "7c108793", + "webgpu/shader/execution/tan.bin": "879dd531", + "webgpu/shader/execution/tanh.bin": "7e1d66a6", + "webgpu/shader/execution/transpose.bin": "762ed60", + "webgpu/shader/execution/trunc.bin": "8c46cbe8", + "webgpu/shader/execution/unpack2x16float.bin": "5bd42344", + "webgpu/shader/execution/unpack2x16snorm.bin": "478e7fa5", + "webgpu/shader/execution/unpack2x16unorm.bin": "a2f9b4f5", + "webgpu/shader/execution/unpack4x8snorm.bin": "93d90295", + "webgpu/shader/execution/unpack4x8unorm.bin": "31999cab", + "webgpu/shader/execution/unary/af_arithmetic.bin": "446e85d9", + "webgpu/shader/execution/unary/af_assignment.bin": "9911dc5f", + "webgpu/shader/execution/unary/bool_conversion.bin": "bcab7d9a", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "b0a84279", + "webgpu/shader/execution/unary/f16_conversion.bin": "4bbd348e", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "a98d962e", + "webgpu/shader/execution/unary/f32_conversion.bin": "9af02ab5", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "8f3c4616", + "webgpu/shader/execution/unary/i32_conversion.bin": "972063c", + "webgpu/shader/execution/unary/u32_conversion.bin": "168fcf74", + "webgpu/shader/execution/unary/ai_assignment.bin": "fc978bdd", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "f89aeb4", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "497ec1e1", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "7fed8020", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "48e954b2", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "209e2c19" } \ No newline at end of file diff --git a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts index 3fc9eeb22c3d..2b0375d7835f 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts @@ -5,7 +5,11 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; -import { Type, kAllScalarsAndVectors, scalarTypeOf } from '../../../../../util/conversion.js'; +import { + Type, + kAllNumericScalarsAndVectors, + scalarTypeOf, +} from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; import { @@ -17,7 +21,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord(kAllScalarsAndVectors); +const kValuesTypes = objectsToRecord(kAllNumericScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index a44f34cedfeb..b28c1c9e822c 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -2303,8 +2303,17 @@ export const kConvertableToFloatScalarsAndVectors = [ ...kFloatScalarsAndVectors, ] as const; -/// All the scalar and vector types -export const kAllScalarsAndVectors = [ +/// All the numeric scalar and vector types. +export const kAllNumericScalarsAndVectors = [ ...kConvertableToFloatScalarsAndVectors, ...kConcreteIntegerScalarsAndVectors, ] as const; + +/// All the scalar and vector types. +export const kAllScalarsAndVectors = [ + Type.bool, + Type.vec(2, Type.bool), + Type.vec(3, Type.bool), + Type.vec(4, Type.bool), + ...kAllNumericScalarsAndVectors, +] as const; From f71a8343c3b4ea23eddf0cf35b9957a0b45b0cf3 Mon Sep 17 00:00:00 2001 From: Greggman Date: Fri, 8 Mar 2024 13:56:24 -0800 Subject: [PATCH 08/33] Fix device_pool refactor (#3469) The old version made one device with no descriptor. If that fail then it stopped creating any devices period. The previous refactor was bad in that if the any device failed it would just stop making devices. This one is simplified. It just used the pool as a pool. There is no special handling for failed devices. --- src/webgpu/util/device_pool.ts | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/webgpu/util/device_pool.ts b/src/webgpu/util/device_pool.ts index d0d1e6c0b6b5..8f734c75aec7 100644 --- a/src/webgpu/util/device_pool.ts +++ b/src/webgpu/util/device_pool.ts @@ -22,7 +22,7 @@ class FeaturesNotSupported extends Error {} export class TestOOMedShouldAttemptGC extends Error {} export class DevicePool { - private holders: 'uninitialized' | 'failed' | DescriptorToHolderMap = 'uninitialized'; + private holders = new DescriptorToHolderMap(); async requestAdapter(recorder: TestCaseRecorder) { const gpu = getGPU(recorder); @@ -36,28 +36,7 @@ export class DevicePool { adapter: GPUAdapter, descriptor?: UncanonicalizedDeviceDescriptor ): Promise { - let holder; - if (this.holders === 'uninitialized') { - this.holders = new DescriptorToHolderMap(); - } - - let errorMessage = ''; - if (this.holders !== 'failed') { - try { - holder = await this.holders.getOrCreate(adapter, descriptor); - } catch (ex) { - this.holders = 'failed'; - if (ex instanceof Error) { - errorMessage = ` with ${ex.name} "${ex.message}"`; - } - } - } - - assert( - this.holders !== 'failed', - `WebGPU device failed to initialize${errorMessage}; not retrying` - ); - assert(!!holder); + const holder = await this.holders.getOrCreate(adapter, descriptor); assert(holder.state === 'free', 'Device was in use on DevicePool.acquire'); holder.state = 'acquired'; holder.beginTestScope(); From 11d3770f4e51cae95ba5542e50ed8dd7187ea193 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Mon, 11 Mar 2024 11:11:14 -0400 Subject: [PATCH 09/33] wgsl: Use selectNCases to restrict the number of AF Matrix cases (#3465) Fixes #3457 --- src/resources/cache/hashes.json | 18 +++++++++--------- .../execution/binary/af_matrix_addition.bin | Bin 914552 -> 631152 bytes .../binary/af_matrix_subtraction.bin | Bin 921752 -> 644432 bytes .../binary/af_matrix_addition.cache.ts | 15 ++++++++++----- .../binary/af_matrix_subtraction.cache.ts | 15 ++++++++++----- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index bf2eca5789b8..16bf07892d15 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -2,11 +2,15 @@ "webgpu/shader/execution/binary/af_addition.bin": "590ce5d0", "webgpu/shader/execution/binary/af_logical.bin": "4269127d", "webgpu/shader/execution/binary/af_division.bin": "d4ff5475", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "9593eb85", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "aa460371", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "298ef48e", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "7fed8020", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "48e954b2", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "6d4ead98", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "209e2c19", "webgpu/shader/execution/binary/af_multiplication.bin": "5eafc30c", "webgpu/shader/execution/binary/af_remainder.bin": "75cfdd3", "webgpu/shader/execution/binary/af_subtraction.bin": "5acd5252", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "f89aeb4", "webgpu/shader/execution/binary/f16_addition.bin": "b3b843b9", "webgpu/shader/execution/binary/f16_logical.bin": "36a51091", "webgpu/shader/execution/binary/f16_division.bin": "67fc610", @@ -93,6 +97,8 @@ "webgpu/shader/execution/unpack4x8unorm.bin": "31999cab", "webgpu/shader/execution/unary/af_arithmetic.bin": "446e85d9", "webgpu/shader/execution/unary/af_assignment.bin": "9911dc5f", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "497ec1e1", + "webgpu/shader/execution/unary/ai_assignment.bin": "fc978bdd", "webgpu/shader/execution/unary/bool_conversion.bin": "bcab7d9a", "webgpu/shader/execution/unary/f16_arithmetic.bin": "b0a84279", "webgpu/shader/execution/unary/f16_conversion.bin": "4bbd348e", @@ -100,11 +106,5 @@ "webgpu/shader/execution/unary/f32_conversion.bin": "9af02ab5", "webgpu/shader/execution/unary/i32_arithmetic.bin": "8f3c4616", "webgpu/shader/execution/unary/i32_conversion.bin": "972063c", - "webgpu/shader/execution/unary/u32_conversion.bin": "168fcf74", - "webgpu/shader/execution/unary/ai_assignment.bin": "fc978bdd", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "f89aeb4", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "497ec1e1", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "7fed8020", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "48e954b2", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "209e2c19" + "webgpu/shader/execution/unary/u32_conversion.bin": "168fcf74" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_matrix_addition.bin b/src/resources/cache/webgpu/shader/execution/binary/af_matrix_addition.bin index d387abcd7f208df8c11ad5470307a737ffe8b81d..634f518e0d1262f906cbb4cdcc9792969fe60583 100644 GIT binary patch delta 8101 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 delta 33288 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 A2mk;8 diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_addition.cache.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.cache.ts index 75c13d7702b6..e89250f57bd4 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_addition.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.cache.ts @@ -1,5 +1,6 @@ import { FP } from '../../../../util/floating_point.js'; import { sparseMatrixF64Range } from '../../../../util/math.js'; +import { selectNCases } from '../case.js'; import { makeCaseCache } from '../case_cache.js'; // Cases: matCxR @@ -7,11 +8,15 @@ const mat_cases = ([2, 3, 4] as const) .flatMap(cols => ([2, 3, 4] as const).map(rows => ({ [`mat${cols}x${rows}`]: () => { - return FP.abstract.generateMatrixPairToMatrixCases( - sparseMatrixF64Range(cols, rows), - sparseMatrixF64Range(cols, rows), - 'finite', - FP.abstract.additionMatrixMatrixInterval + return selectNCases( + 'binary/af_matrix_addition', + 50, + FP.abstract.generateMatrixPairToMatrixCases( + sparseMatrixF64Range(cols, rows), + sparseMatrixF64Range(cols, rows), + 'finite', + FP.abstract.additionMatrixMatrixInterval + ) ); }, })) diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.cache.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.cache.ts index b030f369e3d3..c3e5e856dc26 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.cache.ts @@ -1,5 +1,6 @@ import { FP } from '../../../../util/floating_point.js'; import { sparseMatrixF64Range } from '../../../../util/math.js'; +import { selectNCases } from '../case.js'; import { makeCaseCache } from '../case_cache.js'; // Cases: matCxR @@ -7,11 +8,15 @@ const mat_cases = ([2, 3, 4] as const) .flatMap(cols => ([2, 3, 4] as const).map(rows => ({ [`mat${cols}x${rows}`]: () => { - return FP.abstract.generateMatrixPairToMatrixCases( - sparseMatrixF64Range(cols, rows), - sparseMatrixF64Range(cols, rows), - 'finite', - FP.abstract.subtractionMatrixMatrixInterval + return selectNCases( + 'binary/af_matrix_subtraction', + 50, + FP.abstract.generateMatrixPairToMatrixCases( + sparseMatrixF64Range(cols, rows), + sparseMatrixF64Range(cols, rows), + 'finite', + FP.abstract.subtractionMatrixMatrixInterval + ) ); }, })) From d5ce9402de2658fab0777900affbd3cfc2ecde4f Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Mon, 11 Mar 2024 11:30:57 -0400 Subject: [PATCH 10/33] wgsl: Implement AbstractInt bitwise-and execution tests (#3436) Issue #1631 --- .../expression/binary/bitwise.spec.ts | 158 +++++++++++++----- 1 file changed, 119 insertions(+), 39 deletions(-) diff --git a/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts b/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts index 71744b45959b..b6e9a45e9de8 100644 --- a/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts @@ -7,11 +7,9 @@ import { assert } from '../../../../../common/util/util.js'; import { GPUTest } from '../../../../gpu_test.js'; import { abstractIntBits, - i32, i32Bits, ScalarValue, scalarType, - u32, u32Bits, } from '../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../expression.js'; @@ -221,59 +219,137 @@ Bitwise-or. Component-wise when T is a vector. await run(t, compoundBinary('|='), [type, type], type, t.params, cases); }); -function makeBitwiseAndCases(inputType: string) { - const V = inputType === 'i32' ? i32 : u32; - const cases = [ - // Static patterns +/** Manually calculated bitwise-and cases used a check that the CTS test is correct */ +const kBitwiseAndStaticPatterns = { + 32: [ { - input: [V(0b00000000000000000000000000000000), V(0b00000000000000000000000000000000)], - expected: V(0b00000000000000000000000000000000), + input: [0b00000000000000000000000000000000, 0b00000000000000000000000000000000], + expected: 0b00000000000000000000000000000000, }, { - input: [V(0b11111111111111111111111111111111), V(0b00000000000000000000000000000000)], - expected: V(0b00000000000000000000000000000000), + input: [0b11111111111111111111111111111111, 0b00000000000000000000000000000000], + expected: 0b00000000000000000000000000000000, }, { - input: [V(0b00000000000000000000000000000000), V(0b11111111111111111111111111111111)], - expected: V(0b00000000000000000000000000000000), + input: [0b00000000000000000000000000000000, 0b11111111111111111111111111111111], + expected: 0b00000000000000000000000000000000, }, { - input: [V(0b11111111111111111111111111111111), V(0b11111111111111111111111111111111)], - expected: V(0b11111111111111111111111111111111), + input: [0b11111111111111111111111111111111, 0b11111111111111111111111111111111], + expected: 0b11111111111111111111111111111111, }, { - input: [V(0b10100100010010100100010010100100), V(0b00000000000000000000000000000000)], - expected: V(0b00000000000000000000000000000000), + input: [0b10100100010010100100010010100100, 0b00000000000000000000000000000000], + expected: 0b00000000000000000000000000000000, }, { - input: [V(0b10100100010010100100010010100100), V(0b11111111111111111111111111111111)], - expected: V(0b10100100010010100100010010100100), + input: [0b10100100010010100100010010100100, 0b11111111111111111111111111111111], + expected: 0b10100100010010100100010010100100, }, { - input: [V(0b00000000000000000000000000000000), V(0b10100100010010100100010010100100)], - expected: V(0b00000000000000000000000000000000), + input: [0b00000000000000000000000000000000, 0b10100100010010100100010010100100], + expected: 0b00000000000000000000000000000000, }, { - input: [V(0b11111111111111111111111111111111), V(0b10100100010010100100010010100100)], - expected: V(0b10100100010010100100010010100100), + input: [0b11111111111111111111111111111111, 0b10100100010010100100010010100100], + expected: 0b10100100010010100100010010100100, }, { - input: [V(0b01010010001001010010001001010010), V(0b01011011101101011011101101011011)], - expected: V(0b01010010001001010010001001010010), + input: [0b01010010001001010010001001010010, 0b01011011101101011011101101011011], + expected: 0b01010010001001010010001001010010, }, - ]; - // Permute all combinations of a single bit being set for the LHS and all but one bit set for the RHS - for (let i = 0; i < 32; i++) { - const lhs = 1 << i; - for (let j = 0; j < 32; j++) { - const rhs = 0xffffffff ^ (1 << j); - cases.push({ - input: [V(lhs), V(rhs)], - expected: V(lhs & rhs), + ], + 64: [ + { + input: [ + 0b0000000000000000000000000000000000000000000000000000000000000000n, + 0b0000000000000000000000000000000000000000000000000000000000000000n, + ], + expected: 0b0000000000000000000000000000000000000000000000000000000000000000n, + }, + { + input: [ + 0b1111111111111111111111111111111111111111111111111111111111111111n, + 0b0000000000000000000000000000000000000000000000000000000000000000n, + ], + expected: 0b0000000000000000000000000000000000000000000000000000000000000000n, + }, + { + input: [ + 0b0000000000000000000000000000000000000000000000000000000000000000n, + 0b1111111111111111111111111111111111111111111111111111111111111111n, + ], + expected: 0b0000000000000000000000000000000000000000000000000000000000000000n, + }, + { + input: [ + 0b1111111111111111111111111111111111111111111111111111111111111111n, + 0b1111111111111111111111111111111111111111111111111111111111111111n, + ], + expected: 0b1111111111111111111111111111111111111111111111111111111111111111n, + }, + { + input: [ + 0b1010010001001010010001001010010010100100010010100100010010100100n, + 0b0000000000000000000000000000000000000000000000000000000000000000n, + ], + expected: 0b0000000000000000000000000000000000000000000000000000000000000000n, + }, + { + input: [ + 0b1010010001001010010001001010010010100100010010100100010010100100n, + 0b1111111111111111111111111111111111111111111111111111111111111111n, + ], + expected: 0b1010010001001010010001001010010010100100010010100100010010100100n, + }, + { + input: [ + 0b0000000000000000000000000000000000000000000000000000000000000000n, + 0b1010010001001010010001001010010010100100010010100100010010100100n, + ], + expected: 0b0000000000000000000000000000000000000000000000000000000000000000n, + }, + { + input: [ + 0b1111111111111111111111111111111111111111111111111111111111111111n, + 0b1010010001001010010001001010010010100100010010100100010010100100n, + ], + expected: 0b1010010001001010010001001010010010100100010010100100010010100100n, + }, + { + input: [ + 0b0101001000100101001000100101001001010010001001010010001001010010n, + 0b0101101110110101101110110101101101011011101101011011101101011011n, + ], + expected: 0b0101001000100101001000100101001001010010001001010010001001010010n, + }, + ], +}; + +/** @returns a set of bitwise-or cases for the specific input type */ +function makeBitwiseAndCases(inputType: string) { + const impl = scalarImplForInputType(inputType); + const indices = + impl.size === 64 ? [...Array(impl.size).keys()].map(BigInt) : [...Array(impl.size).keys()]; + + return [ + ...kBitwiseAndStaticPatterns[impl.size].map(c => { + return { + input: c.input.map(impl.builder), + expected: impl.builder(c.expected), + }; + }), + // Permute all combinations of a single bit being set for the LHS and all but one bit set for the RHS + ...indices.flatMap(i => { + const lhs = typeof i === 'bigint' ? 1n << i : 1 << i; + return indices.map(j => { + const rhs = typeof j === 'bigint' ? 0xffffffffffffffffn ^ (1n << j) : 0xffffffff ^ (1 << j); + assert(typeof lhs === typeof rhs); + const result = typeof lhs === 'bigint' ? lhs & (rhs as bigint) : lhs & (rhs as number); + return { input: [impl.builder(lhs), impl.builder(rhs)], expected: impl.builder(result) }; }); - } - } - return cases; + }), + ]; } g.test('bitwise_and') @@ -281,21 +357,25 @@ g.test('bitwise_and') .desc( ` e1 & e2: T -T is i32, u32, vecN, or vecN +T is i32, u32, AbstractInt, vecN, vecN, or vecN Bitwise-and. Component-wise when T is a vector. ` ) .params(u => u - .combine('type', ['i32', 'u32'] as const) + .combine('type', ['i32', 'u32', 'abstract-int'] as const) .combine('inputSource', allInputSources) .combine('vectorize', [undefined, 2, 3, 4] as const) ) .fn(async t => { + t.skipIf( + t.params.type === 'abstract-int' && !onlyConstInputSource.includes(t.params.inputSource) + ); const type = scalarType(t.params.type); const cases = makeBitwiseAndCases(t.params.type); - await run(t, binary('&'), [type, type], type, t.params, cases); + const builder = t.params.type === 'abstract-int' ? abstractIntBinary('&') : binary('&'); + await run(t, builder, [type, type], type, t.params, cases); }); g.test('bitwise_and_compound') From e91b44ae5efdc29978a26730acfbb919fe8e6759 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Mon, 11 Mar 2024 11:44:50 -0400 Subject: [PATCH 11/33] wgsl: Implement AbstractFloat `ceil` execution tests (#3449) Issue #1297 --- src/resources/cache/hashes.json | 186 +++++++++--------- .../cache/webgpu/shader/execution/ceil.bin | Bin 8552 -> 16016 bytes src/unittests/floating_point.spec.ts | 9 +- .../expression/call/builtin/ceil.cache.ts | 3 +- .../expression/call/builtin/ceil.spec.ts | 20 +- src/webgpu/util/floating_point.ts | 2 +- 6 files changed, 119 insertions(+), 101 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 16bf07892d15..69cb91f857c4 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,109 +1,109 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "590ce5d0", + "webgpu/shader/execution/binary/af_addition.bin": "cc90f705", "webgpu/shader/execution/binary/af_logical.bin": "4269127d", - "webgpu/shader/execution/binary/af_division.bin": "d4ff5475", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "298ef48e", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "7fed8020", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "48e954b2", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "6d4ead98", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "209e2c19", - "webgpu/shader/execution/binary/af_multiplication.bin": "5eafc30c", - "webgpu/shader/execution/binary/af_remainder.bin": "75cfdd3", - "webgpu/shader/execution/binary/af_subtraction.bin": "5acd5252", + "webgpu/shader/execution/binary/af_division.bin": "303c23c9", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "bf3f432a", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "f262ebd9", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "ac0e04e3", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "ac5cd859", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "44a39136", + "webgpu/shader/execution/binary/af_multiplication.bin": "4fa27383", + "webgpu/shader/execution/binary/af_remainder.bin": "4fa868c6", + "webgpu/shader/execution/binary/af_subtraction.bin": "8d5c9b21", "webgpu/shader/execution/binary/ai_arithmetic.bin": "f89aeb4", - "webgpu/shader/execution/binary/f16_addition.bin": "b3b843b9", + "webgpu/shader/execution/binary/f16_addition.bin": "f2313284", "webgpu/shader/execution/binary/f16_logical.bin": "36a51091", - "webgpu/shader/execution/binary/f16_division.bin": "67fc610", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "d7b16cc0", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "aae9227", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "d148fe2c", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "7fdc67ab", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "6c6f0373", - "webgpu/shader/execution/binary/f16_multiplication.bin": "d4cba123", - "webgpu/shader/execution/binary/f16_remainder.bin": "39b2f3d1", - "webgpu/shader/execution/binary/f16_subtraction.bin": "eca69567", - "webgpu/shader/execution/binary/f32_addition.bin": "bc6520f4", + "webgpu/shader/execution/binary/f16_division.bin": "6d276852", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "7a8dd641", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "352fbb8f", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "7f315242", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "6e623bf7", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "bc0e0051", + "webgpu/shader/execution/binary/f16_multiplication.bin": "5ddc6615", + "webgpu/shader/execution/binary/f16_remainder.bin": "a7d11cc7", + "webgpu/shader/execution/binary/f16_subtraction.bin": "9a16a043", + "webgpu/shader/execution/binary/f32_addition.bin": "c5b2f15e", "webgpu/shader/execution/binary/f32_logical.bin": "e1d07173", - "webgpu/shader/execution/binary/f32_division.bin": "b9b4bace", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "c1ff4f67", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "12ce1afc", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "728004ae", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "7203414c", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "60bd62d5", - "webgpu/shader/execution/binary/f32_multiplication.bin": "763a40d3", - "webgpu/shader/execution/binary/f32_remainder.bin": "a14fb18d", - "webgpu/shader/execution/binary/f32_subtraction.bin": "8bf897d8", + "webgpu/shader/execution/binary/f32_division.bin": "583afe1", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "d568c885", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "ddcd0f48", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "99892730", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "3ea99dec", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "d5235751", + "webgpu/shader/execution/binary/f32_multiplication.bin": "a465f647", + "webgpu/shader/execution/binary/f32_remainder.bin": "8402dd76", + "webgpu/shader/execution/binary/f32_subtraction.bin": "63832192", "webgpu/shader/execution/binary/i32_arithmetic.bin": "936d712", "webgpu/shader/execution/binary/i32_comparison.bin": "5db8f3b0", "webgpu/shader/execution/binary/u32_arithmetic.bin": "415ac05e", "webgpu/shader/execution/binary/u32_comparison.bin": "f90d60f3", - "webgpu/shader/execution/abs.bin": "ab026917", - "webgpu/shader/execution/acos.bin": "ebec4fe2", - "webgpu/shader/execution/acosh.bin": "538a0f85", - "webgpu/shader/execution/asin.bin": "8f34001e", - "webgpu/shader/execution/asinh.bin": "f6474f7e", - "webgpu/shader/execution/atan.bin": "9f57357c", - "webgpu/shader/execution/atan2.bin": "c3c9ab97", - "webgpu/shader/execution/atanh.bin": "b8c67df8", - "webgpu/shader/execution/bitcast.bin": "54c7c36a", - "webgpu/shader/execution/ceil.bin": "b60fd415", - "webgpu/shader/execution/clamp.bin": "ae2291cc", - "webgpu/shader/execution/cos.bin": "c1c8d701", - "webgpu/shader/execution/cosh.bin": "cce8eabb", - "webgpu/shader/execution/cross.bin": "385fe441", - "webgpu/shader/execution/degrees.bin": "f0fb9a6d", - "webgpu/shader/execution/determinant.bin": "7a7750f3", - "webgpu/shader/execution/distance.bin": "939b3544", - "webgpu/shader/execution/dot.bin": "f1fc309e", - "webgpu/shader/execution/exp.bin": "d49bb232", - "webgpu/shader/execution/exp2.bin": "88b69575", - "webgpu/shader/execution/faceForward.bin": "f5b0b400", - "webgpu/shader/execution/floor.bin": "41edc029", - "webgpu/shader/execution/fma.bin": "2e5b7503", - "webgpu/shader/execution/fract.bin": "dd5251b2", - "webgpu/shader/execution/frexp.bin": "43ded4af", - "webgpu/shader/execution/inverseSqrt.bin": "f6f44cf2", - "webgpu/shader/execution/ldexp.bin": "3ea94e77", - "webgpu/shader/execution/length.bin": "2296407f", - "webgpu/shader/execution/log.bin": "1d09cc4", - "webgpu/shader/execution/log2.bin": "e000def9", - "webgpu/shader/execution/max.bin": "18ccab7a", - "webgpu/shader/execution/min.bin": "ab321644", - "webgpu/shader/execution/mix.bin": "5563295f", - "webgpu/shader/execution/modf.bin": "774fe500", - "webgpu/shader/execution/normalize.bin": "35783a23", + "webgpu/shader/execution/abs.bin": "5f333", + "webgpu/shader/execution/acos.bin": "2abc764c", + "webgpu/shader/execution/acosh.bin": "accd2622", + "webgpu/shader/execution/asin.bin": "514f90a0", + "webgpu/shader/execution/asinh.bin": "55b36d3a", + "webgpu/shader/execution/atan.bin": "1e5c011e", + "webgpu/shader/execution/atan2.bin": "51bc4048", + "webgpu/shader/execution/atanh.bin": "3c86dc17", + "webgpu/shader/execution/bitcast.bin": "e10e2bf5", + "webgpu/shader/execution/ceil.bin": "9e9f3783", + "webgpu/shader/execution/clamp.bin": "f160e302", + "webgpu/shader/execution/cos.bin": "f0047dd0", + "webgpu/shader/execution/cosh.bin": "58d620e5", + "webgpu/shader/execution/cross.bin": "f5c1fd91", + "webgpu/shader/execution/degrees.bin": "fbbc1fa3", + "webgpu/shader/execution/determinant.bin": "35335634", + "webgpu/shader/execution/distance.bin": "c0783351", + "webgpu/shader/execution/dot.bin": "fe3b0bf4", + "webgpu/shader/execution/exp.bin": "10e9c894", + "webgpu/shader/execution/exp2.bin": "6fef9b0b", + "webgpu/shader/execution/faceForward.bin": "596738cf", + "webgpu/shader/execution/floor.bin": "d2efe779", + "webgpu/shader/execution/fma.bin": "25db025e", + "webgpu/shader/execution/fract.bin": "8d9072e", + "webgpu/shader/execution/frexp.bin": "49afbf7f", + "webgpu/shader/execution/inverseSqrt.bin": "5bffd293", + "webgpu/shader/execution/ldexp.bin": "d0947f57", + "webgpu/shader/execution/length.bin": "3d4adcaa", + "webgpu/shader/execution/log.bin": "703cca75", + "webgpu/shader/execution/log2.bin": "7fa8bb66", + "webgpu/shader/execution/max.bin": "a9f6689f", + "webgpu/shader/execution/min.bin": "51966c50", + "webgpu/shader/execution/mix.bin": "b9f407dc", + "webgpu/shader/execution/modf.bin": "1e9c1919", + "webgpu/shader/execution/normalize.bin": "a3c6ac4", "webgpu/shader/execution/pack2x16float.bin": "54f11627", - "webgpu/shader/execution/pow.bin": "7421e698", - "webgpu/shader/execution/quantizeToF16.bin": "7f112f83", - "webgpu/shader/execution/radians.bin": "98a95e1b", - "webgpu/shader/execution/reflect.bin": "3f180462", - "webgpu/shader/execution/refract.bin": "b08230a3", - "webgpu/shader/execution/round.bin": "476b7078", - "webgpu/shader/execution/saturate.bin": "d2b2a9bc", - "webgpu/shader/execution/sign.bin": "b70cde44", - "webgpu/shader/execution/sin.bin": "26beb35a", - "webgpu/shader/execution/sinh.bin": "7c561ea2", - "webgpu/shader/execution/smoothstep.bin": "c889c68a", - "webgpu/shader/execution/sqrt.bin": "dbda0613", - "webgpu/shader/execution/step.bin": "7c108793", - "webgpu/shader/execution/tan.bin": "879dd531", - "webgpu/shader/execution/tanh.bin": "7e1d66a6", - "webgpu/shader/execution/transpose.bin": "762ed60", - "webgpu/shader/execution/trunc.bin": "8c46cbe8", - "webgpu/shader/execution/unpack2x16float.bin": "5bd42344", - "webgpu/shader/execution/unpack2x16snorm.bin": "478e7fa5", - "webgpu/shader/execution/unpack2x16unorm.bin": "a2f9b4f5", - "webgpu/shader/execution/unpack4x8snorm.bin": "93d90295", - "webgpu/shader/execution/unpack4x8unorm.bin": "31999cab", - "webgpu/shader/execution/unary/af_arithmetic.bin": "446e85d9", - "webgpu/shader/execution/unary/af_assignment.bin": "9911dc5f", + "webgpu/shader/execution/pow.bin": "87e8391c", + "webgpu/shader/execution/quantizeToF16.bin": "c5be64e5", + "webgpu/shader/execution/radians.bin": "5bd5a987", + "webgpu/shader/execution/reflect.bin": "e6ba9df9", + "webgpu/shader/execution/refract.bin": "7f1a1889", + "webgpu/shader/execution/round.bin": "f3376643", + "webgpu/shader/execution/saturate.bin": "2a8d20df", + "webgpu/shader/execution/sign.bin": "e7232801", + "webgpu/shader/execution/sin.bin": "8acb35", + "webgpu/shader/execution/sinh.bin": "f356d922", + "webgpu/shader/execution/smoothstep.bin": "24299f69", + "webgpu/shader/execution/sqrt.bin": "724c8512", + "webgpu/shader/execution/step.bin": "4fe42940", + "webgpu/shader/execution/tan.bin": "3366acbb", + "webgpu/shader/execution/tanh.bin": "b4f80ddb", + "webgpu/shader/execution/transpose.bin": "9a65abfa", + "webgpu/shader/execution/trunc.bin": "9292ec12", + "webgpu/shader/execution/unpack2x16float.bin": "81649bef", + "webgpu/shader/execution/unpack2x16snorm.bin": "fbbaa36f", + "webgpu/shader/execution/unpack2x16unorm.bin": "37f1917", + "webgpu/shader/execution/unpack4x8snorm.bin": "46b063cd", + "webgpu/shader/execution/unpack4x8unorm.bin": "ffb80fc5", + "webgpu/shader/execution/unary/af_arithmetic.bin": "8f9a3824", + "webgpu/shader/execution/unary/af_assignment.bin": "d8fab2f2", "webgpu/shader/execution/unary/ai_arithmetic.bin": "497ec1e1", "webgpu/shader/execution/unary/ai_assignment.bin": "fc978bdd", "webgpu/shader/execution/unary/bool_conversion.bin": "bcab7d9a", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "b0a84279", - "webgpu/shader/execution/unary/f16_conversion.bin": "4bbd348e", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "a98d962e", - "webgpu/shader/execution/unary/f32_conversion.bin": "9af02ab5", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "13caa810", + "webgpu/shader/execution/unary/f16_conversion.bin": "3699afe4", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "9236999", + "webgpu/shader/execution/unary/f32_conversion.bin": "416b0d0", "webgpu/shader/execution/unary/i32_arithmetic.bin": "8f3c4616", "webgpu/shader/execution/unary/i32_conversion.bin": "972063c", "webgpu/shader/execution/unary/u32_conversion.bin": "168fcf74" diff --git a/src/resources/cache/webgpu/shader/execution/ceil.bin b/src/resources/cache/webgpu/shader/execution/ceil.bin index 5ec60b6e150d5694fda5f5933f6accd39d76bf2d..7ff8dea27eee16d130ecd1b08b03666896b79702 100644 GIT binary patch literal 16016 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 delta 15 VcmbPG`@)HlX(MB@A`1f$001Z?19boZ diff --git a/src/unittests/floating_point.spec.ts b/src/unittests/floating_point.spec.ts index ad27c1d711d8..7f4388c2eaa9 100644 --- a/src/unittests/floating_point.spec.ts +++ b/src/unittests/floating_point.spec.ts @@ -2657,12 +2657,17 @@ const kCeilIntervalCases = { { input: -(2 ** 14), expected: -(2 ** 14) }, { input: 0x8000, expected: 0x8000 }, // https://github.com/gpuweb/cts/issues/2766 ], + abstract: [ + { input: 2 ** 52, expected: 2 ** 52 }, + { input: -(2 ** 52), expected: -(2 ** 52) }, + { input: 0x8000000000000000, expected: 0x8000000000000000 }, // https://github.com/gpuweb/cts/issues/2766 + ], } as const; g.test('ceilInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .expandWithParams(p => { const constants = FP[p.trait].constants(); @@ -2689,7 +2694,7 @@ g.test('ceilInterval') { input: constants.negative.max, expected: 0 }, ...kCeilIntervalCases[p.trait], - // 32-bit subnormals + // Subnormals { input: constants.positive.subnormal.max, expected: [0, 1] }, { input: constants.positive.subnormal.min, expected: [0, 1] }, { input: constants.negative.subnormal.min, expected: 0 }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts index 3141d1d1a16f..c0178d9b83ad 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts @@ -7,10 +7,11 @@ const kSmallMagnitudeTestValues = [0.1, 0.9, 1.0, 1.1, 1.9, -0.1, -0.9, -1.0, -1 const kIssue2766Value = { f32: 0x8000_0000, f16: 0x8000, + abstract: 0x8000_0000_0000_0000, }; // Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) +const cases = (['f32', 'f16', 'abstract'] as const) .map(trait => ({ [`${trait}`]: () => { return FP[trait].generateScalarToIntervalCases( diff --git a/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts index 5f6236149e53..842875f0940f 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts @@ -11,9 +11,9 @@ 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 { Type } from '../../../../../util/conversion.js'; -import { allInputSources, run } from '../../expression.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); @@ -22,9 +22,21 @@ 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') diff --git a/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index 9015eca362eb..2c850695cef1 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -5120,7 +5120,7 @@ class FPAbstractTraits extends FPTraits { 'atan2Interval' ); public readonly atanhInterval = this.unimplementedScalarToInterval.bind(this, 'atanhInterval'); - public readonly ceilInterval = this.unimplementedScalarToInterval.bind(this, 'ceilInterval'); + public readonly ceilInterval = this.ceilIntervalImpl.bind(this); public readonly clampMedianInterval = this.clampMedianIntervalImpl.bind(this); public readonly clampMinMaxInterval = this.clampMinMaxIntervalImpl.bind(this); public readonly clampIntervals = [this.clampMedianInterval, this.clampMinMaxInterval]; From f4bc7d3eb59789446daacfbfc618d90cc58798a5 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Mon, 11 Mar 2024 11:56:48 -0400 Subject: [PATCH 12/33] wgsl: Enable Absolute Error intervals for AbstractFloat (#3460) --- src/resources/cache/hashes.json | 186 +++++++++++++-------------- src/unittests/floating_point.spec.ts | 43 ++++--- src/webgpu/util/floating_point.ts | 2 +- 3 files changed, 121 insertions(+), 110 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 69cb91f857c4..d178aba9e161 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,109 +1,109 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "cc90f705", + "webgpu/shader/execution/binary/af_addition.bin": "360aab58", "webgpu/shader/execution/binary/af_logical.bin": "4269127d", - "webgpu/shader/execution/binary/af_division.bin": "303c23c9", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "bf3f432a", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "f262ebd9", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "ac0e04e3", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "ac5cd859", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "44a39136", - "webgpu/shader/execution/binary/af_multiplication.bin": "4fa27383", - "webgpu/shader/execution/binary/af_remainder.bin": "4fa868c6", - "webgpu/shader/execution/binary/af_subtraction.bin": "8d5c9b21", + "webgpu/shader/execution/binary/af_division.bin": "2a1ae6a9", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "135913e7", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "83a7b6ab", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "5959bc9f", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "9c125148", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "efbe60ca", + "webgpu/shader/execution/binary/af_multiplication.bin": "923ed423", + "webgpu/shader/execution/binary/af_remainder.bin": "c17bb87f", + "webgpu/shader/execution/binary/af_subtraction.bin": "6457d271", "webgpu/shader/execution/binary/ai_arithmetic.bin": "f89aeb4", - "webgpu/shader/execution/binary/f16_addition.bin": "f2313284", + "webgpu/shader/execution/binary/f16_addition.bin": "85c17d72", "webgpu/shader/execution/binary/f16_logical.bin": "36a51091", - "webgpu/shader/execution/binary/f16_division.bin": "6d276852", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "7a8dd641", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "352fbb8f", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "7f315242", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "6e623bf7", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "bc0e0051", - "webgpu/shader/execution/binary/f16_multiplication.bin": "5ddc6615", - "webgpu/shader/execution/binary/f16_remainder.bin": "a7d11cc7", - "webgpu/shader/execution/binary/f16_subtraction.bin": "9a16a043", - "webgpu/shader/execution/binary/f32_addition.bin": "c5b2f15e", + "webgpu/shader/execution/binary/f16_division.bin": "7c38f4b7", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "9e6258d7", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "916ca7e6", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "a4f59720", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "5e94030b", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "45b64fd9", + "webgpu/shader/execution/binary/f16_multiplication.bin": "ee7ba392", + "webgpu/shader/execution/binary/f16_remainder.bin": "ea8f78e", + "webgpu/shader/execution/binary/f16_subtraction.bin": "f5e0d152", + "webgpu/shader/execution/binary/f32_addition.bin": "d82339c6", "webgpu/shader/execution/binary/f32_logical.bin": "e1d07173", - "webgpu/shader/execution/binary/f32_division.bin": "583afe1", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "d568c885", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "ddcd0f48", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "99892730", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "3ea99dec", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "d5235751", - "webgpu/shader/execution/binary/f32_multiplication.bin": "a465f647", - "webgpu/shader/execution/binary/f32_remainder.bin": "8402dd76", - "webgpu/shader/execution/binary/f32_subtraction.bin": "63832192", + "webgpu/shader/execution/binary/f32_division.bin": "46721765", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "6fdf77f1", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "73520069", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "5ea144ba", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "5f5213d2", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "2ff78c70", + "webgpu/shader/execution/binary/f32_multiplication.bin": "3eb2748f", + "webgpu/shader/execution/binary/f32_remainder.bin": "a6728c3e", + "webgpu/shader/execution/binary/f32_subtraction.bin": "6d3120c0", "webgpu/shader/execution/binary/i32_arithmetic.bin": "936d712", "webgpu/shader/execution/binary/i32_comparison.bin": "5db8f3b0", "webgpu/shader/execution/binary/u32_arithmetic.bin": "415ac05e", "webgpu/shader/execution/binary/u32_comparison.bin": "f90d60f3", - "webgpu/shader/execution/abs.bin": "5f333", - "webgpu/shader/execution/acos.bin": "2abc764c", - "webgpu/shader/execution/acosh.bin": "accd2622", - "webgpu/shader/execution/asin.bin": "514f90a0", - "webgpu/shader/execution/asinh.bin": "55b36d3a", - "webgpu/shader/execution/atan.bin": "1e5c011e", - "webgpu/shader/execution/atan2.bin": "51bc4048", - "webgpu/shader/execution/atanh.bin": "3c86dc17", - "webgpu/shader/execution/bitcast.bin": "e10e2bf5", - "webgpu/shader/execution/ceil.bin": "9e9f3783", - "webgpu/shader/execution/clamp.bin": "f160e302", - "webgpu/shader/execution/cos.bin": "f0047dd0", - "webgpu/shader/execution/cosh.bin": "58d620e5", - "webgpu/shader/execution/cross.bin": "f5c1fd91", - "webgpu/shader/execution/degrees.bin": "fbbc1fa3", - "webgpu/shader/execution/determinant.bin": "35335634", - "webgpu/shader/execution/distance.bin": "c0783351", - "webgpu/shader/execution/dot.bin": "fe3b0bf4", - "webgpu/shader/execution/exp.bin": "10e9c894", - "webgpu/shader/execution/exp2.bin": "6fef9b0b", - "webgpu/shader/execution/faceForward.bin": "596738cf", - "webgpu/shader/execution/floor.bin": "d2efe779", - "webgpu/shader/execution/fma.bin": "25db025e", - "webgpu/shader/execution/fract.bin": "8d9072e", - "webgpu/shader/execution/frexp.bin": "49afbf7f", - "webgpu/shader/execution/inverseSqrt.bin": "5bffd293", - "webgpu/shader/execution/ldexp.bin": "d0947f57", - "webgpu/shader/execution/length.bin": "3d4adcaa", - "webgpu/shader/execution/log.bin": "703cca75", - "webgpu/shader/execution/log2.bin": "7fa8bb66", - "webgpu/shader/execution/max.bin": "a9f6689f", - "webgpu/shader/execution/min.bin": "51966c50", - "webgpu/shader/execution/mix.bin": "b9f407dc", - "webgpu/shader/execution/modf.bin": "1e9c1919", - "webgpu/shader/execution/normalize.bin": "a3c6ac4", + "webgpu/shader/execution/abs.bin": "3bcfb49e", + "webgpu/shader/execution/acos.bin": "86824420", + "webgpu/shader/execution/acosh.bin": "95f9a9a4", + "webgpu/shader/execution/asin.bin": "1d017518", + "webgpu/shader/execution/asinh.bin": "56df3876", + "webgpu/shader/execution/atan.bin": "6aaa6b50", + "webgpu/shader/execution/atan2.bin": "82bcc1a2", + "webgpu/shader/execution/atanh.bin": "36a1f68a", + "webgpu/shader/execution/bitcast.bin": "6b7ea46b", + "webgpu/shader/execution/ceil.bin": "7308c54f", + "webgpu/shader/execution/clamp.bin": "9bfd2fce", + "webgpu/shader/execution/cos.bin": "2fa872d3", + "webgpu/shader/execution/cosh.bin": "2e6538aa", + "webgpu/shader/execution/cross.bin": "71a7e274", + "webgpu/shader/execution/degrees.bin": "31d8728f", + "webgpu/shader/execution/determinant.bin": "399a64cb", + "webgpu/shader/execution/distance.bin": "4282ab43", + "webgpu/shader/execution/dot.bin": "b4940f90", + "webgpu/shader/execution/exp.bin": "3801f256", + "webgpu/shader/execution/exp2.bin": "fb531b99", + "webgpu/shader/execution/faceForward.bin": "ac35ad68", + "webgpu/shader/execution/floor.bin": "177dc3a6", + "webgpu/shader/execution/fma.bin": "27a3e818", + "webgpu/shader/execution/fract.bin": "8ed3cd4b", + "webgpu/shader/execution/frexp.bin": "b6d69258", + "webgpu/shader/execution/inverseSqrt.bin": "b08786b3", + "webgpu/shader/execution/ldexp.bin": "6b8bcdb3", + "webgpu/shader/execution/length.bin": "3dc4b222", + "webgpu/shader/execution/log.bin": "9219d5e4", + "webgpu/shader/execution/log2.bin": "d5ca64af", + "webgpu/shader/execution/max.bin": "9051c302", + "webgpu/shader/execution/min.bin": "e950d75f", + "webgpu/shader/execution/mix.bin": "3a720d24", + "webgpu/shader/execution/modf.bin": "ef00e883", + "webgpu/shader/execution/normalize.bin": "52231704", "webgpu/shader/execution/pack2x16float.bin": "54f11627", - "webgpu/shader/execution/pow.bin": "87e8391c", - "webgpu/shader/execution/quantizeToF16.bin": "c5be64e5", - "webgpu/shader/execution/radians.bin": "5bd5a987", - "webgpu/shader/execution/reflect.bin": "e6ba9df9", - "webgpu/shader/execution/refract.bin": "7f1a1889", - "webgpu/shader/execution/round.bin": "f3376643", - "webgpu/shader/execution/saturate.bin": "2a8d20df", - "webgpu/shader/execution/sign.bin": "e7232801", - "webgpu/shader/execution/sin.bin": "8acb35", - "webgpu/shader/execution/sinh.bin": "f356d922", - "webgpu/shader/execution/smoothstep.bin": "24299f69", - "webgpu/shader/execution/sqrt.bin": "724c8512", - "webgpu/shader/execution/step.bin": "4fe42940", - "webgpu/shader/execution/tan.bin": "3366acbb", - "webgpu/shader/execution/tanh.bin": "b4f80ddb", - "webgpu/shader/execution/transpose.bin": "9a65abfa", - "webgpu/shader/execution/trunc.bin": "9292ec12", - "webgpu/shader/execution/unpack2x16float.bin": "81649bef", - "webgpu/shader/execution/unpack2x16snorm.bin": "fbbaa36f", - "webgpu/shader/execution/unpack2x16unorm.bin": "37f1917", - "webgpu/shader/execution/unpack4x8snorm.bin": "46b063cd", - "webgpu/shader/execution/unpack4x8unorm.bin": "ffb80fc5", - "webgpu/shader/execution/unary/af_arithmetic.bin": "8f9a3824", - "webgpu/shader/execution/unary/af_assignment.bin": "d8fab2f2", + "webgpu/shader/execution/pow.bin": "9fdb1669", + "webgpu/shader/execution/quantizeToF16.bin": "e980118b", + "webgpu/shader/execution/radians.bin": "a676aec1", + "webgpu/shader/execution/reflect.bin": "a951c771", + "webgpu/shader/execution/refract.bin": "d8953376", + "webgpu/shader/execution/round.bin": "eee2fc9", + "webgpu/shader/execution/saturate.bin": "161df54c", + "webgpu/shader/execution/sign.bin": "9d9a22ff", + "webgpu/shader/execution/sin.bin": "29f7032a", + "webgpu/shader/execution/sinh.bin": "9d282f12", + "webgpu/shader/execution/smoothstep.bin": "f4230ffb", + "webgpu/shader/execution/sqrt.bin": "1dba9696", + "webgpu/shader/execution/step.bin": "2048f771", + "webgpu/shader/execution/tan.bin": "adc5217b", + "webgpu/shader/execution/tanh.bin": "20f7d479", + "webgpu/shader/execution/transpose.bin": "727afd91", + "webgpu/shader/execution/trunc.bin": "bc14dd25", + "webgpu/shader/execution/unpack2x16float.bin": "b8428e59", + "webgpu/shader/execution/unpack2x16snorm.bin": "e38f4982", + "webgpu/shader/execution/unpack2x16unorm.bin": "c7cdd947", + "webgpu/shader/execution/unpack4x8snorm.bin": "71e176b1", + "webgpu/shader/execution/unpack4x8unorm.bin": "5e61d6ea", + "webgpu/shader/execution/unary/af_arithmetic.bin": "29bc881a", + "webgpu/shader/execution/unary/af_assignment.bin": "33c25997", "webgpu/shader/execution/unary/ai_arithmetic.bin": "497ec1e1", "webgpu/shader/execution/unary/ai_assignment.bin": "fc978bdd", "webgpu/shader/execution/unary/bool_conversion.bin": "bcab7d9a", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "13caa810", - "webgpu/shader/execution/unary/f16_conversion.bin": "3699afe4", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "9236999", - "webgpu/shader/execution/unary/f32_conversion.bin": "416b0d0", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "925ed6d", + "webgpu/shader/execution/unary/f16_conversion.bin": "751ff969", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "b26d9187", + "webgpu/shader/execution/unary/f32_conversion.bin": "482ee20f", "webgpu/shader/execution/unary/i32_arithmetic.bin": "8f3c4616", "webgpu/shader/execution/unary/i32_conversion.bin": "972063c", "webgpu/shader/execution/unary/u32_conversion.bin": "168fcf74" diff --git a/src/unittests/floating_point.spec.ts b/src/unittests/floating_point.spec.ts index 7f4388c2eaa9..64f3bec47d08 100644 --- a/src/unittests/floating_point.spec.ts +++ b/src/unittests/floating_point.spec.ts @@ -1836,24 +1836,27 @@ interface AbsoluteErrorCase { const kSmallAbsoluteErrorValue = { f32: 2 ** -11, // Builtin cos and sin has a absolute error 2**-11 for f32 f16: 2 ** -7, // Builtin cos and sin has a absolute error 2**-7 for f16 + abstract: 2 ** -11, // Builtin cos and sin has a absolute error 2**-11 for AbstractFloat } as const; // A large absolute error value is a representable value x that much smaller than maximum // positive, but positive.max - x is still exactly representable. const kLargeAbsoluteErrorValue = { f32: 2 ** 110, // f32.positive.max - 2**110 = 3.4028104e+38 = 0x7f7fffbf in f32 f16: 2 ** 10, // f16.positive.max - 2**10 = 64480 = 0x7bdf in f16 + abstract: 2 ** 977, // f64.positive.man - 2**977 = 1.79769e+308 = 0x7fefffffffffffbf in f64 } as const; // A subnormal absolute error value is a subnormal representable value x of kind, which ensures // that positive.subnormal.min +/- x is still exactly representable. const kSubnormalAbsoluteErrorValue = { f32: 2 ** -140, // f32 0x00000200 f16: 2 ** -20, // f16 0x0010 + abstract: 2 ** -1065, // f64 0x0000_0000_0000_0200 } as const; g.test('absoluteErrorInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .expandWithParams(p => { const trait = FP[p.trait]; @@ -1861,6 +1864,27 @@ g.test('absoluteErrorInterval') const smallErr = kSmallAbsoluteErrorValue[p.trait]; const largeErr = kLargeAbsoluteErrorValue[p.trait]; const subnormalErr = kSubnormalAbsoluteErrorValue[p.trait]; + + // Additional testing for non-f64 values, since JS number is f64 internally + // prettier-ignore + const additionalSubnormal64bit = p.trait !== 'abstract' ? + [ + // 64-bit subnormals, expected to be treated as 0.0 or smallest subnormal of kind. + { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: 0, expected: [0, constants.positive.subnormal.min] }, + { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: subnormalErr, expected: [-subnormalErr, constants.positive.subnormal.min + subnormalErr] }, + // Note that f32 minimum subnormal is so smaller than 1.0, adding them together may result in the f64 results 1.0. + { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: 1, expected: [-1, constants.positive.subnormal.min + 1] }, + { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: 0, expected: [0, constants.positive.subnormal.min] }, + { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: subnormalErr, expected: [-subnormalErr, constants.positive.subnormal.min + subnormalErr] }, + { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: 1, expected: [-1, constants.positive.subnormal.min + 1] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: 0, expected: [constants.negative.subnormal.max, 0] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: subnormalErr, expected: [constants.negative.subnormal.max - subnormalErr, subnormalErr] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 0, expected: [constants.negative.subnormal.max, 0] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: subnormalErr, expected: [constants.negative.subnormal.max - subnormalErr, subnormalErr] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, + ] as const : [] as const; + // prettier-ignore return [ // Edge Cases @@ -1903,21 +1927,6 @@ g.test('absoluteErrorInterval') { value: constants.negative.subnormal.max, error: smallErr, expected: [constants.negative.subnormal.max - smallErr, smallErr] }, { value: constants.negative.subnormal.max, error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, - // 64-bit subnormals, expected to be treated as 0.0 or smallest subnormal of kind. - { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: 0, expected: [0, constants.positive.subnormal.min] }, - { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: subnormalErr, expected: [-subnormalErr, constants.positive.subnormal.min + subnormalErr] }, - // Note that f32 minimum subnormal is so smaller than 1.0, adding them together may result in the f64 results 1.0. - { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: 1, expected: [-1, constants.positive.subnormal.min + 1] }, - { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: 0, expected: [0, constants.positive.subnormal.min] }, - { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: subnormalErr, expected: [-subnormalErr, constants.positive.subnormal.min + subnormalErr] }, - { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: 1, expected: [-1, constants.positive.subnormal.min + 1] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: 0, expected: [constants.negative.subnormal.max, 0] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: subnormalErr, expected: [constants.negative.subnormal.max - subnormalErr, subnormalErr] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 0, expected: [constants.negative.subnormal.max, 0] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: subnormalErr, expected: [constants.negative.subnormal.max - subnormalErr, subnormalErr] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, - // Zero { value: 0, error: 0, expected: 0 }, { value: 0, error: smallErr, expected: [-smallErr, smallErr] }, @@ -1930,6 +1939,8 @@ g.test('absoluteErrorInterval') { value: -2, error: 0, expected: -2 }, { value: -2, error: smallErr, expected: [-2 - smallErr, -2 + smallErr] }, { value: -2, error: 1, expected: [-3, -1] }, + + ...additionalSubnormal64bit, ]; }) ) diff --git a/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index 2c850695cef1..9b6fbeac39bd 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -5091,7 +5091,7 @@ class FPAbstractTraits extends FPTraits { public readonly sparseMatrixRange = sparseMatrixF64Range; // Framework - Fundamental Error Intervals - Overrides - public readonly absoluteErrorInterval = this.unboundedAbsoluteErrorInterval.bind(this); + public readonly absoluteErrorInterval = this.absoluteErrorIntervalImpl.bind(this); public readonly correctlyRoundedInterval = this.correctlyRoundedIntervalImpl.bind(this); public readonly correctlyRoundedMatrix = this.correctlyRoundedMatrixImpl.bind(this); public readonly ulpInterval = (n: number, numULP: number): FPInterval => { From f55affd6fb2b8e0f7c3c1c1312aa867f934691d4 Mon Sep 17 00:00:00 2001 From: James Price Date: Mon, 11 Mar 2024 13:47:24 -0400 Subject: [PATCH 13/33] wgsl: Add comparison operator validation tests (#3471) Test that comparison operators are only accepted for valid combinations of scalar/vector types, and that they are never accepted for non-scalar/non-vector types. --- src/webgpu/listing_meta.json | 2 + .../expression/binary/comparison.spec.ts | 186 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 src/webgpu/shader/validation/expression/binary/comparison.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 327ba644120d..7c4982ca1853 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1813,6 +1813,8 @@ "webgpu:shader,validation,expression,binary,bitwise_shift:shift_left_vec_size_mismatch:*": { "subcaseMS": 1.367 }, "webgpu:shader,validation,expression,binary,bitwise_shift:shift_right_concrete:*": { "subcaseMS": 1.237 }, "webgpu:shader,validation,expression,binary,bitwise_shift:shift_right_vec_size_mismatch:*": { "subcaseMS": 1.334 }, + "webgpu:shader,validation,expression,binary,comparison:invalid_types:*": { "subcaseMS": 39.526 }, + "webgpu:shader,validation,expression,binary,comparison:scalar_vector:*": { "subcaseMS": 1598.064 }, "webgpu:shader,validation,expression,call,builtin,abs:parameters:*": { "subcaseMS": 10.133 }, "webgpu:shader,validation,expression,call,builtin,abs:values:*": { "subcaseMS": 0.391 }, "webgpu:shader,validation,expression,call,builtin,acos:integer_argument:*": { "subcaseMS": 1.512 }, diff --git a/src/webgpu/shader/validation/expression/binary/comparison.spec.ts b/src/webgpu/shader/validation/expression/binary/comparison.spec.ts new file mode 100644 index 000000000000..bfba7adaa6b4 --- /dev/null +++ b/src/webgpu/shader/validation/expression/binary/comparison.spec.ts @@ -0,0 +1,186 @@ +export const description = ` +Validation tests for comparison expressions. +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { keysOf, objectsToRecord } from '../../../../../common/util/data_tables.js'; +import { + isFloatType, + kAllScalarsAndVectors, + ScalarType, + scalarTypeOf, + Type, + VectorType, +} from '../../../../util/conversion.js'; +import { ShaderValidationTest } from '../../shader_validation_test.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +// A list of scalar and vector types. +const kScalarAndVectorTypes = objectsToRecord(kAllScalarsAndVectors); + +// A list of comparison operators and a flag for whether they support boolean values or not. +const kComparisonOperators = { + eq: { op: '==', supportsBool: true }, + ne: { op: '!=', supportsBool: true }, + gt: { op: '>', supportsBool: false }, + ge: { op: '>=', supportsBool: false }, + lt: { op: '<', supportsBool: false }, + le: { op: '<=', supportsBool: false }, +}; + +g.test('scalar_vector') + .desc( + ` + Validates that scalar and vector comparison expressions are only accepted for compatible types. + ` + ) + .params(u => + u + .combine('op', keysOf(kComparisonOperators)) + .combine('lhs', keysOf(kScalarAndVectorTypes)) + .combine( + 'rhs', + // Skip vec3 and vec4 on the RHS to keep the number of subcases down. + keysOf(kScalarAndVectorTypes).filter( + value => !(value.startsWith('vec3') || value.startsWith('vec4')) + ) + ) + .beginSubcases() + ) + .beforeAllSubcases(t => { + if ( + scalarTypeOf(kScalarAndVectorTypes[t.params.lhs]) === Type.f16 || + scalarTypeOf(kScalarAndVectorTypes[t.params.rhs]) === Type.f16 + ) { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(t => { + const lhs = kScalarAndVectorTypes[t.params.lhs]; + const rhs = kScalarAndVectorTypes[t.params.rhs]; + const lhsElement = scalarTypeOf(lhs); + const rhsElement = scalarTypeOf(rhs); + const hasF16 = lhsElement === Type.f16 || rhsElement === Type.f16; + const code = ` +${hasF16 ? 'enable f16;' : ''} +const lhs = ${lhs.create(lhsElement.kind === 'abstract-int' ? 0n : 0).wgsl()}; +const rhs = ${rhs.create(rhsElement.kind === 'abstract-int' ? 0n : 0).wgsl()}; +const foo = lhs ${kComparisonOperators[t.params.op].op} rhs; +`; + + let valid = false; + + // Determine if the element types are comparable. + let elementIsCompatible = false; + if (lhsElement.kind === 'abstract-int') { + // Abstract integers are comparable to any other numeric type. + elementIsCompatible = rhsElement.kind !== 'bool'; + } else if (rhsElement.kind === 'abstract-int') { + // Abstract integers are comparable to any other numeric type. + elementIsCompatible = lhsElement.kind !== 'bool'; + } else if (lhsElement.kind === 'abstract-float') { + // Abstract floats are comparable to any other float type. + elementIsCompatible = isFloatType(rhsElement); + } else if (rhsElement.kind === 'abstract-float') { + // Abstract floats are comparable to any other float type. + elementIsCompatible = isFloatType(lhsElement); + } else { + // Non-abstract types are only comparable to values with the exact same type. + elementIsCompatible = lhsElement === rhsElement; + } + + // Determine if the full type is comparable. + if (lhs instanceof ScalarType && rhs instanceof ScalarType) { + valid = elementIsCompatible; + } else if (lhs instanceof VectorType && rhs instanceof VectorType) { + // Vectors are only comparable if the vector widths match. + valid = lhs.width === rhs.width && elementIsCompatible; + } + + if (lhsElement.kind === 'bool') { + valid &&= kComparisonOperators[t.params.op].supportsBool; + } + + t.expectCompileResult(valid, code); + }); + +interface InvalidTypeConfig { + // An expression that produces a value of the target type. + expr: string; + // A function that converts an expression of the target type into a valid comparison operand. + control: (x: string) => string; +} +const kInvalidTypes: Record = { + mat2x2f: { + expr: 'm', + control: e => `${e}[0]`, + }, + + array: { + expr: 'arr', + control: e => `${e}[0]`, + }, + + ptr: { + expr: '(&u)', + control: e => `*${e}`, + }, + + atomic: { + expr: 'a', + control: e => `atomicLoad(&${e})`, + }, + + texture: { + expr: 't', + control: e => `textureLoad(${e}, vec2(), 0)`, + }, + + sampler: { + expr: 's', + control: e => `textureSampleLevel(t, ${e}, vec2(), 0)`, + }, + + struct: { + expr: 'str', + control: e => `${e}.u`, + }, +}; + +g.test('invalid_types') + .desc( + ` + Validates that comparison expressions are never accepted for non-scalar and non-vector types. + ` + ) + .params(u => + u + .combine('op', keysOf(kComparisonOperators)) + .combine('type', keysOf(kInvalidTypes)) + .combine('control', [true, false]) + .beginSubcases() + ) + .fn(t => { + const type = kInvalidTypes[t.params.type]; + const expr = t.params.control ? type.control(type.expr) : type.expr; + const code = ` +@group(0) @binding(0) var t : texture_2d; +@group(0) @binding(1) var s : sampler; +@group(0) @binding(2) var a : atomic; + +struct S { u : u32 } + +var u : u32; +var m : mat2x2f; +var arr : array; +var str : S; + +@compute @workgroup_size(1) +fn main() { + let foo = ${expr} ${kComparisonOperators[t.params.op].op} ${expr}; +} +`; + + t.expectCompileResult(t.params.control, code); + }); From 4f913dcf23a3aa31287ba81b71f743f49c564928 Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Mon, 11 Mar 2024 11:40:03 -0700 Subject: [PATCH 14/33] Clean up deprecated .copyDst/.renderable/.renderTarget* from kTextureFormatInfo (#3412) * Refactor deprecated .copyDst/.renderable in texture_zero_init_test.ts * Remove no-op use of .renderable in texture_zero_init_test.ts * Refactor deprecated .renderable in createTexture.spec.ts * Remove deprecated .copyDst * Remove deprecated .renderable * Remove unused .renderTarget{ComponentAlignment,PixelByteCost} .renderTargetComponentAlignment = .colorRender.alignment .renderTargetPixelByteCost = .colorRender.byteCost The one exception to this is rg11b10ufloat, which is not renderable in core, but has an extension for renderability. kTextureFormatInfo currently only expresses core capabilities. The alignment and byteCost for this format will need to be added back when the refactor is done to make a table for "static" properties of texture formats (which never vary based on capabilities) - shape of that refactor is TBD. * Refactor trivial uses of deprecated .bytesPerBlock --- .../command_buffer/image_copy.spec.ts | 2 +- .../rendering/depth_clip_clamp.spec.ts | 10 +- .../check_texture/texture_zero_init_test.ts | 31 ++-- .../storage_texture/read_only.spec.ts | 2 +- .../api/validation/createTexture.spec.ts | 19 ++- .../image_copy/texture_related.spec.ts | 2 +- .../encoding/cmds/copyTextureToBuffer.spec.ts | 9 +- src/webgpu/format_info.ts | 133 +----------------- 8 files changed, 42 insertions(+), 166 deletions(-) diff --git a/src/webgpu/api/operation/command_buffer/image_copy.spec.ts b/src/webgpu/api/operation/command_buffer/image_copy.spec.ts index 9927bfb89872..cff2bd50d575 100644 --- a/src/webgpu/api/operation/command_buffer/image_copy.spec.ts +++ b/src/webgpu/api/operation/command_buffer/image_copy.spec.ts @@ -1595,7 +1595,7 @@ works for every format with 2d and 2d-array textures. }; let textureHeight = 4 * info.blockHeight; let rowsPerImage = rowsPerImageEqualsCopyHeight ? copyHeight : copyHeight + 1; - const bytesPerRow = align(copyWidth * info.bytesPerBlock, 256); + const bytesPerRow = align(copyWidth * info.color.bytes, 256); if (dimension === '1d') { copySize.height = 1; diff --git a/src/webgpu/api/operation/rendering/depth_clip_clamp.spec.ts b/src/webgpu/api/operation/rendering/depth_clip_clamp.spec.ts index 65e2e8af1f21..5e8357114107 100644 --- a/src/webgpu/api/operation/rendering/depth_clip_clamp.spec.ts +++ b/src/webgpu/api/operation/rendering/depth_clip_clamp.spec.ts @@ -4,6 +4,7 @@ depth ranges as well. `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; +import { assert } from '../../../../common/util/util.js'; import { kDepthStencilFormats, kTextureFormatInfo } from '../../../format_info.js'; import { GPUTest } from '../../../gpu_test.js'; import { @@ -52,6 +53,7 @@ have unexpected values then get drawn to the color buffer, which is later checke .fn(async t => { const { format, unclippedDepth, writeDepth, multisampled } = t.params; const info = kTextureFormatInfo[format]; + assert(!!info.depth); /** Number of depth values to test for both vertex output and frag_depth output. */ const kNumDepthValues = 8; @@ -222,16 +224,16 @@ have unexpected values then get drawn to the color buffer, which is later checke : undefined; const dsActual = - !multisampled && info.bytesPerBlock + !multisampled && info.depth.bytes ? t.device.createBuffer({ - size: kNumTestPoints * info.bytesPerBlock, + size: kNumTestPoints * info.depth.bytes, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ, }) : undefined; const dsExpected = - !multisampled && info.bytesPerBlock + !multisampled && info.depth.bytes ? t.device.createBuffer({ - size: kNumTestPoints * info.bytesPerBlock, + size: kNumTestPoints * info.depth.bytes, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ, }) : undefined; diff --git a/src/webgpu/api/operation/resource_init/check_texture/texture_zero_init_test.ts b/src/webgpu/api/operation/resource_init/check_texture/texture_zero_init_test.ts index 22ffe0a3e518..6ff3ab4c9b41 100644 --- a/src/webgpu/api/operation/resource_init/check_texture/texture_zero_init_test.ts +++ b/src/webgpu/api/operation/resource_init/check_texture/texture_zero_init_test.ts @@ -115,6 +115,14 @@ const initializedStateAsStencil = { [InitializedState.Canary]: 42, }; +function allAspectsCopyDst(info: (typeof kTextureFormatInfo)[UncompressedTextureFormat]) { + return ( + (!info.color || info.color.copyDst) && + (!info.depth || info.depth.copyDst) && + (!info.stencil || info.stencil.copyDst) + ); +} + export function getRequiredTextureUsage( format: UncompressedTextureFormat, sampleCount: number, @@ -159,10 +167,11 @@ export function getRequiredTextureUsage( usage |= GPUConst.TextureUsage.RENDER_ATTACHMENT; } - if (!kTextureFormatInfo[format].copyDst) { + const info = kTextureFormatInfo[format]; + if (!allAspectsCopyDst(info)) { // Copies are not possible. We need OutputAttachment to initialize // canary data. - assert(kTextureFormatInfo[format].renderable); + if (info.color) assert(!!info.colorRender, 'not implemented for non-renderable color'); usage |= GPUConst.TextureUsage.RENDER_ATTACHMENT; } @@ -388,10 +397,11 @@ export class TextureZeroInitTest extends GPUTest { state: InitializedState, subresourceRange: SubresourceRange ): void { - if (this.p.sampleCount > 1 || !kTextureFormatInfo[this.p.format].copyDst) { + const info = kTextureFormatInfo[this.p.format]; + if (this.p.sampleCount > 1 || !allAspectsCopyDst(info)) { // Copies to multisampled textures not yet specified. // Use a storeOp for now. - assert(kTextureFormatInfo[this.p.format].renderable); + if (info.color) assert(!!info.colorRender, 'not implemented for non-renderable color'); this.initializeWithStoreOp(state, texture, subresourceRange); } else { this.initializeWithCopy(texture, state, subresourceRange); @@ -517,20 +527,15 @@ export const kTestParams = kUnitCaseParamsBuilder const info = kTextureFormatInfo[format]; return ( - ((usage & GPUConst.TextureUsage.RENDER_ATTACHMENT) !== 0 && !info.renderable) || + ((usage & GPUConst.TextureUsage.RENDER_ATTACHMENT) !== 0 && + info.color && + !info.colorRender) || ((usage & GPUConst.TextureUsage.STORAGE_BINDING) !== 0 && !info.color?.storage) || (sampleCount > 1 && !info.multisample) ); }) .combine('nonPowerOfTwo', [false, true]) - .combine('canaryOnCreation', [false, true]) - .filter(({ canaryOnCreation, format }) => { - // We can only initialize the texture if it's encodable or renderable. - const canInitialize = format in kTextureFormatInfo || kTextureFormatInfo[format].renderable; - - // Filter out cases where we want canary values but can't initialize. - return !canaryOnCreation || canInitialize; - }); + .combine('canaryOnCreation', [false, true]); type TextureZeroParams = ParamTypeOf; diff --git a/src/webgpu/api/operation/storage_texture/read_only.spec.ts b/src/webgpu/api/operation/storage_texture/read_only.spec.ts index 9a479eee9d15..978924aabd67 100644 --- a/src/webgpu/api/operation/storage_texture/read_only.spec.ts +++ b/src/webgpu/api/operation/storage_texture/read_only.spec.ts @@ -51,7 +51,7 @@ class F extends GPUTest { storageTexture: GPUTexture, format: ColorTextureFormat ): ArrayBuffer { - const bytesPerBlock = kTextureFormatInfo[format].bytesPerBlock; + const bytesPerBlock = kTextureFormatInfo[format].color.bytes; assert(bytesPerBlock !== undefined); const width = storageTexture.width; diff --git a/src/webgpu/api/validation/createTexture.spec.ts b/src/webgpu/api/validation/createTexture.spec.ts index 04c029d78f1d..fc1c8b86b26b 100644 --- a/src/webgpu/api/validation/createTexture.spec.ts +++ b/src/webgpu/api/validation/createTexture.spec.ts @@ -299,7 +299,7 @@ g.test('sampleCount,various_sampleCount_with_all_formats') usage, }; - const success = sampleCount === 1 || (sampleCount === 4 && info.multisample && info.renderable); + const success = sampleCount === 1 || (sampleCount === 4 && info.multisample); t.expectValidationError(() => { t.device.createTexture(descriptor); @@ -1066,16 +1066,13 @@ g.test('texture_usage') // Note that we unconditionally test copy usages for all formats. We don't check copySrc/copyDst in kTextureFormatInfo in capability_info.js // if (!info.copySrc && (usage & GPUTextureUsage.COPY_SRC) !== 0) success = false; // if (!info.copyDst && (usage & GPUTextureUsage.COPY_DST) !== 0) success = false; - if ( - (usage & GPUTextureUsage.STORAGE_BINDING) !== 0 && - !isTextureFormatUsableAsStorageFormat(format, t.isCompatibility) - ) - success = false; - if ( - (!info.renderable || (appliedDimension !== '2d' && appliedDimension !== '3d')) && - (usage & GPUTextureUsage.RENDER_ATTACHMENT) !== 0 - ) - success = false; + if (usage & GPUTextureUsage.STORAGE_BINDING) { + if (!isTextureFormatUsableAsStorageFormat(format, t.isCompatibility)) success = false; + } + if (usage & GPUTextureUsage.RENDER_ATTACHMENT) { + if (appliedDimension === '1d') success = false; + if (info.color && !info.colorRender) success = false; + } t.expectValidationError(() => { t.device.createTexture(descriptor); diff --git a/src/webgpu/api/validation/image_copy/texture_related.spec.ts b/src/webgpu/api/validation/image_copy/texture_related.spec.ts index a0fe38e8e313..cbc36b1431ba 100644 --- a/src/webgpu/api/validation/image_copy/texture_related.spec.ts +++ b/src/webgpu/api/validation/image_copy/texture_related.spec.ts @@ -440,7 +440,7 @@ Test that the copy size must be aligned to the texture's format's block size. const texture = t.createAlignedTexture(format, size, origin, dimension); const bytesPerRow = align( - Math.max(1, Math.ceil(size.width / info.blockWidth)) * info.bytesPerBlock, + Math.max(1, Math.ceil(size.width / info.blockWidth)) * info.color.bytes, 256 ); const rowsPerImage = Math.ceil(size.height / info.blockHeight); diff --git a/src/webgpu/compat/api/validation/encoding/cmds/copyTextureToBuffer.spec.ts b/src/webgpu/compat/api/validation/encoding/cmds/copyTextureToBuffer.spec.ts index a9af7795b3c4..e7d5164c4539 100644 --- a/src/webgpu/compat/api/validation/encoding/cmds/copyTextureToBuffer.spec.ts +++ b/src/webgpu/compat/api/validation/encoding/cmds/copyTextureToBuffer.spec.ts @@ -19,16 +19,17 @@ g.test('compressed') .fn(t => { const { format } = t.params; - const { blockWidth, blockHeight, bytesPerBlock } = kTextureFormatInfo[format]; + const info = kTextureFormatInfo[format]; + const textureSize = [info.blockWidth, info.blockHeight, 1]; const texture = t.device.createTexture({ - size: [blockWidth, blockHeight, 1], + size: textureSize, format, usage: GPUTextureUsage.COPY_SRC, }); t.trackForCleanup(texture); - const bytesPerRow = align(bytesPerBlock, 256); + const bytesPerRow = align(info.color.bytes, 256); const buffer = t.device.createBuffer({ size: bytesPerRow, @@ -37,7 +38,7 @@ g.test('compressed') t.trackForCleanup(buffer); const encoder = t.device.createCommandEncoder(); - encoder.copyTextureToBuffer({ texture }, { buffer, bytesPerRow }, [blockWidth, blockHeight, 1]); + encoder.copyTextureToBuffer({ texture }, { buffer, bytesPerRow }, textureSize); t.expectGPUError('validation', () => { encoder.finish(); }); diff --git a/src/webgpu/format_info.ts b/src/webgpu/format_info.ts index 8ed39cdf3d22..be1320d3cdad 100644 --- a/src/webgpu/format_info.ts +++ b/src/webgpu/format_info.ts @@ -31,16 +31,8 @@ const kFormatUniversalDefaults = { /** The base format for srgb formats. Specified on both srgb and equivalent non-srgb formats. */ baseFormat: undefined, - /** @deprecated */ - copyDst: undefined, /** @deprecated Use `.color.bytes`, `.depth.bytes`, or `.stencil.bytes`. */ bytesPerBlock: undefined, - /** @deprecated */ - renderable: false, - /** @deprecated */ - renderTargetPixelByteCost: undefined, - /** @deprecated */ - renderTargetComponentAlignment: undefined, // IMPORTANT: // Add new top-level keys both here and in TextureFormatInfo_TypeCheck. @@ -76,7 +68,7 @@ function formatTableWithDefaults kColorTextureFormatInfo[v].colorRender ); -assert( - kRenderableColorTextureFormats.every( - f => - kAllTextureFormatInfo[f].renderTargetComponentAlignment !== undefined && - kAllTextureFormatInfo[f].renderTargetPixelByteCost !== undefined - ) -); /** Per-GPUTextureFormat-per-aspect info. */ interface TextureFormatAspectInfo { @@ -1608,11 +1483,7 @@ type TextureFormatInfo_TypeCheck = { baseFormat: GPUTextureFormat | undefined; feature: GPUFeatureName | undefined; - copyDst: boolean; bytesPerBlock: number | undefined; - renderable: boolean; - renderTargetPixelByteCost: number | undefined; - renderTargetComponentAlignment: number | undefined; // IMPORTANT: // Add new top-level keys both here and in kUniversalDefaults. From fcf6a223a1b33eb1fb1e3e3a57242ae342272a7d Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Fri, 8 Mar 2024 17:18:23 +0000 Subject: [PATCH 15/33] Rework the expression harness to support array values. This change makes the harness smarter about inputs and output structures are laid out, and there's far less redundant padding - especially for storage buffers. This might help performance, as more cases can be packed into a single shader dispatch. --- src/resources/cache/hashes.json | 216 +++++++-------- src/webgpu/listing_meta.json | 2 + .../shader/execution/expression/expression.ts | 245 +++++++++--------- src/webgpu/util/conversion.ts | 25 +- 4 files changed, 258 insertions(+), 230 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index d178aba9e161..3e6a9f6e58d3 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "360aab58", - "webgpu/shader/execution/binary/af_logical.bin": "4269127d", - "webgpu/shader/execution/binary/af_division.bin": "2a1ae6a9", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "135913e7", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "83a7b6ab", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "5959bc9f", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "9c125148", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "efbe60ca", - "webgpu/shader/execution/binary/af_multiplication.bin": "923ed423", - "webgpu/shader/execution/binary/af_remainder.bin": "c17bb87f", - "webgpu/shader/execution/binary/af_subtraction.bin": "6457d271", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "f89aeb4", - "webgpu/shader/execution/binary/f16_addition.bin": "85c17d72", - "webgpu/shader/execution/binary/f16_logical.bin": "36a51091", - "webgpu/shader/execution/binary/f16_division.bin": "7c38f4b7", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "9e6258d7", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "916ca7e6", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "a4f59720", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "5e94030b", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "45b64fd9", - "webgpu/shader/execution/binary/f16_multiplication.bin": "ee7ba392", - "webgpu/shader/execution/binary/f16_remainder.bin": "ea8f78e", - "webgpu/shader/execution/binary/f16_subtraction.bin": "f5e0d152", - "webgpu/shader/execution/binary/f32_addition.bin": "d82339c6", - "webgpu/shader/execution/binary/f32_logical.bin": "e1d07173", - "webgpu/shader/execution/binary/f32_division.bin": "46721765", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "6fdf77f1", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "73520069", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "5ea144ba", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "5f5213d2", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "2ff78c70", - "webgpu/shader/execution/binary/f32_multiplication.bin": "3eb2748f", - "webgpu/shader/execution/binary/f32_remainder.bin": "a6728c3e", - "webgpu/shader/execution/binary/f32_subtraction.bin": "6d3120c0", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "936d712", - "webgpu/shader/execution/binary/i32_comparison.bin": "5db8f3b0", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "415ac05e", - "webgpu/shader/execution/binary/u32_comparison.bin": "f90d60f3", - "webgpu/shader/execution/abs.bin": "3bcfb49e", - "webgpu/shader/execution/acos.bin": "86824420", - "webgpu/shader/execution/acosh.bin": "95f9a9a4", - "webgpu/shader/execution/asin.bin": "1d017518", - "webgpu/shader/execution/asinh.bin": "56df3876", - "webgpu/shader/execution/atan.bin": "6aaa6b50", - "webgpu/shader/execution/atan2.bin": "82bcc1a2", - "webgpu/shader/execution/atanh.bin": "36a1f68a", - "webgpu/shader/execution/bitcast.bin": "6b7ea46b", - "webgpu/shader/execution/ceil.bin": "7308c54f", - "webgpu/shader/execution/clamp.bin": "9bfd2fce", - "webgpu/shader/execution/cos.bin": "2fa872d3", - "webgpu/shader/execution/cosh.bin": "2e6538aa", - "webgpu/shader/execution/cross.bin": "71a7e274", - "webgpu/shader/execution/degrees.bin": "31d8728f", - "webgpu/shader/execution/determinant.bin": "399a64cb", - "webgpu/shader/execution/distance.bin": "4282ab43", - "webgpu/shader/execution/dot.bin": "b4940f90", - "webgpu/shader/execution/exp.bin": "3801f256", - "webgpu/shader/execution/exp2.bin": "fb531b99", - "webgpu/shader/execution/faceForward.bin": "ac35ad68", - "webgpu/shader/execution/floor.bin": "177dc3a6", - "webgpu/shader/execution/fma.bin": "27a3e818", - "webgpu/shader/execution/fract.bin": "8ed3cd4b", - "webgpu/shader/execution/frexp.bin": "b6d69258", - "webgpu/shader/execution/inverseSqrt.bin": "b08786b3", - "webgpu/shader/execution/ldexp.bin": "6b8bcdb3", - "webgpu/shader/execution/length.bin": "3dc4b222", - "webgpu/shader/execution/log.bin": "9219d5e4", - "webgpu/shader/execution/log2.bin": "d5ca64af", - "webgpu/shader/execution/max.bin": "9051c302", - "webgpu/shader/execution/min.bin": "e950d75f", - "webgpu/shader/execution/mix.bin": "3a720d24", - "webgpu/shader/execution/modf.bin": "ef00e883", - "webgpu/shader/execution/normalize.bin": "52231704", - "webgpu/shader/execution/pack2x16float.bin": "54f11627", - "webgpu/shader/execution/pow.bin": "9fdb1669", - "webgpu/shader/execution/quantizeToF16.bin": "e980118b", - "webgpu/shader/execution/radians.bin": "a676aec1", - "webgpu/shader/execution/reflect.bin": "a951c771", - "webgpu/shader/execution/refract.bin": "d8953376", - "webgpu/shader/execution/round.bin": "eee2fc9", - "webgpu/shader/execution/saturate.bin": "161df54c", - "webgpu/shader/execution/sign.bin": "9d9a22ff", - "webgpu/shader/execution/sin.bin": "29f7032a", - "webgpu/shader/execution/sinh.bin": "9d282f12", - "webgpu/shader/execution/smoothstep.bin": "f4230ffb", - "webgpu/shader/execution/sqrt.bin": "1dba9696", - "webgpu/shader/execution/step.bin": "2048f771", - "webgpu/shader/execution/tan.bin": "adc5217b", - "webgpu/shader/execution/tanh.bin": "20f7d479", - "webgpu/shader/execution/transpose.bin": "727afd91", - "webgpu/shader/execution/trunc.bin": "bc14dd25", - "webgpu/shader/execution/unpack2x16float.bin": "b8428e59", - "webgpu/shader/execution/unpack2x16snorm.bin": "e38f4982", - "webgpu/shader/execution/unpack2x16unorm.bin": "c7cdd947", - "webgpu/shader/execution/unpack4x8snorm.bin": "71e176b1", - "webgpu/shader/execution/unpack4x8unorm.bin": "5e61d6ea", - "webgpu/shader/execution/unary/af_arithmetic.bin": "29bc881a", - "webgpu/shader/execution/unary/af_assignment.bin": "33c25997", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "497ec1e1", - "webgpu/shader/execution/unary/ai_assignment.bin": "fc978bdd", - "webgpu/shader/execution/unary/bool_conversion.bin": "bcab7d9a", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "925ed6d", - "webgpu/shader/execution/unary/f16_conversion.bin": "751ff969", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "b26d9187", - "webgpu/shader/execution/unary/f32_conversion.bin": "482ee20f", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "8f3c4616", - "webgpu/shader/execution/unary/i32_conversion.bin": "972063c", - "webgpu/shader/execution/unary/u32_conversion.bin": "168fcf74" + "webgpu/shader/execution/binary/af_addition.bin": "b6797474", + "webgpu/shader/execution/binary/af_logical.bin": "ff27f50f", + "webgpu/shader/execution/binary/af_division.bin": "97ba2e6d", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "842a508f", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "f086bb00", + "webgpu/shader/execution/binary/af_multiplication.bin": "fb6d6475", + "webgpu/shader/execution/binary/af_remainder.bin": "aa462ca7", + "webgpu/shader/execution/binary/af_subtraction.bin": "f04fbb32", + "webgpu/shader/execution/binary/f16_addition.bin": "3f136f5a", + "webgpu/shader/execution/binary/f16_logical.bin": "8556deb1", + "webgpu/shader/execution/binary/f16_division.bin": "693df765", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "296a11b6", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "cf1ca531", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "d3a317bd", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "a7a1b148", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "8f540eee", + "webgpu/shader/execution/binary/f16_multiplication.bin": "1a7bca1c", + "webgpu/shader/execution/binary/f16_remainder.bin": "62b7bff8", + "webgpu/shader/execution/binary/f16_subtraction.bin": "c921e09d", + "webgpu/shader/execution/binary/f32_addition.bin": "7d7629bd", + "webgpu/shader/execution/binary/f32_logical.bin": "3e903bfb", + "webgpu/shader/execution/binary/f32_division.bin": "35b49326", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "1c2aff7c", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "966b264e", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "4ab99310", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "77f37d5a", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "50dfea9a", + "webgpu/shader/execution/binary/f32_multiplication.bin": "2524db25", + "webgpu/shader/execution/binary/f32_remainder.bin": "b47ad113", + "webgpu/shader/execution/binary/f32_subtraction.bin": "e98584ae", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "88d15602", + "webgpu/shader/execution/binary/i32_comparison.bin": "ddd815a3", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "d882b7f6", + "webgpu/shader/execution/binary/u32_comparison.bin": "e3dfeec7", + "webgpu/shader/execution/abs.bin": "b6f6d4ec", + "webgpu/shader/execution/acos.bin": "d681fe45", + "webgpu/shader/execution/acosh.bin": "a58482ca", + "webgpu/shader/execution/asin.bin": "3856670", + "webgpu/shader/execution/asinh.bin": "7567d4b7", + "webgpu/shader/execution/atan.bin": "80fc2c53", + "webgpu/shader/execution/atan2.bin": "2fc91f76", + "webgpu/shader/execution/atanh.bin": "b68b4e98", + "webgpu/shader/execution/bitcast.bin": "142a34e", + "webgpu/shader/execution/ceil.bin": "65c551a0", + "webgpu/shader/execution/clamp.bin": "369a3396", + "webgpu/shader/execution/cos.bin": "894e3d79", + "webgpu/shader/execution/cosh.bin": "3bf810f1", + "webgpu/shader/execution/cross.bin": "582e87e8", + "webgpu/shader/execution/degrees.bin": "762053b5", + "webgpu/shader/execution/determinant.bin": "a60ac4f9", + "webgpu/shader/execution/distance.bin": "602fc718", + "webgpu/shader/execution/dot.bin": "c33454b1", + "webgpu/shader/execution/exp.bin": "ad6670cf", + "webgpu/shader/execution/exp2.bin": "6f8a0b39", + "webgpu/shader/execution/faceForward.bin": "759ef028", + "webgpu/shader/execution/floor.bin": "d8f91dbe", + "webgpu/shader/execution/fma.bin": "5019731d", + "webgpu/shader/execution/fract.bin": "3a9c858d", + "webgpu/shader/execution/frexp.bin": "33ffb585", + "webgpu/shader/execution/inverseSqrt.bin": "3684c8a", + "webgpu/shader/execution/ldexp.bin": "30881102", + "webgpu/shader/execution/length.bin": "fed952", + "webgpu/shader/execution/log.bin": "b55f7cd0", + "webgpu/shader/execution/log2.bin": "e4fddf41", + "webgpu/shader/execution/max.bin": "ca77aba5", + "webgpu/shader/execution/min.bin": "6b57ac82", + "webgpu/shader/execution/mix.bin": "89f42f5a", + "webgpu/shader/execution/modf.bin": "77eb4ff5", + "webgpu/shader/execution/normalize.bin": "36214034", + "webgpu/shader/execution/pack2x16float.bin": "8bee7573", + "webgpu/shader/execution/pow.bin": "8bcaf788", + "webgpu/shader/execution/quantizeToF16.bin": "8c1bc956", + "webgpu/shader/execution/radians.bin": "c35eecd0", + "webgpu/shader/execution/reflect.bin": "3591dbf7", + "webgpu/shader/execution/refract.bin": "8b2a061d", + "webgpu/shader/execution/round.bin": "58787d04", + "webgpu/shader/execution/saturate.bin": "b5c39be6", + "webgpu/shader/execution/sign.bin": "3b6e8c88", + "webgpu/shader/execution/sin.bin": "1e39b6f9", + "webgpu/shader/execution/sinh.bin": "296767d4", + "webgpu/shader/execution/smoothstep.bin": "2771eb6a", + "webgpu/shader/execution/sqrt.bin": "78c4534c", + "webgpu/shader/execution/step.bin": "b8817ec0", + "webgpu/shader/execution/tan.bin": "f2537ac4", + "webgpu/shader/execution/tanh.bin": "c611a910", + "webgpu/shader/execution/transpose.bin": "7fdac41d", + "webgpu/shader/execution/trunc.bin": "da4434eb", + "webgpu/shader/execution/unpack2x16float.bin": "a5db67bb", + "webgpu/shader/execution/unpack2x16snorm.bin": "b0e6f0f4", + "webgpu/shader/execution/unpack2x16unorm.bin": "ddf30c4f", + "webgpu/shader/execution/unpack4x8snorm.bin": "599b3fb9", + "webgpu/shader/execution/unpack4x8unorm.bin": "d47c58be", + "webgpu/shader/execution/unary/af_arithmetic.bin": "790d7c95", + "webgpu/shader/execution/unary/af_assignment.bin": "679cfa9a", + "webgpu/shader/execution/unary/bool_conversion.bin": "d10f1dbb", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "1b44c7f", + "webgpu/shader/execution/unary/f16_conversion.bin": "436a5636", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "8f8fcd42", + "webgpu/shader/execution/unary/f32_conversion.bin": "97d0e21a", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "41460aa", + "webgpu/shader/execution/unary/i32_conversion.bin": "d5a46ee6", + "webgpu/shader/execution/unary/u32_conversion.bin": "f03edff7", + "webgpu/shader/execution/unary/ai_assignment.bin": "5d612a40", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "c0dd04f9", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "3ea222b8", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "3d9cf9b7", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "a84ecc73", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "6fe3e741" } \ No newline at end of file diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 7c4982ca1853..fd2ef2c147a6 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -899,6 +899,8 @@ "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,access,index,array:scalar:*": { "subcaseMS": 657.148 }, + "webgpu:shader,execution,expression,access,index,array:vector:*": { "subcaseMS": 1089.448 }, "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/expression.ts b/src/webgpu/shader/execution/expression/expression.ts index 21e1d40eab90..50885c0d3594 100644 --- a/src/webgpu/shader/execution/expression/expression.ts +++ b/src/webgpu/shader/execution/expression/expression.ts @@ -13,7 +13,9 @@ import { VectorValue, isAbstractType, scalarTypeOf, + ArrayType, } from '../../../util/conversion.js'; +import { align } from '../../../util/math.js'; import { Case } from './case.js'; import { toComparator } from './expectation.js'; @@ -47,103 +49,117 @@ export type Config = { vectorize?: number; }; -// Helper for returning the stride for a given Type -function valueStride(ty: Type): number { - // AbstractFloats and AbstractInts are passed out of the shader via structs of - // 2x u32s and unpacking containers as arrays - if (scalarTypeOf(ty).kind === 'abstract-float' || scalarTypeOf(ty).kind === 'abstract-int') { - if (ty instanceof ScalarType) { - return 16; - } - if (ty instanceof VectorType) { - if (ty.width === 2) { - return 16; - } - // vec3s have padding to make them the same size as vec4s - return 32; - } - if (ty instanceof MatrixType) { - switch (ty.cols) { - case 2: - switch (ty.rows) { - case 2: - return 32; - case 3: - return 64; - case 4: - return 64; - } - break; - case 3: - switch (ty.rows) { - case 2: - return 48; - case 3: - return 96; - case 4: - return 96; - } - break; - case 4: - switch (ty.rows) { - case 2: - return 64; - case 3: - return 128; - case 4: - return 128; - } - break; - } +/** + * @returns the size and alignment in bytes of the type 'ty', taking into + * consideration storage alignment constraints and abstract numerics, which are + * encoded as a struct of holding two u32s. + */ +function sizeAndAlignmentOf(ty: Type, source: InputSource): { size: number; alignment: number } { + if (ty instanceof ScalarType) { + if (ty.kind === 'abstract-float' || ty.kind === 'abstract-int') { + // AbstractFloats and AbstractInts are passed out of the shader via structs of + // 2x u32s and unpacking containers as arrays + return { size: 8, alignment: 8 }; } - unreachable(`AbstractFloats have not yet been implemented for ${ty.toString()}`); + return { size: ty.size, alignment: ty.alignment }; + } + + if (ty instanceof VectorType) { + const out = sizeAndAlignmentOf(ty.elementType, source); + const n = ty.width === 3 ? 4 : ty.width; + out.size *= n; + out.alignment *= n; + return out; } if (ty instanceof MatrixType) { - switch (ty.cols) { - case 2: - switch (ty.rows) { - case 2: - return 16; - case 3: - return 32; - case 4: - return 32; - } - break; - case 3: - switch (ty.rows) { - case 2: - return 32; - case 3: - return 64; - case 4: - return 64; - } - break; - case 4: - switch (ty.rows) { - case 2: - return 32; - case 3: - return 64; - case 4: - return 64; - } - break; + const out = sizeAndAlignmentOf(ty.elementType, source); + const n = ty.rows === 3 ? 4 : ty.rows; + out.size *= n * ty.cols; + out.alignment *= n; + return out; + } + + if (ty instanceof ArrayType) { + const out = sizeAndAlignmentOf(ty.elementType, source); + if (source === 'uniform') { + out.alignment = align(out.alignment, 16); } - unreachable( - `Attempted to get stride length for a matrix with dimensions (${ty.cols}x${ty.rows}), which isn't currently handled` - ); + out.size *= ty.count; + return out; + } + + unreachable(`unhandled type: ${ty}`); +} + +/** + * @returns the stride in bytes of the type 'ty', taking into consideration abstract numerics, + * which are encoded as a struct of 2 x u32. + */ +function strideOf(ty: Type, source: InputSource): number { + const sizeAndAlign = sizeAndAlignmentOf(ty, source); + return align(sizeAndAlign.size, sizeAndAlign.alignment); +} + +/** + * Calls 'callback' with the layout information of each structure member with the types 'members'. + * @returns the byte size, stride and alignment of the structure. + */ +function structLayout( + members: Type[], + source: InputSource, + callback?: (m: { + index: number; + type: Type; + size: number; + alignment: number; + offset: number; + }) => void +): { size: number; stride: number; alignment: number } { + let offset = 0; + let alignment = 1; + for (let i = 0; i < members.length; i++) { + const member = members[i]; + const sizeAndAlign = sizeAndAlignmentOf(member, source); + offset = align(offset, sizeAndAlign.alignment); + if (callback) { + callback({ + index: i, + type: member, + size: sizeAndAlign.size, + alignment: sizeAndAlign.alignment, + offset, + }); + } + offset += sizeAndAlign.size; + alignment = Math.max(alignment, sizeAndAlign.alignment); } - // Handles scalars and vectors - return 16; + if (source === 'uniform') { + alignment = align(alignment, 16); + } + + const size = offset; + const stride = align(size, alignment); + return { size, stride, alignment }; +} + +/** @returns the stride in bytes between two consecutive structures with the given members */ +function structStride(members: Type[], source: InputSource): number { + return structLayout(members, source).stride; } -// Helper for summing up all the stride values for an array of Types -function valueStrides(tys: Type[]): number { - return tys.map(valueStride).reduce((sum, c) => sum + c); +/** @returns the WGSL to describe the structure members in 'members' */ +function wgslMembers(members: Type[], source: InputSource, memberName: (i: number) => string) { + const lines: string[] = []; + const layout = structLayout(members, source, m => { + lines.push(` @size(${m.size}) ${memberName(lines.length)} : ${m.type},`); + }); + const padding = layout.stride - layout.size; + if (padding > 0) { + lines.push(` @size(${padding}) padding : i32,`); + } + return lines.join('\n'); } // Helper for returning the WGSL storage type for the given Type. @@ -294,12 +310,13 @@ export async function run( // 2k appears to be a sweet-spot when benchmarking. return Math.floor( Math.min(1024 * 2, t.device.limits.maxUniformBufferBindingSize) / - valueStrides(parameterTypes) + structStride(parameterTypes, cfg.inputSource) ); case 'storage_r': case 'storage_rw': return Math.floor( - t.device.limits.maxStorageBufferBindingSize / valueStrides(parameterTypes) + t.device.limits.maxStorageBufferBindingSize / + structStride(parameterTypes, cfg.inputSource) ); } })(); @@ -378,7 +395,8 @@ async function submitBatch( pipelineCache: PipelineCache ): Promise<() => void> { // Construct a buffer to hold the results of the expression tests - const outputBufferSize = cases.length * valueStride(resultType); + const outputStride = structStride([resultType], 'storage_rw'); + const outputBufferSize = align(cases.length * outputStride, 4); const outputBuffer = t.device.createBuffer({ size: outputBufferSize, usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST | GPUBufferUsage.STORAGE, @@ -413,7 +431,7 @@ async function submitBatch( // Read the outputs from the output buffer const outputs = new Array(cases.length); for (let i = 0; i < cases.length; i++) { - outputs[i] = resultType.read(outputData, i * valueStride(resultType)); + outputs[i] = resultType.read(outputData, i * outputStride); } // The list of expectation failures @@ -482,7 +500,7 @@ function wgslOutputs(resultType: Type, count: number): string { ) { output_struct = ` struct Output { - @size(${valueStride(resultType)}) value : ${storageType(resultType)} + @size(${strideOf(resultType, 'storage_rw')}) value : ${storageType(resultType)} };`; } else { if (resultType instanceof ScalarType) { @@ -492,7 +510,7 @@ struct Output { }; struct Output { - @size(${valueStride(resultType)}) value: AF, + @size(${strideOf(resultType, 'storage_rw')}) value: AF, };`; } if (resultType instanceof VectorType) { @@ -503,7 +521,7 @@ struct Output { }; struct Output { - @size(${valueStride(resultType)}) value: array, + @size(${strideOf(resultType, 'storage_rw')}) value: array, };`; } @@ -516,7 +534,7 @@ struct Output { }; struct Output { - @size(${valueStride(resultType)}) value: array, ${cols}>, + @size(${strideOf(resultType, 'storage_rw')}) value: array, ${cols}>, };`; } @@ -654,10 +672,8 @@ ${body} return ` struct Input { -${parameterTypes - .map((ty, i) => ` @size(${valueStride(ty)}) param${i} : ${storageType(ty)},`) - .join('\n')} -}; +${wgslMembers(parameterTypes.map(storageType), inputSource, i => `param${i}`)} +} ${wgslOutputs(resultType, cases.length)} @@ -789,8 +805,7 @@ ${wgslHeader(parameterTypes, resultType)} ${wgslOutputs(resultType, cases.length)} struct Input { - @size(${valueStride(lhsType)}) lhs : ${storageType(lhsType)}, - @size(${valueStride(rhsType)}) rhs : ${storageType(rhsType)}, +${wgslMembers([lhsType, rhsType].map(storageType), inputSource, i => ['lhs', 'rhs'][i])} } ${wgslInputVar(inputSource, cases.length)} @@ -1126,27 +1141,23 @@ async function buildPipeline( // Input values come from a uniform or storage buffer // size in bytes of the input buffer - const inputSize = cases.length * valueStrides(parameterTypes); + const caseStride = structStride(parameterTypes, inputSource); + const inputSize = align(cases.length * caseStride, 4); // Holds all the parameter values for all cases const inputData = new Uint8Array(inputSize); // Pack all the input parameter values into the inputData buffer - { - const caseStride = valueStrides(parameterTypes); - for (let caseIdx = 0; caseIdx < cases.length; caseIdx++) { - const caseBase = caseIdx * caseStride; - let offset = caseBase; - for (let paramIdx = 0; paramIdx < parameterTypes.length; paramIdx++) { - const params = cases[caseIdx].input; - if (params instanceof Array) { - params[paramIdx].copyTo(inputData, offset); - } else { - params.copyTo(inputData, offset); - } - offset += valueStride(parameterTypes[paramIdx]); + for (let caseIdx = 0; caseIdx < cases.length; caseIdx++) { + const offset = caseIdx * caseStride; + structLayout(parameterTypes, inputSource, m => { + const arg = cases[caseIdx].input; + if (arg instanceof Array) { + arg[m.index].copyTo(inputData, offset + m.offset); + } else { + arg.copyTo(inputData, offset + m.offset); } - } + }); } // build the compute pipeline, if the shader hasn't been compiled already. diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index b28c1c9e822c..15557b3d4c57 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -6,6 +6,7 @@ import { Float16Array } from '../../external/petamoriken/float16/float16.js'; import BinaryStream from './binary_stream.js'; import { kBit } from './constants.js'; import { + align, cartesianProduct, clamp, correctlyRoundedF16, @@ -627,6 +628,8 @@ export class ScalarType { switch (this.kind) { case 'abstract-float': return abstractFloat(value); + case 'abstract-int': + return abstractInt(BigInt(value)); case 'f64': return f64(value); case 'f32': @@ -836,9 +839,10 @@ export class ArrayType { */ public read(buf: Uint8Array, offset: number): ArrayValue { const elements: Array = []; + for (let i = 0; i < this.count; i++) { elements[i] = this.elementType.read(buf, offset); - offset += this.elementType.size; + offset += this.stride; } return new ArrayValue(elements); } @@ -847,8 +851,12 @@ export class ArrayType { return `array<${this.elementType}, ${this.count}>`; } + public get stride(): number { + return align(this.elementType.size, this.elementType.alignment); + } + public get size(): number { - return this.elementType.alignment * this.count; + return this.stride * this.count; } public get alignment(): number { @@ -914,6 +922,7 @@ export type Type = ScalarType | VectorType | MatrixType | ArrayType; /** Type holds pre-declared Types along with helper constructor functions. */ export const Type = { abstractInt: abstractIntType, + 'abstract-int': abstractIntType, i32: i32Type, u32: u32Type, i16: i16Type, @@ -922,6 +931,7 @@ export const Type = { u8: u8Type, abstractFloat: abstractFloatType, + 'abstract-float': abstractFloatType, f64: f64Type, f32: f32Type, f16: f16Type, @@ -1878,7 +1888,7 @@ export class ArrayValue { } /** - * Copies the matrix value to the Uint8Array buffer at the provided byte offset. + * Copies the array value to the Uint8Array buffer at the provided byte offset. * @param buffer the destination buffer * @param offset the byte offset within buffer */ @@ -1890,11 +1900,11 @@ export class ArrayValue { } /** - * @returns the WGSL representation of this matrix value + * @returns the WGSL representation of this array value */ public wgsl(): string { const els = this.elements.map(r => r.wgsl()).join(', '); - return `${this.type}(${els})`; + return isAbstractType(this.type.elementType) ? `array(${els})` : `${this.type}(${els})`; } public toString(): string { @@ -1902,6 +1912,11 @@ export class ArrayValue { } } +/** Helper for constructing an ArrayValue with the provided values */ +export function array(...elements: Value[]) { + return new ArrayValue(elements); +} + /** * Helper for constructing Matrices from arrays of numbers * From b9a1823c418d617106fb1ff51804facfbbd5d1be Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Fri, 8 Mar 2024 17:32:13 +0000 Subject: [PATCH 16/33] af_matrix_matrix_mul: Remove 'selectDeviceOrSkipTestCase()' for 'shader-f16' f16 isn't used in this test, so don't skip these if the device does not support f16. --- .../expression/binary/af_matrix_matrix_multiplication.spec.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts index a1aa005d341c..9320fb1226bf 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts @@ -27,9 +27,6 @@ Accuracy: Correctly rounded .combine('x_rows', [2, 3, 4] as const) .combine('y_cols', [2, 3, 4] as const) ) - .beforeAllSubcases(t => { - t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] }); - }) .fn(async t => { const x_cols = t.params.common_dim; const x_rows = t.params.x_rows; From 19fc59b1a69a6d7894482da980eaa8cfefffcf6b Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Thu, 7 Mar 2024 11:00:43 +0000 Subject: [PATCH 17/33] Begin basic array indexing tests. --- src/webgpu/listing_meta.json | 5 +- .../expression/access/index/array.spec.ts | 199 ++++++++++++++++++ 2 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 src/webgpu/shader/execution/expression/access/index/array.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index fd2ef2c147a6..db0c24be2c78 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -899,8 +899,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,access,index,array:scalar:*": { "subcaseMS": 657.148 }, - "webgpu:shader,execution,expression,access,index,array:vector:*": { "subcaseMS": 1089.448 }, + "webgpu:shader,execution,expression,access,index,array:abstract_scalar:*": { "subcaseMS": 189.344 }, + "webgpu:shader,execution,expression,access,index,array:concrete_scalar:*": { "subcaseMS": 989.619 }, + "webgpu:shader,execution,expression,access,index,array:vector:*": { "subcaseMS": 1546.461 }, "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/access/index/array.spec.ts b/src/webgpu/shader/execution/expression/access/index/array.spec.ts new file mode 100644 index 000000000000..6cf84c979da9 --- /dev/null +++ b/src/webgpu/shader/execution/expression/access/index/array.spec.ts @@ -0,0 +1,199 @@ +export const description = ` +Execution Tests for array indexing expressions +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { Type, array, f32 } from '../../../../../util/conversion.js'; +import { Case } from '../../case.js'; +import { allInputSources, basicExpressionBuilder, run } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('concrete_scalar') + .specURL('https://www.w3.org/TR/WGSL/#array-access-expr') + .desc(`Test indexing of an array of concrete scalars`) + .params(u => + u + .combine( + 'inputSource', + // 'uniform' address space requires array stride to be multiple of 16 bytes + allInputSources.filter(s => s !== 'uniform') + ) + .combine('elementType', ['i32', 'u32', 'f32', 'f16'] as const) + .combine('indexType', ['i32', 'u32'] as const) + ) + .beforeAllSubcases(t => { + if (t.params.elementType === 'f16') { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(async t => { + const elementType = Type[t.params.elementType]; + const indexType = Type[t.params.indexType]; + const cases: Case[] = [ + { + input: [ + array( + /* 0 */ elementType.create(10), + /* 1 */ elementType.create(11), + /* 2 */ elementType.create(12) + ), + indexType.create(0), + ], + expected: elementType.create(10), + }, + { + input: [ + array( + /* 0 */ elementType.create(20), + /* 1 */ elementType.create(21), + /* 2 */ elementType.create(22) + ), + indexType.create(1), + ], + expected: elementType.create(21), + }, + { + input: [ + array( + /* 0 */ elementType.create(30), + /* 1 */ elementType.create(31), + /* 2 */ elementType.create(32) + ), + indexType.create(2), + ], + expected: elementType.create(32), + }, + ]; + await run( + t, + basicExpressionBuilder(ops => `${ops[0]}[${ops[1]}]`), + [Type.array(3, elementType), indexType], + elementType, + t.params, + cases + ); + }); + +g.test('abstract_scalar') + .specURL('https://www.w3.org/TR/WGSL/#array-access-expr') + .desc(`Test indexing of an array of scalars`) + .params(u => + u + .combine('elementType', ['abstract-int', 'abstract-float'] as const) + .combine('indexType', ['i32', 'u32'] as const) + ) + .fn(async t => { + const elementType = Type[t.params.elementType]; + const indexType = Type[t.params.indexType]; + const cases: Case[] = [ + { + input: [ + array( + /* 0 */ elementType.create(0x10_00000000), + /* 1 */ elementType.create(0x11_00000000), + /* 2 */ elementType.create(0x12_00000000) + ), + indexType.create(0), + ], + expected: f32(0x10), + }, + { + input: [ + array( + /* 0 */ elementType.create(0x20_00000000), + /* 1 */ elementType.create(0x21_00000000), + /* 2 */ elementType.create(0x22_00000000) + ), + indexType.create(1), + ], + expected: f32(0x21), + }, + { + input: [ + array( + /* 0 */ elementType.create(0x30_00000000), + /* 1 */ elementType.create(0x31_00000000), + /* 2 */ elementType.create(0x32_00000000) + ), + indexType.create(2), + ], + expected: f32(0x32), + }, + ]; + await run( + t, + basicExpressionBuilder(ops => `${ops[0]}[${ops[1]}] / 0x100000000`), + [Type.array(3, elementType), indexType], + Type.f32, + { inputSource: 'const' }, + cases + ); + }); + +g.test('vector') + .specURL('https://www.w3.org/TR/WGSL/#array-access-expr') + .desc(`Test indexing of an array of vectors`) + .params(u => + u + .combine('inputSource', allInputSources) + .expand('elementType', t => + t.inputSource === 'uniform' + ? (['vec4i', 'vec4u', 'vec4f'] as const) + : (['vec4i', 'vec4u', 'vec4f', 'vec4h'] as const) + ) + .combine('indexType', ['i32', 'u32'] as const) + ) + .beforeAllSubcases(t => { + if (t.params.elementType === 'vec4h') { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(async t => { + const elementType = Type[t.params.elementType]; + const indexType = Type[t.params.indexType]; + const cases: Case[] = [ + { + input: [ + array( + /* 0 */ elementType.create([0x10, 0x11, 0x12, 0x13]), + /* 1 */ elementType.create([0x14, 0x15, 0x16, 0x17]), + /* 2 */ elementType.create([0x18, 0x19, 0x1a, 0x1b]) + ), + indexType.create(0), + ], + expected: elementType.create([0x10, 0x11, 0x12, 0x13]), + }, + { + input: [ + array( + /* 0 */ elementType.create([0x20, 0x21, 0x22, 0x23]), + /* 1 */ elementType.create([0x24, 0x25, 0x26, 0x27]), + /* 2 */ elementType.create([0x28, 0x29, 0x2a, 0x2b]) + ), + indexType.create(1), + ], + expected: elementType.create([0x24, 0x25, 0x26, 0x27]), + }, + { + input: [ + array( + /* 0 */ elementType.create([0x30, 0x31, 0x32, 0x33]), + /* 1 */ elementType.create([0x34, 0x35, 0x36, 0x37]), + /* 2 */ elementType.create([0x38, 0x39, 0x3a, 0x3b]) + ), + indexType.create(2), + ], + expected: elementType.create([0x38, 0x39, 0x3a, 0x3b]), + }, + ]; + await run( + t, + basicExpressionBuilder(ops => `${ops[0]}[${ops[1]}]`), + [Type.array(3, elementType), indexType], + elementType, + t.params, + cases + ); + }); From f9f6c90b8044589b908e9396b09231b778e35484 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Fri, 8 Mar 2024 22:09:19 +0000 Subject: [PATCH 18/33] Begin basic vector indexing tests. --- src/webgpu/listing_meta.json | 2 + .../expression/access/index/vector.spec.ts | 83 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/webgpu/shader/execution/expression/access/index/vector.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index db0c24be2c78..887bbeffe2bf 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -902,6 +902,8 @@ "webgpu:shader,execution,expression,access,index,array:abstract_scalar:*": { "subcaseMS": 189.344 }, "webgpu:shader,execution,expression,access,index,array:concrete_scalar:*": { "subcaseMS": 989.619 }, "webgpu:shader,execution,expression,access,index,array:vector:*": { "subcaseMS": 1546.461 }, + "webgpu:shader,execution,expression,access,index,vector:abstract_scalar:*": { "subcaseMS": 213.529 }, + "webgpu:shader,execution,expression,access,index,vector:concrete_scalar:*": { "subcaseMS": 1918.096 }, "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/access/index/vector.spec.ts b/src/webgpu/shader/execution/expression/access/index/vector.spec.ts new file mode 100644 index 000000000000..034ac5a95f43 --- /dev/null +++ b/src/webgpu/shader/execution/expression/access/index/vector.spec.ts @@ -0,0 +1,83 @@ +export const description = ` +Execution Tests for vector indexing expressions +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { ScalarValue, Type, VectorValue, f32 } from '../../../../../util/conversion.js'; +import { Case } from '../../case.js'; +import { allInputSources, basicExpressionBuilder, run } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('concrete_scalar') + .specURL('https://www.w3.org/TR/WGSL/#vector-access-expr') + .desc(`Test indexing of an array of concrete scalars`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('elementType', ['i32', 'u32', 'f32', 'f16'] as const) + .combine('indexType', ['i32', 'u32'] as const) + .combine('width', [2, 3, 4] as const) + ) + .beforeAllSubcases(t => { + if (t.params.elementType === 'f16') { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(async t => { + const elementType = Type[t.params.elementType]; + const indexType = Type[t.params.indexType]; + const vectorType = Type.vec(t.params.width, elementType); + const elements: ScalarValue[] = []; + for (let i = 0; i < t.params.width; i++) { + elements.push(elementType.create((i + 1) * 10)); + } + const vector = new VectorValue(elements); + const cases: Case[] = []; + for (let i = 0; i < t.params.width; i++) { + cases.push({ input: [vector, indexType.create(i)], expected: elements[i] }); + } + + await run( + t, + basicExpressionBuilder(ops => `${ops[0]}[${ops[1]}]`), + [vectorType, indexType], + elementType, + t.params, + cases + ); + }); + +g.test('abstract_scalar') + .specURL('https://www.w3.org/TR/WGSL/#vector-access-expr') + .desc(`Test indexing of an array of scalars`) + .params(u => + u + .combine('elementType', ['abstract-int', 'abstract-float'] as const) + .combine('indexType', ['i32', 'u32'] as const) + .combine('width', [2, 3, 4] as const) + ) + .fn(async t => { + const elementType = Type[t.params.elementType]; + const indexType = Type[t.params.indexType]; + const vectorType = Type.vec(t.params.width, elementType); + const elements: ScalarValue[] = []; + for (let i = 0; i < t.params.width; i++) { + elements.push(elementType.create((i + 1) * 0x100000000)); + } + const vector = new VectorValue(elements); + const cases: Case[] = []; + for (let i = 0; i < t.params.width; i++) { + cases.push({ input: [vector, indexType.create(i)], expected: f32(i + 1) }); + } + + await run( + t, + basicExpressionBuilder(ops => `${ops[0]}[${ops[1]}] / 0x100000000`), + [vectorType, indexType], + Type.f32, + { inputSource: 'const' }, + cases + ); + }); From 5ad758908a36665d48cf69a209fe7fa85e07fac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beaufort?= Date: Mon, 11 Mar 2024 20:54:26 +0100 Subject: [PATCH 19/33] Add service worker support (#3419) * Generate Web Worker script for every test file This will be needed to launch Service Workers, which cannot be generated at runtime and cannot use dynamic import(). * Add service worker support * Update wpt.ts * Run tests * Address kainino0x feedback * Address feedback | part 2 * Address feedback | part 3 * Address feedback | part 4 * Address feedback | part 5 * Address feedback | part 6 * Address feedback | part 7 * Address feedback | part 8 * Apply suggestions from code review * use WorkerTestRunRequest in the postMessage/onmessage interface * Clean up resolvers map * Use express routing for .worker.js * Skip worker tests when run in a worker that can't support them DedicatedWorker can be created from DedicatedWorker, but none of the other nested worker pairs are allowed. * Clean up all service workers on startup and shutdown * Avoid reinitializing service workers for every single case * lint fixes * Catch errors in wrapTestGroupForWorker onMessage * Make sure the service worker has the correct URL --------- Co-authored-by: Kai Ninomiya --- Gruntfile.js | 10 +- docs/intro/developing.md | 1 + docs/terms.md | 2 +- src/common/internal/query/compare.ts | 5 +- src/common/internal/test_suite_listing.ts | 2 +- src/common/runtime/helper/options.ts | 3 +- .../runtime/helper/test_worker-worker.ts | 27 +-- src/common/runtime/helper/test_worker.ts | 152 ++++++++---- src/common/runtime/helper/utils_worker.ts | 34 +++ src/common/runtime/helper/wrap_for_worker.ts | 43 ++++ src/common/runtime/standalone.ts | 5 +- src/common/runtime/wpt.ts | 5 +- src/common/tools/dev_server.ts | 13 ++ src/common/tools/gen_listings.ts | 56 ----- .../tools/gen_listings_and_webworkers.ts | 90 ++++++++ src/demo/webworker/.gitignore | 2 + src/manual/webworker/.gitignore | 2 + src/resources/cache/hashes.json | 216 +++++++++--------- src/stress/webworker/.gitignore | 2 + src/unittests/webworker/.gitignore | 2 + src/webgpu/listing_meta.json | 1 + src/webgpu/web_platform/worker/worker.spec.ts | 59 +++-- src/webgpu/web_platform/worker/worker.ts | 2 +- .../web_platform/worker/worker_launcher.ts | 40 ++++ src/webgpu/webworker/.gitignore | 2 + tools/gen_listings | 7 - tools/gen_listings_and_webworkers | 8 + 27 files changed, 512 insertions(+), 279 deletions(-) create mode 100644 src/common/runtime/helper/utils_worker.ts create mode 100644 src/common/runtime/helper/wrap_for_worker.ts delete mode 100644 src/common/tools/gen_listings.ts create mode 100644 src/common/tools/gen_listings_and_webworkers.ts create mode 100644 src/demo/webworker/.gitignore create mode 100644 src/manual/webworker/.gitignore create mode 100644 src/stress/webworker/.gitignore create mode 100644 src/unittests/webworker/.gitignore create mode 100644 src/webgpu/webworker/.gitignore delete mode 100755 tools/gen_listings create mode 100755 tools/gen_listings_and_webworkers diff --git a/Gruntfile.js b/Gruntfile.js index c354249f8b69..eba38704f3ee 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -24,9 +24,9 @@ module.exports = function (grunt) { cmd: 'node', args: ['tools/gen_version'], }, - 'generate-listings': { + 'generate-listings-and-webworkers': { cmd: 'node', - args: ['tools/gen_listings', 'gen/', ...kAllSuites.map(s => 'src/' + s)], + args: ['tools/gen_listings_and_webworkers', 'gen/', ...kAllSuites.map(s => 'src/' + s)], }, validate: { cmd: 'node', @@ -159,14 +159,14 @@ module.exports = function (grunt) { // Must run after generate-common and run:build-out. files: [ { expand: true, dest: 'out/', cwd: 'gen', src: 'common/internal/version.js' }, - { expand: true, dest: 'out/', cwd: 'gen', src: '*/listing.js' }, + { expand: true, dest: 'out/', cwd: 'gen', src: '*/**/*.js' }, ], }, 'gen-to-out-wpt': { // Must run after generate-common and run:build-out-wpt. files: [ { expand: true, dest: 'out-wpt/', cwd: 'gen', src: 'common/internal/version.js' }, - { expand: true, dest: 'out-wpt/', cwd: 'gen', src: 'webgpu/listing.js' }, + { expand: true, dest: 'out-wpt/', cwd: 'gen', src: 'webgpu/**/*.js' }, ], }, 'htmlfiles-to-out': { @@ -243,7 +243,7 @@ module.exports = function (grunt) { grunt.registerTask('generate-common', 'Generate files into gen/ and src/', [ 'run:generate-version', - 'run:generate-listings', + 'run:generate-listings-and-webworkers', 'run:generate-cache', ]); grunt.registerTask('build-standalone', 'Build out/ (no checks; run after generate-common)', [ diff --git a/docs/intro/developing.md b/docs/intro/developing.md index d8869f985b31..23da946b1e0c 100644 --- a/docs/intro/developing.md +++ b/docs/intro/developing.md @@ -58,6 +58,7 @@ The following url parameters change how the harness runs: - `debug=1` enables verbose debug logging from tests. - `worker=dedicated` runs the tests on a dedicated worker instead of the main thread. - `worker=shared` runs the tests on a shared worker instead of the main thread. +- `worker=service` runs the tests on a service worker instead of the main thread. - `power_preference=low-power` runs most tests passing `powerPreference: low-power` to `requestAdapter` - `power_preference=high-performance` runs most tests passing `powerPreference: high-performance` to `requestAdapter` diff --git a/docs/terms.md b/docs/terms.md index 032639be577a..0dc6f0ca1701 100644 --- a/docs/terms.md +++ b/docs/terms.md @@ -111,7 +111,7 @@ Each Suite has one Listing File (`suite/listing.[tj]s`), containing a list of th in the suite. In `src/suite/listing.ts`, this is computed dynamically. -In `out/suite/listing.js`, the listing has been pre-baked (by `tools/gen_listings`). +In `out/suite/listing.js`, the listing has been pre-baked (by `tools/gen_listings_and_webworkers`). **Type:** Once `import`ed, `ListingFile` diff --git a/src/common/internal/query/compare.ts b/src/common/internal/query/compare.ts index a9419b87c196..f49833f5a2e0 100644 --- a/src/common/internal/query/compare.ts +++ b/src/common/internal/query/compare.ts @@ -58,7 +58,10 @@ function compareOneLevel(ordering: Ordering, aIsBig: boolean, bIsBig: boolean): return Ordering.Unordered; } -function comparePaths(a: readonly string[], b: readonly string[]): Ordering { +/** + * Compare two file paths, or file-local test paths, returning an Ordering between the two. + */ +export function comparePaths(a: readonly string[], b: readonly string[]): Ordering { const shorter = Math.min(a.length, b.length); for (let i = 0; i < shorter; ++i) { diff --git a/src/common/internal/test_suite_listing.ts b/src/common/internal/test_suite_listing.ts index 2d2b555366e4..c5a0e1144839 100644 --- a/src/common/internal/test_suite_listing.ts +++ b/src/common/internal/test_suite_listing.ts @@ -1,6 +1,6 @@ // A listing of all specs within a single suite. This is the (awaited) type of // `groups` in '{cts,unittests}/listing.ts' and `listing` in the auto-generated -// 'out/{cts,unittests}/listing.js' files (see tools/gen_listings). +// 'out/{cts,unittests}/listing.js' files (see tools/gen_listings_and_webworkers). export type TestSuiteListing = TestSuiteListingEntry[]; export type TestSuiteListingEntry = TestSuiteListingEntrySpec | TestSuiteListingEntryReadme; diff --git a/src/common/runtime/helper/options.ts b/src/common/runtime/helper/options.ts index 60c2d1069181..67fd00372d39 100644 --- a/src/common/runtime/helper/options.ts +++ b/src/common/runtime/helper/options.ts @@ -25,7 +25,7 @@ export function optionString( * The possible options for the tests. */ export interface CTSOptions { - worker?: 'dedicated' | 'shared' | ''; + worker?: 'dedicated' | 'shared' | 'service' | ''; debug: boolean; compatibility: boolean; forceFallbackAdapter: boolean; @@ -68,6 +68,7 @@ export const kCTSOptionsInfo: OptionsInfos = { { value: '', description: 'no worker' }, { value: 'dedicated', description: 'dedicated worker' }, { value: 'shared', description: 'shared worker' }, + { value: 'service', description: 'service worker' }, ], }, debug: { description: 'show more info' }, diff --git a/src/common/runtime/helper/test_worker-worker.ts b/src/common/runtime/helper/test_worker-worker.ts index caf6e7a1bc17..cb31f4d26388 100644 --- a/src/common/runtime/helper/test_worker-worker.ts +++ b/src/common/runtime/helper/test_worker-worker.ts @@ -1,13 +1,9 @@ import { setBaseResourcePath } from '../../framework/resources.js'; -import { globalTestConfig } from '../../framework/test_config.js'; import { DefaultTestFileLoader } from '../../internal/file_loader.js'; -import { Logger } from '../../internal/logging/logger.js'; import { parseQuery } from '../../internal/query/parseQuery.js'; -import { TestQueryWithExpectation } from '../../internal/query/query.js'; -import { setDefaultRequestAdapterOptions } from '../../util/navigator_gpu.js'; import { assert } from '../../util/util.js'; -import { CTSOptions } from './options.js'; +import { setupWorkerEnvironment, WorkerTestRunRequest } from './utils_worker.js'; // Should be WorkerGlobalScope, but importing lib "webworker" conflicts with lib "dom". /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -18,24 +14,9 @@ const loader = new DefaultTestFileLoader(); setBaseResourcePath('../../../resources'); async function reportTestResults(this: MessagePort | Worker, ev: MessageEvent) { - const query: string = ev.data.query; - const expectations: TestQueryWithExpectation[] = ev.data.expectations; - const ctsOptions: CTSOptions = ev.data.ctsOptions; + const { query, expectations, ctsOptions } = ev.data as WorkerTestRunRequest; - const { debug, unrollConstEvalLoops, powerPreference, compatibility } = ctsOptions; - globalTestConfig.unrollConstEvalLoops = unrollConstEvalLoops; - globalTestConfig.compatibility = compatibility; - - Logger.globalDebugMode = debug; - const log = new Logger(); - - if (powerPreference || compatibility) { - setDefaultRequestAdapterOptions({ - ...(powerPreference && { powerPreference }), - // MAINTENANCE_TODO: Change this to whatever the option ends up being - ...(compatibility && { compatibilityMode: true }), - }); - } + const log = setupWorkerEnvironment(ctsOptions); const testcases = Array.from(await loader.loadCases(parseQuery(query))); assert(testcases.length === 1, 'worker query resulted in != 1 cases'); @@ -48,7 +29,7 @@ async function reportTestResults(this: MessagePort | Worker, ev: MessageEvent) { } self.onmessage = (ev: MessageEvent) => { - void reportTestResults.call(self, ev); + void reportTestResults.call(ev.source || self, ev); }; self.onconnect = (event: MessageEvent) => { diff --git a/src/common/runtime/helper/test_worker.ts b/src/common/runtime/helper/test_worker.ts index 9e2ec17efa95..42848106b68e 100644 --- a/src/common/runtime/helper/test_worker.ts +++ b/src/common/runtime/helper/test_worker.ts @@ -2,80 +2,121 @@ import { LogMessageWithStack } from '../../internal/logging/log_message.js'; import { TransferredTestCaseResult, LiveTestCaseResult } from '../../internal/logging/result.js'; import { TestCaseRecorder } from '../../internal/logging/test_case_recorder.js'; import { TestQueryWithExpectation } from '../../internal/query/query.js'; +import { timeout } from '../../util/timeout.js'; +import { assert } from '../../util/util.js'; import { CTSOptions, kDefaultCTSOptions } from './options.js'; +import { WorkerTestRunRequest } from './utils_worker.js'; -export class TestDedicatedWorker { - private readonly ctsOptions: CTSOptions; - private readonly worker: Worker; - private readonly resolvers = new Map void>(); +/** Query all currently-registered service workers, and unregister them. */ +function unregisterAllServiceWorkers() { + void navigator.serviceWorker.getRegistrations().then(registrations => { + for (const registration of registrations) { + void registration.unregister(); + } + }); +} - constructor(ctsOptions?: CTSOptions) { - this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker: 'dedicated' } }; - const selfPath = import.meta.url; - const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/')); - const workerPath = selfPathDir + '/test_worker-worker.js'; - this.worker = new Worker(workerPath, { type: 'module' }); - this.worker.onmessage = ev => { - const query: string = ev.data.query; - const result: TransferredTestCaseResult = ev.data.result; - if (result.logs) { - for (const l of result.logs) { - Object.setPrototypeOf(l, LogMessageWithStack.prototype); - } +// NOTE: This code runs on startup for any runtime with worker support. Here, we use that chance to +// delete any leaked service workers, and register to clean up after ourselves at shutdown. +unregisterAllServiceWorkers(); +window.addEventListener('beforeunload', () => { + unregisterAllServiceWorkers(); +}); + +class TestBaseWorker { + protected readonly ctsOptions: CTSOptions; + protected readonly resolvers = new Map void>(); + + constructor(worker: CTSOptions['worker'], ctsOptions?: CTSOptions) { + this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker } }; + } + + onmessage(ev: MessageEvent) { + const query: string = ev.data.query; + const result: TransferredTestCaseResult = ev.data.result; + if (result.logs) { + for (const l of result.logs) { + Object.setPrototypeOf(l, LogMessageWithStack.prototype); } - this.resolvers.get(query)!(result as LiveTestCaseResult); + } + this.resolvers.get(query)!(result as LiveTestCaseResult); + this.resolvers.delete(query); - // MAINTENANCE_TODO(kainino0x): update the Logger with this result (or don't have a logger and - // update the entire results JSON somehow at some point). - }; + // MAINTENANCE_TODO(kainino0x): update the Logger with this result (or don't have a logger and + // update the entire results JSON somehow at some point). } - async run( + async makeRequestAndRecordResult( + target: MessagePort | Worker | ServiceWorker, rec: TestCaseRecorder, query: string, - expectations: TestQueryWithExpectation[] = [] - ): Promise { - this.worker.postMessage({ + expectations: TestQueryWithExpectation[] + ) { + const request: WorkerTestRunRequest = { query, expectations, ctsOptions: this.ctsOptions, - }); + }; + target.postMessage(request); + const workerResult = await new Promise(resolve => { + assert(!this.resolvers.has(query), "can't request same query twice simultaneously"); this.resolvers.set(query, resolve); }); rec.injectResult(workerResult); } } +export class TestDedicatedWorker extends TestBaseWorker { + private readonly worker: Worker; + + constructor(ctsOptions?: CTSOptions) { + super('dedicated', ctsOptions); + const selfPath = import.meta.url; + const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/')); + const workerPath = selfPathDir + '/test_worker-worker.js'; + this.worker = new Worker(workerPath, { type: 'module' }); + this.worker.onmessage = ev => this.onmessage(ev); + } + + async run( + rec: TestCaseRecorder, + query: string, + expectations: TestQueryWithExpectation[] = [] + ): Promise { + await this.makeRequestAndRecordResult(this.worker, rec, query, expectations); + } +} + export class TestWorker extends TestDedicatedWorker {} -export class TestSharedWorker { - private readonly ctsOptions: CTSOptions; +export class TestSharedWorker extends TestBaseWorker { private readonly port: MessagePort; - private readonly resolvers = new Map void>(); constructor(ctsOptions?: CTSOptions) { - this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker: 'shared' } }; + super('shared', ctsOptions); const selfPath = import.meta.url; const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/')); const workerPath = selfPathDir + '/test_worker-worker.js'; const worker = new SharedWorker(workerPath, { type: 'module' }); this.port = worker.port; this.port.start(); - this.port.onmessage = ev => { - const query: string = ev.data.query; - const result: TransferredTestCaseResult = ev.data.result; - if (result.logs) { - for (const l of result.logs) { - Object.setPrototypeOf(l, LogMessageWithStack.prototype); - } - } - this.resolvers.get(query)!(result as LiveTestCaseResult); + this.port.onmessage = ev => this.onmessage(ev); + } - // MAINTENANCE_TODO(kainino0x): update the Logger with this result (or don't have a logger and - // update the entire results JSON somehow at some point). - }; + async run( + rec: TestCaseRecorder, + query: string, + expectations: TestQueryWithExpectation[] = [] + ): Promise { + await this.makeRequestAndRecordResult(this.port, rec, query, expectations); + } +} + +export class TestServiceWorker extends TestBaseWorker { + constructor(ctsOptions?: CTSOptions) { + super('service', ctsOptions); } async run( @@ -83,14 +124,25 @@ export class TestSharedWorker { query: string, expectations: TestQueryWithExpectation[] = [] ): Promise { - this.port.postMessage({ - query, - expectations, - ctsOptions: this.ctsOptions, - }); - const workerResult = await new Promise(resolve => { - this.resolvers.set(query, resolve); + const [suite, name] = query.split(':', 2); + const fileName = name.split(',').join('/'); + const serviceWorkerURL = new URL( + `/out/${suite}/webworker/${fileName}.worker.js`, + window.location.href + ).toString(); + + // If a registration already exists for this path, it will be ignored. + const registration = await navigator.serviceWorker.register(serviceWorkerURL, { + type: 'module', }); - rec.injectResult(workerResult); + // Make sure the registration we just requested is active. (We don't worry about it being + // outdated from a previous page load, because we wipe all service workers on shutdown/startup.) + while (!registration.active || registration.active.scriptURL !== serviceWorkerURL) { + await new Promise(resolve => timeout(resolve, 0)); + } + const serviceWorker = registration.active; + + navigator.serviceWorker.onmessage = ev => this.onmessage(ev); + await this.makeRequestAndRecordResult(serviceWorker, rec, query, expectations); } } diff --git a/src/common/runtime/helper/utils_worker.ts b/src/common/runtime/helper/utils_worker.ts new file mode 100644 index 000000000000..c8ca6a2a3a0a --- /dev/null +++ b/src/common/runtime/helper/utils_worker.ts @@ -0,0 +1,34 @@ +import { globalTestConfig } from '../../framework/test_config.js'; +import { Logger } from '../../internal/logging/logger.js'; +import { TestQueryWithExpectation } from '../../internal/query/query.js'; +import { setDefaultRequestAdapterOptions } from '../../util/navigator_gpu.js'; + +import { CTSOptions } from './options.js'; + +export interface WorkerTestRunRequest { + query: string; + expectations: TestQueryWithExpectation[]; + ctsOptions: CTSOptions; +} + +/** + * Set config environment for workers with ctsOptions and return a Logger. + */ +export function setupWorkerEnvironment(ctsOptions: CTSOptions): Logger { + const { debug, unrollConstEvalLoops, powerPreference, compatibility } = ctsOptions; + globalTestConfig.unrollConstEvalLoops = unrollConstEvalLoops; + globalTestConfig.compatibility = compatibility; + + Logger.globalDebugMode = debug; + const log = new Logger(); + + if (powerPreference || compatibility) { + setDefaultRequestAdapterOptions({ + ...(powerPreference && { powerPreference }), + // MAINTENANCE_TODO: Change this to whatever the option ends up being + ...(compatibility && { compatibilityMode: true }), + }); + } + + return log; +} diff --git a/src/common/runtime/helper/wrap_for_worker.ts b/src/common/runtime/helper/wrap_for_worker.ts new file mode 100644 index 000000000000..8e206f8399ab --- /dev/null +++ b/src/common/runtime/helper/wrap_for_worker.ts @@ -0,0 +1,43 @@ +import { Fixture } from '../../framework/fixture'; +import { LogMessageWithStack } from '../../internal/logging/log_message.js'; +import { comparePaths, comparePublicParamsPaths, Ordering } from '../../internal/query/compare.js'; +import { parseQuery } from '../../internal/query/parseQuery.js'; +import { TestQuerySingleCase } from '../../internal/query/query.js'; +import { TestGroup } from '../../internal/test_group.js'; +import { assert } from '../../util/util.js'; + +import { setupWorkerEnvironment, WorkerTestRunRequest } from './utils_worker.js'; + +export function wrapTestGroupForWorker(g: TestGroup) { + self.onmessage = async (ev: MessageEvent) => { + const { query, expectations, ctsOptions } = ev.data as WorkerTestRunRequest; + try { + const log = setupWorkerEnvironment(ctsOptions); + + const testQuery = parseQuery(query); + assert(testQuery instanceof TestQuerySingleCase); + let testcase = null; + for (const t of g.iterate()) { + if (comparePaths(t.testPath, testQuery.testPathParts) !== Ordering.Equal) { + continue; + } + for (const c of t.iterate(testQuery.params)) { + if (comparePublicParamsPaths(c.id.params, testQuery.params) === Ordering.Equal) { + testcase = c; + } + } + } + assert(!!testcase, 'testcase not found'); + const [rec, result] = log.record(query); + await testcase.run(rec, testQuery, expectations); + + ev.source?.postMessage({ query, result }); + } catch (thrown) { + const ex = thrown instanceof Error ? thrown : new Error(`${thrown}`); + ev.source?.postMessage({ + query, + result: { status: 'fail', timems: 0, logs: [new LogMessageWithStack('INTERNAL', ex)] }, + }); + } + }; +} diff --git a/src/common/runtime/standalone.ts b/src/common/runtime/standalone.ts index fa938c55a276..62090ee5dc92 100644 --- a/src/common/runtime/standalone.ts +++ b/src/common/runtime/standalone.ts @@ -21,7 +21,7 @@ import { OptionsInfos, camelCaseToSnakeCase, } from './helper/options.js'; -import { TestDedicatedWorker, TestSharedWorker } from './helper/test_worker.js'; +import { TestDedicatedWorker, TestSharedWorker, TestServiceWorker } from './helper/test_worker.js'; const rootQuerySpec = 'webgpu:*'; let promptBeforeReload = false; @@ -66,6 +66,7 @@ setBaseResourcePath('../out/resources'); const dedicatedWorker = options.worker === 'dedicated' ? new TestDedicatedWorker(options) : undefined; const sharedWorker = options.worker === 'shared' ? new TestSharedWorker(options) : undefined; +const serviceWorker = options.worker === 'service' ? new TestServiceWorker(options) : undefined; const autoCloseOnPass = document.getElementById('autoCloseOnPass') as HTMLInputElement; const resultsVis = document.getElementById('resultsVis')!; @@ -182,6 +183,8 @@ function makeCaseHTML(t: TestTreeLeaf): VisualizedSubtree { await dedicatedWorker.run(rec, name); } else if (sharedWorker) { await sharedWorker.run(rec, name); + } else if (serviceWorker) { + await serviceWorker.run(rec, name); } else { await t.run(rec); } diff --git a/src/common/runtime/wpt.ts b/src/common/runtime/wpt.ts index aacd34b13e51..56ff9649e045 100644 --- a/src/common/runtime/wpt.ts +++ b/src/common/runtime/wpt.ts @@ -9,7 +9,7 @@ import { parseExpectationsForTestQuery, relativeQueryString } from '../internal/ import { assert } from '../util/util.js'; import { optionEnabled, optionString } from './helper/options.js'; -import { TestDedicatedWorker, TestSharedWorker } from './helper/test_worker.js'; +import { TestDedicatedWorker, TestServiceWorker, TestSharedWorker } from './helper/test_worker.js'; // testharness.js API (https://web-platform-tests.org/writing-tests/testharness-api.html) declare interface WptTestObject { @@ -34,6 +34,7 @@ void (async () => { const workerString = optionString('worker'); const dedicatedWorker = workerString === 'dedicated' ? new TestDedicatedWorker() : undefined; const sharedWorker = workerString === 'shared' ? new TestSharedWorker() : undefined; + const serviceWorker = workerString === 'service' ? new TestServiceWorker() : undefined; globalTestConfig.unrollConstEvalLoops = optionEnabled('unroll_const_eval_loops'); @@ -68,6 +69,8 @@ void (async () => { await dedicatedWorker.run(rec, name, expectations); } else if (sharedWorker) { await sharedWorker.run(rec, name, expectations); + } else if (serviceWorker) { + await serviceWorker.run(rec, name, expectations); } else { await testcase.run(rec, expectations); } diff --git a/src/common/tools/dev_server.ts b/src/common/tools/dev_server.ts index 57cb6a7ea4f6..8e0e3bdbe656 100644 --- a/src/common/tools/dev_server.ts +++ b/src/common/tools/dev_server.ts @@ -144,6 +144,19 @@ app.get('/out/:suite([a-zA-Z0-9_-]+)/listing.js', async (req, res, next) => { } }); +// Serve .worker.js files by generating the necessary wrapper. +app.get('/out/:suite([a-zA-Z0-9_-]+)/webworker/:filepath(*).worker.js', (req, res, next) => { + const { suite, filepath } = req.params; + const result = `\ +import { g } from '/out/${suite}/${filepath}.spec.js'; +import { wrapTestGroupForWorker } from '/out/common/runtime/helper/wrap_for_worker.js'; + +wrapTestGroupForWorker(g); +`; + res.setHeader('Content-Type', 'application/javascript'); + res.send(result); +}); + // Serve all other .js files by fetching the source .ts file and compiling it. app.get('/out/**/*.js', async (req, res, next) => { const jsUrl = path.relative('/out', req.url); diff --git a/src/common/tools/gen_listings.ts b/src/common/tools/gen_listings.ts deleted file mode 100644 index 032ebaa890f0..000000000000 --- a/src/common/tools/gen_listings.ts +++ /dev/null @@ -1,56 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import * as process from 'process'; - -import { crawl } from './crawl.js'; - -function usage(rc: number): void { - console.error(`Usage: tools/gen_listings [options] [OUT_DIR] [SUITE_DIRS...] - -For each suite in SUITE_DIRS, generate listings and write each listing.js -into OUT_DIR/{suite}/listing.js. Example: - tools/gen_listings gen/ src/unittests/ src/webgpu/ - -Options: - --help Print this message and exit. -`); - process.exit(rc); -} - -const argv = process.argv; -if (argv.indexOf('--help') !== -1) { - usage(0); -} - -{ - // Ignore old argument that is now the default - const i = argv.indexOf('--no-validate'); - if (i !== -1) { - argv.splice(i, 1); - } -} - -if (argv.length < 4) { - usage(0); -} - -const myself = 'src/common/tools/gen_listings.ts'; - -const outDir = argv[2]; - -for (const suiteDir of argv.slice(3)) { - // Run concurrently for each suite (might be a tiny bit more efficient) - void crawl(suiteDir, false).then(listing => { - const suite = path.basename(suiteDir); - const outFile = path.normalize(path.join(outDir, `${suite}/listing.js`)); - fs.mkdirSync(path.join(outDir, suite), { recursive: true }); - fs.writeFileSync( - outFile, - `\ -// AUTO-GENERATED - DO NOT EDIT. See ${myself}. - -export const listing = ${JSON.stringify(listing, undefined, 2)}; -` - ); - }); -} diff --git a/src/common/tools/gen_listings_and_webworkers.ts b/src/common/tools/gen_listings_and_webworkers.ts new file mode 100644 index 000000000000..e446936b66cf --- /dev/null +++ b/src/common/tools/gen_listings_and_webworkers.ts @@ -0,0 +1,90 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as process from 'process'; + +import { crawl } from './crawl.js'; + +function usage(rc: number): void { + console.error(`Usage: tools/gen_listings_and_webworkers [options] [OUT_DIR] [SUITE_DIRS...] + +For each suite in SUITE_DIRS, generate listings into OUT_DIR/{suite}/listing.js, +and generate Web Worker proxies in OUT_DIR/{suite}/webworker/**/*.worker.js for +every .spec.js file. (Note {suite}/webworker/ is reserved for this purpose.) + +Example: + tools/gen_listings_and_webworkers gen/ src/unittests/ src/webgpu/ + +Options: + --help Print this message and exit. +`); + process.exit(rc); +} + +const argv = process.argv; +if (argv.indexOf('--help') !== -1) { + usage(0); +} + +{ + // Ignore old argument that is now the default + const i = argv.indexOf('--no-validate'); + if (i !== -1) { + argv.splice(i, 1); + } +} + +if (argv.length < 4) { + usage(0); +} + +const myself = 'src/common/tools/gen_listings_and_webworkers.ts'; + +const outDir = argv[2]; + +for (const suiteDir of argv.slice(3)) { + // Run concurrently for each suite (might be a tiny bit more efficient) + void crawl(suiteDir, false).then(listing => { + const suite = path.basename(suiteDir); + + // Write listing.js + const outFile = path.normalize(path.join(outDir, `${suite}/listing.js`)); + fs.mkdirSync(path.join(outDir, suite), { recursive: true }); + fs.writeFileSync( + outFile, + `\ +// AUTO-GENERATED - DO NOT EDIT. See ${myself}. + +export const listing = ${JSON.stringify(listing, undefined, 2)}; +` + ); + + // Write suite/webworker/**/*.worker.js + for (const entry of listing) { + if ('readme' in entry) continue; + + const outFileDir = path.join( + outDir, + suite, + 'webworker', + ...entry.file.slice(0, entry.file.length - 1) + ); + const outFile = path.join(outDir, suite, 'webworker', ...entry.file) + '.worker.js'; + + const relPathToSuiteRoot = Array(entry.file.length).fill('..').join('/'); + + fs.mkdirSync(outFileDir, { recursive: true }); + fs.writeFileSync( + outFile, + `\ +// AUTO-GENERATED - DO NOT EDIT. See ${myself}. + +// g is a TestGroup object (defined in common/internal/test_group.ts). +import { g } from '${relPathToSuiteRoot}/${entry.file.join('/')}.spec.js'; +import { wrapTestGroupForWorker } from '${relPathToSuiteRoot}/../common/runtime/helper/wrap_for_worker.js'; + +wrapTestGroupForWorker(g); +` + ); + } + }); +} diff --git a/src/demo/webworker/.gitignore b/src/demo/webworker/.gitignore new file mode 100644 index 000000000000..8496277ad72e --- /dev/null +++ b/src/demo/webworker/.gitignore @@ -0,0 +1,2 @@ +# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers. +* diff --git a/src/manual/webworker/.gitignore b/src/manual/webworker/.gitignore new file mode 100644 index 000000000000..8496277ad72e --- /dev/null +++ b/src/manual/webworker/.gitignore @@ -0,0 +1,2 @@ +# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers. +* diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 3e6a9f6e58d3..a92aa14b76db 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "b6797474", - "webgpu/shader/execution/binary/af_logical.bin": "ff27f50f", - "webgpu/shader/execution/binary/af_division.bin": "97ba2e6d", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "842a508f", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "f086bb00", - "webgpu/shader/execution/binary/af_multiplication.bin": "fb6d6475", - "webgpu/shader/execution/binary/af_remainder.bin": "aa462ca7", - "webgpu/shader/execution/binary/af_subtraction.bin": "f04fbb32", - "webgpu/shader/execution/binary/f16_addition.bin": "3f136f5a", - "webgpu/shader/execution/binary/f16_logical.bin": "8556deb1", - "webgpu/shader/execution/binary/f16_division.bin": "693df765", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "296a11b6", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "cf1ca531", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "d3a317bd", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "a7a1b148", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "8f540eee", - "webgpu/shader/execution/binary/f16_multiplication.bin": "1a7bca1c", - "webgpu/shader/execution/binary/f16_remainder.bin": "62b7bff8", - "webgpu/shader/execution/binary/f16_subtraction.bin": "c921e09d", - "webgpu/shader/execution/binary/f32_addition.bin": "7d7629bd", - "webgpu/shader/execution/binary/f32_logical.bin": "3e903bfb", - "webgpu/shader/execution/binary/f32_division.bin": "35b49326", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "1c2aff7c", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "966b264e", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "4ab99310", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "77f37d5a", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "50dfea9a", - "webgpu/shader/execution/binary/f32_multiplication.bin": "2524db25", - "webgpu/shader/execution/binary/f32_remainder.bin": "b47ad113", - "webgpu/shader/execution/binary/f32_subtraction.bin": "e98584ae", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "88d15602", - "webgpu/shader/execution/binary/i32_comparison.bin": "ddd815a3", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "d882b7f6", - "webgpu/shader/execution/binary/u32_comparison.bin": "e3dfeec7", - "webgpu/shader/execution/abs.bin": "b6f6d4ec", - "webgpu/shader/execution/acos.bin": "d681fe45", - "webgpu/shader/execution/acosh.bin": "a58482ca", - "webgpu/shader/execution/asin.bin": "3856670", - "webgpu/shader/execution/asinh.bin": "7567d4b7", - "webgpu/shader/execution/atan.bin": "80fc2c53", - "webgpu/shader/execution/atan2.bin": "2fc91f76", - "webgpu/shader/execution/atanh.bin": "b68b4e98", - "webgpu/shader/execution/bitcast.bin": "142a34e", - "webgpu/shader/execution/ceil.bin": "65c551a0", - "webgpu/shader/execution/clamp.bin": "369a3396", - "webgpu/shader/execution/cos.bin": "894e3d79", - "webgpu/shader/execution/cosh.bin": "3bf810f1", - "webgpu/shader/execution/cross.bin": "582e87e8", - "webgpu/shader/execution/degrees.bin": "762053b5", - "webgpu/shader/execution/determinant.bin": "a60ac4f9", - "webgpu/shader/execution/distance.bin": "602fc718", - "webgpu/shader/execution/dot.bin": "c33454b1", - "webgpu/shader/execution/exp.bin": "ad6670cf", - "webgpu/shader/execution/exp2.bin": "6f8a0b39", - "webgpu/shader/execution/faceForward.bin": "759ef028", - "webgpu/shader/execution/floor.bin": "d8f91dbe", - "webgpu/shader/execution/fma.bin": "5019731d", - "webgpu/shader/execution/fract.bin": "3a9c858d", - "webgpu/shader/execution/frexp.bin": "33ffb585", - "webgpu/shader/execution/inverseSqrt.bin": "3684c8a", - "webgpu/shader/execution/ldexp.bin": "30881102", - "webgpu/shader/execution/length.bin": "fed952", - "webgpu/shader/execution/log.bin": "b55f7cd0", - "webgpu/shader/execution/log2.bin": "e4fddf41", - "webgpu/shader/execution/max.bin": "ca77aba5", - "webgpu/shader/execution/min.bin": "6b57ac82", - "webgpu/shader/execution/mix.bin": "89f42f5a", - "webgpu/shader/execution/modf.bin": "77eb4ff5", - "webgpu/shader/execution/normalize.bin": "36214034", - "webgpu/shader/execution/pack2x16float.bin": "8bee7573", - "webgpu/shader/execution/pow.bin": "8bcaf788", - "webgpu/shader/execution/quantizeToF16.bin": "8c1bc956", - "webgpu/shader/execution/radians.bin": "c35eecd0", - "webgpu/shader/execution/reflect.bin": "3591dbf7", - "webgpu/shader/execution/refract.bin": "8b2a061d", - "webgpu/shader/execution/round.bin": "58787d04", - "webgpu/shader/execution/saturate.bin": "b5c39be6", - "webgpu/shader/execution/sign.bin": "3b6e8c88", - "webgpu/shader/execution/sin.bin": "1e39b6f9", - "webgpu/shader/execution/sinh.bin": "296767d4", - "webgpu/shader/execution/smoothstep.bin": "2771eb6a", - "webgpu/shader/execution/sqrt.bin": "78c4534c", - "webgpu/shader/execution/step.bin": "b8817ec0", - "webgpu/shader/execution/tan.bin": "f2537ac4", - "webgpu/shader/execution/tanh.bin": "c611a910", - "webgpu/shader/execution/transpose.bin": "7fdac41d", - "webgpu/shader/execution/trunc.bin": "da4434eb", - "webgpu/shader/execution/unpack2x16float.bin": "a5db67bb", - "webgpu/shader/execution/unpack2x16snorm.bin": "b0e6f0f4", - "webgpu/shader/execution/unpack2x16unorm.bin": "ddf30c4f", - "webgpu/shader/execution/unpack4x8snorm.bin": "599b3fb9", - "webgpu/shader/execution/unpack4x8unorm.bin": "d47c58be", - "webgpu/shader/execution/unary/af_arithmetic.bin": "790d7c95", - "webgpu/shader/execution/unary/af_assignment.bin": "679cfa9a", - "webgpu/shader/execution/unary/bool_conversion.bin": "d10f1dbb", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "1b44c7f", - "webgpu/shader/execution/unary/f16_conversion.bin": "436a5636", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "8f8fcd42", - "webgpu/shader/execution/unary/f32_conversion.bin": "97d0e21a", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "41460aa", - "webgpu/shader/execution/unary/i32_conversion.bin": "d5a46ee6", - "webgpu/shader/execution/unary/u32_conversion.bin": "f03edff7", - "webgpu/shader/execution/unary/ai_assignment.bin": "5d612a40", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "c0dd04f9", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "3ea222b8", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "3d9cf9b7", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "a84ecc73", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "6fe3e741" + "webgpu/shader/execution/binary/af_addition.bin": "1043cbb2", + "webgpu/shader/execution/binary/af_logical.bin": "9ab41597", + "webgpu/shader/execution/binary/af_division.bin": "9ec22a4e", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "1da2ca22", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "25303b8e", + "webgpu/shader/execution/binary/af_multiplication.bin": "70d8f01a", + "webgpu/shader/execution/binary/af_remainder.bin": "101c7835", + "webgpu/shader/execution/binary/af_subtraction.bin": "26791651", + "webgpu/shader/execution/binary/f16_addition.bin": "2d67db8f", + "webgpu/shader/execution/binary/f16_logical.bin": "80f9e754", + "webgpu/shader/execution/binary/f16_division.bin": "53e3bdaa", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "911d18f7", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "a306da0a", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "be77024a", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "ea813df5", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "1692bec1", + "webgpu/shader/execution/binary/f16_multiplication.bin": "7c618e51", + "webgpu/shader/execution/binary/f16_remainder.bin": "9de2be15", + "webgpu/shader/execution/binary/f16_subtraction.bin": "c77bff8a", + "webgpu/shader/execution/binary/f32_addition.bin": "d4282b93", + "webgpu/shader/execution/binary/f32_logical.bin": "47467130", + "webgpu/shader/execution/binary/f32_division.bin": "4d79a5f6", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "5972ccf0", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "8bc3f04a", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "7376da03", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c4785f3a", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "20d4b1", + "webgpu/shader/execution/binary/f32_multiplication.bin": "7586ed6d", + "webgpu/shader/execution/binary/f32_remainder.bin": "59f46306", + "webgpu/shader/execution/binary/f32_subtraction.bin": "4311ba10", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "d345f9c7", + "webgpu/shader/execution/binary/i32_comparison.bin": "6d4ae7e0", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "163f8a38", + "webgpu/shader/execution/binary/u32_comparison.bin": "f6c1497b", + "webgpu/shader/execution/abs.bin": "63fa988e", + "webgpu/shader/execution/acos.bin": "47521b3e", + "webgpu/shader/execution/acosh.bin": "59762b6f", + "webgpu/shader/execution/asin.bin": "db0c6641", + "webgpu/shader/execution/asinh.bin": "f0329d7f", + "webgpu/shader/execution/atan.bin": "f6fea79e", + "webgpu/shader/execution/atan2.bin": "1c390d0f", + "webgpu/shader/execution/atanh.bin": "4c430905", + "webgpu/shader/execution/bitcast.bin": "2dfc8f9", + "webgpu/shader/execution/ceil.bin": "3319384e", + "webgpu/shader/execution/clamp.bin": "4873dd79", + "webgpu/shader/execution/cos.bin": "c16d181a", + "webgpu/shader/execution/cosh.bin": "137f4997", + "webgpu/shader/execution/cross.bin": "aebda306", + "webgpu/shader/execution/degrees.bin": "5001b340", + "webgpu/shader/execution/determinant.bin": "c461c715", + "webgpu/shader/execution/distance.bin": "d05e2f8d", + "webgpu/shader/execution/dot.bin": "7f8b7e13", + "webgpu/shader/execution/exp.bin": "d448dc91", + "webgpu/shader/execution/exp2.bin": "1d232c0e", + "webgpu/shader/execution/faceForward.bin": "87c0576c", + "webgpu/shader/execution/floor.bin": "c3a60f36", + "webgpu/shader/execution/fma.bin": "6b94384d", + "webgpu/shader/execution/fract.bin": "d49c3d50", + "webgpu/shader/execution/frexp.bin": "252e0615", + "webgpu/shader/execution/inverseSqrt.bin": "1deb1185", + "webgpu/shader/execution/ldexp.bin": "617e4cc4", + "webgpu/shader/execution/length.bin": "f7b51c96", + "webgpu/shader/execution/log.bin": "ae124706", + "webgpu/shader/execution/log2.bin": "c428e9f2", + "webgpu/shader/execution/max.bin": "a45914cd", + "webgpu/shader/execution/min.bin": "1017e86d", + "webgpu/shader/execution/mix.bin": "3f5ebb6e", + "webgpu/shader/execution/modf.bin": "fedc0c50", + "webgpu/shader/execution/normalize.bin": "60173a1e", + "webgpu/shader/execution/pack2x16float.bin": "e472a927", + "webgpu/shader/execution/pow.bin": "c5a2a9c1", + "webgpu/shader/execution/quantizeToF16.bin": "8d1cb7a6", + "webgpu/shader/execution/radians.bin": "94cbdf6c", + "webgpu/shader/execution/reflect.bin": "39a479a0", + "webgpu/shader/execution/refract.bin": "63e5b11a", + "webgpu/shader/execution/round.bin": "13bf324", + "webgpu/shader/execution/saturate.bin": "c368b1a7", + "webgpu/shader/execution/sign.bin": "5cd05b5c", + "webgpu/shader/execution/sin.bin": "85679e", + "webgpu/shader/execution/sinh.bin": "eaa61750", + "webgpu/shader/execution/smoothstep.bin": "bd06d7cc", + "webgpu/shader/execution/sqrt.bin": "997f5572", + "webgpu/shader/execution/step.bin": "87505049", + "webgpu/shader/execution/tan.bin": "9d7ca121", + "webgpu/shader/execution/tanh.bin": "8bfa913c", + "webgpu/shader/execution/transpose.bin": "7f8fa4c6", + "webgpu/shader/execution/trunc.bin": "9a054523", + "webgpu/shader/execution/unpack2x16float.bin": "ed71c71d", + "webgpu/shader/execution/unpack2x16snorm.bin": "89b4cd2d", + "webgpu/shader/execution/unpack2x16unorm.bin": "df3b3851", + "webgpu/shader/execution/unpack4x8snorm.bin": "aa10073", + "webgpu/shader/execution/unpack4x8unorm.bin": "2fe14b98", + "webgpu/shader/execution/unary/af_arithmetic.bin": "accfc1bd", + "webgpu/shader/execution/unary/af_assignment.bin": "cb3e120e", + "webgpu/shader/execution/unary/bool_conversion.bin": "d0c1e5a3", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "310a05dd", + "webgpu/shader/execution/unary/f16_conversion.bin": "9d94aa72", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "fe955d67", + "webgpu/shader/execution/unary/f32_conversion.bin": "918f6cb3", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "a8649cbb", + "webgpu/shader/execution/unary/i32_conversion.bin": "e5157a69", + "webgpu/shader/execution/unary/u32_conversion.bin": "d07d0c20", + "webgpu/shader/execution/unary/ai_assignment.bin": "f62c765c", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "43501242", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "8e448c53", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "7ea0df71", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "4919c460", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "33c10dad" } \ No newline at end of file diff --git a/src/stress/webworker/.gitignore b/src/stress/webworker/.gitignore new file mode 100644 index 000000000000..8496277ad72e --- /dev/null +++ b/src/stress/webworker/.gitignore @@ -0,0 +1,2 @@ +# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers. +* diff --git a/src/unittests/webworker/.gitignore b/src/unittests/webworker/.gitignore new file mode 100644 index 000000000000..8496277ad72e --- /dev/null +++ b/src/unittests/webworker/.gitignore @@ -0,0 +1,2 @@ +# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers. +* diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 887bbeffe2bf..94126a1f711b 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -2219,6 +2219,7 @@ "webgpu:web_platform,external_texture,video:importExternalTexture,sampleWithVideoFrameWithVisibleRectParam:*": { "subcaseMS": 29.160 }, "webgpu:web_platform,external_texture,video:importExternalTexture,sample_non_YUV_video_frame:*": { "subcaseMS": 36.270 }, "webgpu:web_platform,worker,worker:dedicated_worker:*": { "subcaseMS": 245.901 }, + "webgpu:web_platform,worker,worker:service_worker:*": { "subcaseMS": 102.800 }, "webgpu:web_platform,worker,worker:shared_worker:*": { "subcaseMS": 26.801 }, "_end": "" } diff --git a/src/webgpu/web_platform/worker/worker.spec.ts b/src/webgpu/web_platform/worker/worker.spec.ts index 0f34a58ddaa5..deed5d75e232 100644 --- a/src/webgpu/web_platform/worker/worker.spec.ts +++ b/src/webgpu/web_platform/worker/worker.spec.ts @@ -4,6 +4,9 @@ Tests WebGPU is available in a dedicated worker and a shared worker. Note: The CTS test can be run respectively in a dedicated worker and a shared worker by passing in worker=dedicated and worker=shared as a query parameter. These tests are specifically to check that WebGPU is available in a dedicated worker and a shared worker. + +TODO[2]: Figure out how to make these tests run in service workers (not actually +important unless service workers gain the ability to launch other workers). `; import { Fixture } from '../../../common/framework/fixture.js'; @@ -12,24 +15,26 @@ import { assert } from '../../../common/util/util.js'; export const g = makeTestGroup(Fixture); -function isNode(): boolean { - return typeof process !== 'undefined' && process?.versions?.node !== undefined; -} +const isNode = typeof process !== 'undefined' && process?.versions?.node !== undefined; + +// [1]: we load worker_launcher dynamically because ts-node support +// is using commonjs which doesn't support import.meta. Further, +// we need to put the url in a string and pass the string to import +// otherwise typescript tries to parse the file which again, fails. +// worker_launcher.js is excluded in node.tsconfig.json. + +// [2]: That hack does not work in Service Workers. +const isServiceWorker = globalThis.constructor.name === 'ServiceWorkerGlobalScope'; g.test('dedicated_worker') .desc(`test WebGPU is available in dedicated workers and check for basic functionality`) .fn(async t => { - if (isNode()) { - t.skip('node does not support 100% compatible workers'); - return; - } - // Note: we load worker_launcher dynamically because ts-node support - // is using commonjs which doesn't support import.meta. Further, - // we need to put the url in a string add pass the string to import - // otherwise typescript tries to parse the file which again, fails. - // worker_launcher.js is excluded in node.tsconfig.json. + t.skipIf(isNode, 'node does not support 100% compatible workers'); + + t.skipIf(isServiceWorker, 'Service workers do not support this import() hack'); // [2] const url = './worker_launcher.js'; - const { launchDedicatedWorker } = await import(url); + const { launchDedicatedWorker } = await import(url); // [1] + const result = await launchDedicatedWorker(); assert(result.error === undefined, `should be no error from worker but was: ${result.error}`); }); @@ -37,17 +42,25 @@ g.test('dedicated_worker') g.test('shared_worker') .desc(`test WebGPU is available in shared workers and check for basic functionality`) .fn(async t => { - if (isNode()) { - t.skip('node does not support 100% compatible workers'); - return; - } - // Note: we load worker_launcher dynamically because ts-node support - // is using commonjs which doesn't support import.meta. Further, - // we need to put the url in a string add pass the string to import - // otherwise typescript tries to parse the file which again, fails. - // worker_launcher.js is excluded in node.tsconfig.json. + t.skipIf(isNode, 'node does not support 100% compatible workers'); + + t.skipIf(isServiceWorker, 'Service workers do not support this import() hack'); // [2] const url = './worker_launcher.js'; - const { launchSharedWorker } = await import(url); + const { launchSharedWorker } = await import(url); // [1] + const result = await launchSharedWorker(); assert(result.error === undefined, `should be no error from worker but was: ${result.error}`); }); + +g.test('service_worker') + .desc(`test WebGPU is available in service workers and check for basic functionality`) + .fn(async t => { + t.skipIf(isNode, 'node does not support 100% compatible workers'); + + t.skipIf(isServiceWorker, 'Service workers do not support this import() hack'); // [2] + const url = './worker_launcher.js'; + const { launchServiceWorker } = await import(url); // [1] + + const result = await launchServiceWorker(); + assert(result.error === undefined, `should be no error from worker but was: ${result.error}`); + }); diff --git a/src/webgpu/web_platform/worker/worker.ts b/src/webgpu/web_platform/worker/worker.ts index f3c907a4118a..033473d63a97 100644 --- a/src/webgpu/web_platform/worker/worker.ts +++ b/src/webgpu/web_platform/worker/worker.ts @@ -87,7 +87,7 @@ async function reportTestResults(this: MessagePort | Worker, ev: MessageEvent) { } self.onmessage = (ev: MessageEvent) => { - void reportTestResults.call(self, ev); + void reportTestResults.call(ev.source || self, ev); }; self.onconnect = (event: MessageEvent) => { diff --git a/src/webgpu/web_platform/worker/worker_launcher.ts b/src/webgpu/web_platform/worker/worker_launcher.ts index 0487e4ad38b4..4b1d31ae4921 100644 --- a/src/webgpu/web_platform/worker/worker_launcher.ts +++ b/src/webgpu/web_platform/worker/worker_launcher.ts @@ -1,3 +1,4 @@ +import { SkipTestCase } from '../../../common/framework/fixture.js'; import { getDefaultRequestAdapterOptions } from '../../../common/util/navigator_gpu.js'; export type TestResult = { @@ -5,6 +6,10 @@ export type TestResult = { }; export async function launchDedicatedWorker() { + if (typeof Worker === 'undefined') { + throw new SkipTestCase(`Worker undefined in context ${globalThis.constructor.name}`); + } + const selfPath = import.meta.url; const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/')); const workerPath = selfPathDir + '/worker.js'; @@ -18,6 +23,10 @@ export async function launchDedicatedWorker() { } export async function launchSharedWorker() { + if (typeof SharedWorker === 'undefined') { + throw new SkipTestCase(`SharedWorker undefined in context ${globalThis.constructor.name}`); + } + const selfPath = import.meta.url; const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/')); const workerPath = selfPathDir + '/worker.js'; @@ -33,3 +42,34 @@ export async function launchSharedWorker() { }); return await promise; } + +export async function launchServiceWorker() { + if (typeof navigator === 'undefined' || typeof navigator.serviceWorker === 'undefined') { + throw new SkipTestCase( + `navigator.serviceWorker undefined in context ${globalThis.constructor.name}` + ); + } + + const selfPath = import.meta.url; + const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/')); + const serviceWorkerPath = selfPathDir + '/worker.js'; + const registration = await navigator.serviceWorker.register(serviceWorkerPath, { + type: 'module', + }); + await registration.update(); + + const promise = new Promise(resolve => { + navigator.serviceWorker.addEventListener( + 'message', + ev => { + resolve(ev.data as TestResult); + void registration.unregister(); + }, + { once: true } + ); + }); + registration.active?.postMessage({ + defaultRequestAdapterOptions: getDefaultRequestAdapterOptions(), + }); + return await promise; +} diff --git a/src/webgpu/webworker/.gitignore b/src/webgpu/webworker/.gitignore new file mode 100644 index 000000000000..8496277ad72e --- /dev/null +++ b/src/webgpu/webworker/.gitignore @@ -0,0 +1,2 @@ +# DIRECTORY RESERVED FOR GENERATED FILES. See gen_listings_and_webworkers. +* diff --git a/tools/gen_listings b/tools/gen_listings deleted file mode 100755 index 6c25622423a1..000000000000 --- a/tools/gen_listings +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env node - -// Crawl a suite directory (e.g. src/webgpu/) to generate a listing.js containing -// the listing of test files in the suite. - -require('../src/common/tools/setup-ts-in-node.js'); -require('../src/common/tools/gen_listings.ts'); diff --git a/tools/gen_listings_and_webworkers b/tools/gen_listings_and_webworkers new file mode 100755 index 000000000000..285df3657d2a --- /dev/null +++ b/tools/gen_listings_and_webworkers @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +// Crawl a suite directory (e.g. src/webgpu/) to generate a listing.js containing +// the listing of test files in the suite, and some generated test files +// (like suite/serviceworker/**/*.spec.js). + +require('../src/common/tools/setup-ts-in-node.js'); +require('../src/common/tools/gen_listings_and_webworkers.ts'); From d97cc728ede861fe19eb38fc3200af1560aa9995 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Mon, 11 Mar 2024 19:58:39 +0000 Subject: [PATCH 20/33] shader/execution: Implement vector component selection tests Bug: #3170 --- src/webgpu/listing_meta.json | 12 +- .../array.spec.ts => array/index.spec.ts} | 0 .../access/vector/components.spec.ts | 117 ++++++++++++++++++ .../vector.spec.ts => vector/index.spec.ts} | 4 +- 4 files changed, 126 insertions(+), 7 deletions(-) rename src/webgpu/shader/execution/expression/access/{index/array.spec.ts => array/index.spec.ts} (100%) create mode 100644 src/webgpu/shader/execution/expression/access/vector/components.spec.ts rename src/webgpu/shader/execution/expression/access/{index/vector.spec.ts => vector/index.spec.ts} (96%) diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 94126a1f711b..c752ba6e467a 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -899,11 +899,13 @@ "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,access,index,array:abstract_scalar:*": { "subcaseMS": 189.344 }, - "webgpu:shader,execution,expression,access,index,array:concrete_scalar:*": { "subcaseMS": 989.619 }, - "webgpu:shader,execution,expression,access,index,array:vector:*": { "subcaseMS": 1546.461 }, - "webgpu:shader,execution,expression,access,index,vector:abstract_scalar:*": { "subcaseMS": 213.529 }, - "webgpu:shader,execution,expression,access,index,vector:concrete_scalar:*": { "subcaseMS": 1918.096 }, + "webgpu:shader,execution,expression,access,array,index:abstract_scalar:*": { "subcaseMS": 235.962 }, + "webgpu:shader,execution,expression,access,array,index:concrete_scalar:*": { "subcaseMS": 1439.796 }, + "webgpu:shader,execution,expression,access,array,index:vector:*": { "subcaseMS": 2137.684 }, + "webgpu:shader,execution,expression,access,vector,components:abstract_scalar:*": { "subcaseMS": 533.768 }, + "webgpu:shader,execution,expression,access,vector,components:concrete_scalar:*": { "subcaseMS": 11636.652 }, + "webgpu:shader,execution,expression,access,vector,index:abstract_scalar:*": { "subcaseMS": 215.404 }, + "webgpu:shader,execution,expression,access,vector,index:concrete_scalar:*": { "subcaseMS": 1707.582 }, "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/access/index/array.spec.ts b/src/webgpu/shader/execution/expression/access/array/index.spec.ts similarity index 100% rename from src/webgpu/shader/execution/expression/access/index/array.spec.ts rename to src/webgpu/shader/execution/expression/access/array/index.spec.ts diff --git a/src/webgpu/shader/execution/expression/access/vector/components.spec.ts b/src/webgpu/shader/execution/expression/access/vector/components.spec.ts new file mode 100644 index 000000000000..e04e4884811b --- /dev/null +++ b/src/webgpu/shader/execution/expression/access/vector/components.spec.ts @@ -0,0 +1,117 @@ +export const description = ` +Execution Tests for vector component selection expressions +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { ScalarValue, Type, VectorValue, f32 } from '../../../../../util/conversion.js'; +import { allInputSources, basicExpressionBuilder, run } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +/** @returns the full permutation of component indices used to component select a vector of width 'n' */ +function indices(n: number) { + const out: number[][] = []; + for (let width = 1; width < n; width++) { + let generate = (swizzle: number[]) => { + out.push(swizzle); + }; + for (let i = 0; i < width; i++) { + const next = generate; + generate = (swizzle: number[]) => { + for (let j = 0; j < width; j++) { + next([...swizzle, j]); + } + }; + } + generate([]); + } + return out; +} + +g.test('concrete_scalar') + .specURL('https://www.w3.org/TR/WGSL/#vector-access-expr') + .desc(`Test vector component selection of concrete vectors`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('elementType', ['i32', 'u32', 'f32', 'f16'] as const) + .combine('width', [2, 3, 4] as const) + .combine('components', ['rgba', 'xyzw'] as const) + .beginSubcases() + .expand('indices', u => indices(u.width)) + ) + .beforeAllSubcases(t => { + if (t.params.elementType === 'f16') { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(async t => { + const elementType = Type[t.params.elementType]; + const vectorType = Type.vec(t.params.width, elementType); + const elementValues = (i: number) => (i + 1) * 10; + const elements: ScalarValue[] = []; + for (let i = 0; i < t.params.width; i++) { + elements.push(elementType.create(elementValues(i))); + } + const result = (() => { + if (t.params.indices.length === 1) { + return { type: elementType, value: elementType.create(elementValues(0)) }; + } else { + const vec = Type.vec(t.params.indices.length, elementType); + return { type: vec, value: vec.create(t.params.indices.map(i => elementValues(i))) }; + } + })(); + + const components = t.params.indices.map(i => t.params.components[i]).join(''); + await run( + t, + basicExpressionBuilder(ops => `${ops[0]}.${components}`), + [vectorType], + result.type, + t.params, + [{ input: [new VectorValue(elements)], expected: result.value }] + ); + }); + +g.test('abstract_scalar') + .specURL('https://www.w3.org/TR/WGSL/#vector-access-expr') + .desc(`Test vector component selection of abstract numeric vectors`) + .params(u => + u + .combine('elementType', ['abstract-int', 'abstract-float'] as const) + .combine('width', [2, 3, 4] as const) + .combine('components', ['rgba', 'xyzw'] as const) + .beginSubcases() + .expand('indices', u => indices(u.width)) + ) + .fn(async t => { + const elementType = Type[t.params.elementType]; + const vectorType = Type.vec(t.params.width, elementType); + const elementValues = (i: number) => (i + 1) * 0x100000000; + const elements: ScalarValue[] = []; + for (let i = 0; i < t.params.width; i++) { + elements.push(elementType.create(elementValues(i))); + } + const result = (() => { + if (t.params.indices.length === 1) { + return { type: Type.f32, value: f32(elementValues(0) / 0x100000000) }; + } else { + const vec = Type.vec(t.params.indices.length, Type.f32); + return { + type: vec, + value: vec.create(t.params.indices.map(i => elementValues(i) / 0x100000000)), + }; + } + })(); + + const components = t.params.indices.map(i => t.params.components[i]).join(''); + await run( + t, + basicExpressionBuilder(ops => `${ops[0]}.${components} / 0x100000000`), + [vectorType], + result.type, + { inputSource: 'const' }, + [{ input: [new VectorValue(elements)], expected: result.value }] + ); + }); diff --git a/src/webgpu/shader/execution/expression/access/index/vector.spec.ts b/src/webgpu/shader/execution/expression/access/vector/index.spec.ts similarity index 96% rename from src/webgpu/shader/execution/expression/access/index/vector.spec.ts rename to src/webgpu/shader/execution/expression/access/vector/index.spec.ts index 034ac5a95f43..acfab82e68ca 100644 --- a/src/webgpu/shader/execution/expression/access/index/vector.spec.ts +++ b/src/webgpu/shader/execution/expression/access/vector/index.spec.ts @@ -12,7 +12,7 @@ export const g = makeTestGroup(GPUTest); g.test('concrete_scalar') .specURL('https://www.w3.org/TR/WGSL/#vector-access-expr') - .desc(`Test indexing of an array of concrete scalars`) + .desc(`Test indexing of concrete vectors`) .params(u => u .combine('inputSource', allInputSources) @@ -51,7 +51,7 @@ g.test('concrete_scalar') g.test('abstract_scalar') .specURL('https://www.w3.org/TR/WGSL/#vector-access-expr') - .desc(`Test indexing of an array of scalars`) + .desc(`Test indexing of abstract numeric vectors`) .params(u => u .combine('elementType', ['abstract-int', 'abstract-float'] as const) From 0ad72a6e701d1cdddd5f7d7ff91daef90fd2fec2 Mon Sep 17 00:00:00 2001 From: James Price Date: Mon, 11 Mar 2024 16:38:20 -0400 Subject: [PATCH 21/33] wgsl: Add logical negation validation tests (#3473) Test that the logical negation operator is only accepted for bool scalar and vector types. --- src/webgpu/listing_meta.json | 2 + .../expression/unary/logical_negation.spec.ts | 114 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/webgpu/shader/validation/expression/unary/logical_negation.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index c752ba6e467a..214c81844eda 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1939,6 +1939,8 @@ "webgpu:shader,validation,expression,unary,address_of_and_indirection:basic:*": { "subcaseMS": 0.000 }, "webgpu:shader,validation,expression,unary,address_of_and_indirection:composite:*": { "subcaseMS": 0.000 }, "webgpu:shader,validation,expression,unary,address_of_and_indirection:invalid:*": { "subcaseMS": 0.000 }, + "webgpu:shader,validation,expression,unary,logical_negation:invalid_types:*": { "subcaseMS": 7.100 }, + "webgpu:shader,validation,expression,unary,logical_negation:scalar_vector:*": { "subcaseMS": 84.680 }, "webgpu:shader,validation,extension,pointer_composite_access:deref:*": { "subcaseMS": 0.000 }, "webgpu:shader,validation,extension,pointer_composite_access:pointer:*": { "subcaseMS": 0.000 }, "webgpu:shader,validation,extension,readonly_and_readwrite_storage_textures:textureBarrier:*": { "subcaseMS": 1.141 }, diff --git a/src/webgpu/shader/validation/expression/unary/logical_negation.spec.ts b/src/webgpu/shader/validation/expression/unary/logical_negation.spec.ts new file mode 100644 index 000000000000..eda5e224dfd4 --- /dev/null +++ b/src/webgpu/shader/validation/expression/unary/logical_negation.spec.ts @@ -0,0 +1,114 @@ +export const description = ` +Validation tests for logical negation expressions. +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { keysOf, objectsToRecord } from '../../../../../common/util/data_tables.js'; +import { kAllScalarsAndVectors, scalarTypeOf, Type } from '../../../../util/conversion.js'; +import { ShaderValidationTest } from '../../shader_validation_test.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +// A list of scalar and vector types. +const kScalarAndVectorTypes = objectsToRecord(kAllScalarsAndVectors); + +g.test('scalar_vector') + .desc( + ` + Validates that scalar and vector logical negation expressions are only accepted for bool types. + ` + ) + .params(u => u.combine('type', keysOf(kScalarAndVectorTypes)).beginSubcases()) + .beforeAllSubcases(t => { + if (scalarTypeOf(kScalarAndVectorTypes[t.params.type]) === Type.f16) { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(t => { + const type = kScalarAndVectorTypes[t.params.type]; + const elementTy = scalarTypeOf(type); + const hasF16 = elementTy === Type.f16; + const code = ` +${hasF16 ? 'enable f16;' : ''} +const rhs = ${type.create(elementTy.kind === 'abstract-int' ? 0n : 0).wgsl()}; +const foo = !rhs; +`; + + t.expectCompileResult(elementTy === Type.bool, code); + }); + +interface InvalidTypeConfig { + // An expression that produces a value of the target type. + expr: string; + // A function that converts an expression of the target type into a valid negation operand. + control: (x: string) => string; +} +const kInvalidTypes: Record = { + mat2x2f: { + expr: 'm', + control: e => `bool(${e}[0][0])`, + }, + + array: { + expr: 'arr', + control: e => `${e}[0]`, + }, + + ptr: { + expr: '(&b)', + control: e => `*${e}`, + }, + + atomic: { + expr: 'a', + control: e => `bool(atomicLoad(&${e}))`, + }, + + texture: { + expr: 't', + control: e => `bool(textureLoad(${e}, vec2(), 0).x)`, + }, + + sampler: { + expr: 's', + control: e => `bool(textureSampleLevel(t, ${e}, vec2(), 0).x)`, + }, + + struct: { + expr: 'str', + control: e => `${e}.b`, + }, +}; + +g.test('invalid_types') + .desc( + ` + Validates that logical negation expressions are never accepted for non-scalar and non-vector types. + ` + ) + .params(u => + u.combine('type', keysOf(kInvalidTypes)).combine('control', [true, false]).beginSubcases() + ) + .fn(t => { + const type = kInvalidTypes[t.params.type]; + const expr = t.params.control ? type.control(type.expr) : type.expr; + const code = ` +@group(0) @binding(0) var t : texture_2d; +@group(0) @binding(1) var s : sampler; +@group(0) @binding(2) var a : atomic; + +struct S { b : bool } + +var b : bool; +var m : mat2x2f; +var arr : array; +var str : S; + +@compute @workgroup_size(1) +fn main() { + let foo = !${expr}; +} +`; + + t.expectCompileResult(t.params.control, code); + }); From 02a12ed607754032f33f5466ef130dc6e99c9254 Mon Sep 17 00:00:00 2001 From: James Price Date: Mon, 11 Mar 2024 16:59:33 -0400 Subject: [PATCH 22/33] wgsl: Add and/or/xor validation tests (#3476) Test that the operators are only accepted for booleans or compatible integer types. --- src/webgpu/listing_meta.json | 2 + .../expression/binary/and_or_xor.spec.ts | 181 ++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 src/webgpu/shader/validation/expression/binary/and_or_xor.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 214c81844eda..ac2c73f31a2a 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1816,6 +1816,8 @@ "webgpu:shader,validation,decl,var_access_mode:read_access:*": { "subcaseMS": 1.177 }, "webgpu:shader,validation,decl,var_access_mode:write_access:*": { "subcaseMS": 1.154 }, "webgpu:shader,validation,expression,access,vector:vector:*": { "subcaseMS": 1.407 }, + "webgpu:shader,validation,expression,binary,and_or_xor:invalid_types:*": { "subcaseMS": 24.069 }, + "webgpu:shader,validation,expression,binary,and_or_xor:scalar_vector:*": { "subcaseMS": 666.807 }, "webgpu:shader,validation,expression,binary,bitwise_shift:shift_left_concrete:*": { "subcaseMS": 1.216 }, "webgpu:shader,validation,expression,binary,bitwise_shift:shift_left_vec_size_mismatch:*": { "subcaseMS": 1.367 }, "webgpu:shader,validation,expression,binary,bitwise_shift:shift_right_concrete:*": { "subcaseMS": 1.237 }, diff --git a/src/webgpu/shader/validation/expression/binary/and_or_xor.spec.ts b/src/webgpu/shader/validation/expression/binary/and_or_xor.spec.ts new file mode 100644 index 000000000000..b1b0f12004ac --- /dev/null +++ b/src/webgpu/shader/validation/expression/binary/and_or_xor.spec.ts @@ -0,0 +1,181 @@ +export const description = ` +Validation tests for logical and bitwise and/or/xor expressions. +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { keysOf, objectsToRecord } from '../../../../../common/util/data_tables.js'; +import { + kAllScalarsAndVectors, + ScalarType, + scalarTypeOf, + Type, + VectorType, +} from '../../../../util/conversion.js'; +import { ShaderValidationTest } from '../../shader_validation_test.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +// A list of operators and a flag for whether they support boolean values or not. +const kOperators = { + and: { op: '&', supportsBool: true }, + or: { op: '|', supportsBool: true }, + xor: { op: '^', supportsBool: false }, +}; + +// A list of scalar and vector types. +const kScalarAndVectorTypes = objectsToRecord(kAllScalarsAndVectors); + +g.test('scalar_vector') + .desc( + ` + Validates that scalar and vector expressions are only accepted for bool or compatible integer types. + ` + ) + .params(u => + u + .combine('op', keysOf(kOperators)) + .combine('lhs', keysOf(kScalarAndVectorTypes)) + .combine( + 'rhs', + // Skip vec3 and vec4 on the RHS to keep the number of subcases down. + keysOf(kScalarAndVectorTypes).filter( + value => !(value.startsWith('vec3') || value.startsWith('vec4')) + ) + ) + .beginSubcases() + ) + .beforeAllSubcases(t => { + if ( + scalarTypeOf(kScalarAndVectorTypes[t.params.lhs]) === Type.f16 || + scalarTypeOf(kScalarAndVectorTypes[t.params.rhs]) === Type.f16 + ) { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(t => { + const op = kOperators[t.params.op]; + const lhs = kScalarAndVectorTypes[t.params.lhs]; + const rhs = kScalarAndVectorTypes[t.params.rhs]; + const lhsElement = scalarTypeOf(lhs); + const rhsElement = scalarTypeOf(rhs); + const hasF16 = lhsElement === Type.f16 || rhsElement === Type.f16; + const code = ` +${hasF16 ? 'enable f16;' : ''} +const lhs = ${lhs.create(lhsElement.kind === 'abstract-int' ? 0n : 0).wgsl()}; +const rhs = ${rhs.create(rhsElement.kind === 'abstract-int' ? 0n : 0).wgsl()}; +const foo = lhs ${op.op} rhs; +`; + + // Determine if the element types are compatible. + const kIntegerTypes = [Type.abstractInt, Type.i32, Type.u32]; + let elementIsCompatible = false; + if (lhsElement === Type.abstractInt) { + // Abstract integers are compatible with any other integer type. + elementIsCompatible = kIntegerTypes.includes(rhsElement); + } else if (rhsElement === Type.abstractInt) { + // Abstract integers are compatible with any other numeric type. + elementIsCompatible = kIntegerTypes.includes(lhsElement); + } else if (kIntegerTypes.includes(lhsElement)) { + // Concrete integers are only compatible with values with the exact same type. + elementIsCompatible = lhsElement === rhsElement; + } else if (lhsElement === Type.bool) { + // Booleans are only compatible with other booleans. + elementIsCompatible = rhsElement === Type.bool; + } + + // Determine if the full type is compatible. + let valid = false; + if (lhs instanceof ScalarType && rhs instanceof ScalarType) { + valid = elementIsCompatible; + } else if (lhs instanceof VectorType && rhs instanceof VectorType) { + // Vectors are only compatible with if the vector widths match. + valid = lhs.width === rhs.width && elementIsCompatible; + } + + if (lhsElement.kind === 'bool') { + valid &&= op.supportsBool; + } + + t.expectCompileResult(valid, code); + }); + +interface InvalidTypeConfig { + // An expression that produces a value of the target type. + expr: string; + // A function that converts an expression of the target type into a valid integer operand. + control: (x: string) => string; +} +const kInvalidTypes: Record = { + mat2x2f: { + expr: 'm', + control: e => `i32(${e}[0][0])`, + }, + + array: { + expr: 'arr', + control: e => `${e}[0]`, + }, + + ptr: { + expr: '(&u)', + control: e => `*${e}`, + }, + + atomic: { + expr: 'a', + control: e => `atomicLoad(&${e})`, + }, + + texture: { + expr: 't', + control: e => `i32(textureLoad(${e}, vec2(), 0).x)`, + }, + + sampler: { + expr: 's', + control: e => `i32(textureSampleLevel(t, ${e}, vec2(), 0).x)`, + }, + + struct: { + expr: 'str', + control: e => `${e}.u`, + }, +}; + +g.test('invalid_types') + .desc( + ` + Validates that expressions are never accepted for non-scalar and non-vector types. + ` + ) + .params(u => + u + .combine('op', keysOf(kOperators)) + .combine('type', keysOf(kInvalidTypes)) + .combine('control', [true, false]) + .beginSubcases() + ) + .fn(t => { + const op = kOperators[t.params.op]; + const type = kInvalidTypes[t.params.type]; + const expr = t.params.control ? type.control(type.expr) : type.expr; + const code = ` +@group(0) @binding(0) var t : texture_2d; +@group(0) @binding(1) var s : sampler; +@group(0) @binding(2) var a : atomic; + +struct S { u : u32 } + +var u : u32; +var m : mat2x2f; +var arr : array; +var str : S; + +@compute @workgroup_size(1) +fn main() { + let foo = ${expr} ${op.op} ${expr}; +} +`; + + t.expectCompileResult(t.params.control, code); + }); From 40acabf7d796a00a8686315571c69586f8e0b256 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 12 Mar 2024 12:24:25 +0000 Subject: [PATCH 23/33] Re-add `gen_listings.ts` Chromium build infrastructure depends on this file. In order to migrate to the new `gen_listings_and_webworkers.ts`, we need to do a N-way patch. --- src/common/tools/gen_listings.ts | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/common/tools/gen_listings.ts diff --git a/src/common/tools/gen_listings.ts b/src/common/tools/gen_listings.ts new file mode 100644 index 000000000000..032ebaa890f0 --- /dev/null +++ b/src/common/tools/gen_listings.ts @@ -0,0 +1,56 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as process from 'process'; + +import { crawl } from './crawl.js'; + +function usage(rc: number): void { + console.error(`Usage: tools/gen_listings [options] [OUT_DIR] [SUITE_DIRS...] + +For each suite in SUITE_DIRS, generate listings and write each listing.js +into OUT_DIR/{suite}/listing.js. Example: + tools/gen_listings gen/ src/unittests/ src/webgpu/ + +Options: + --help Print this message and exit. +`); + process.exit(rc); +} + +const argv = process.argv; +if (argv.indexOf('--help') !== -1) { + usage(0); +} + +{ + // Ignore old argument that is now the default + const i = argv.indexOf('--no-validate'); + if (i !== -1) { + argv.splice(i, 1); + } +} + +if (argv.length < 4) { + usage(0); +} + +const myself = 'src/common/tools/gen_listings.ts'; + +const outDir = argv[2]; + +for (const suiteDir of argv.slice(3)) { + // Run concurrently for each suite (might be a tiny bit more efficient) + void crawl(suiteDir, false).then(listing => { + const suite = path.basename(suiteDir); + const outFile = path.normalize(path.join(outDir, `${suite}/listing.js`)); + fs.mkdirSync(path.join(outDir, suite), { recursive: true }); + fs.writeFileSync( + outFile, + `\ +// AUTO-GENERATED - DO NOT EDIT. See ${myself}. + +export const listing = ${JSON.stringify(listing, undefined, 2)}; +` + ); + }); +} From 3222c793c46c5cea034957c0e07bdb4e5b202226 Mon Sep 17 00:00:00 2001 From: James Price Date: Tue, 12 Mar 2024 12:21:49 -0400 Subject: [PATCH 24/33] wgsl: Add bitwise complement validation tests (#3477) Test that the bitwise complement operator is only accepted for integer scalar and vector types. --- src/webgpu/listing_meta.json | 2 + .../unary/bitwise_complement.spec.ts | 114 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/webgpu/shader/validation/expression/unary/bitwise_complement.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index ac2c73f31a2a..2db6cc8785dd 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1941,6 +1941,8 @@ "webgpu:shader,validation,expression,unary,address_of_and_indirection:basic:*": { "subcaseMS": 0.000 }, "webgpu:shader,validation,expression,unary,address_of_and_indirection:composite:*": { "subcaseMS": 0.000 }, "webgpu:shader,validation,expression,unary,address_of_and_indirection:invalid:*": { "subcaseMS": 0.000 }, + "webgpu:shader,validation,expression,unary,bitwise_complement:invalid_types:*": { "subcaseMS": 7.564 }, + "webgpu:shader,validation,expression,unary,bitwise_complement:scalar_vector:*": { "subcaseMS": 73.852 }, "webgpu:shader,validation,expression,unary,logical_negation:invalid_types:*": { "subcaseMS": 7.100 }, "webgpu:shader,validation,expression,unary,logical_negation:scalar_vector:*": { "subcaseMS": 84.680 }, "webgpu:shader,validation,extension,pointer_composite_access:deref:*": { "subcaseMS": 0.000 }, diff --git a/src/webgpu/shader/validation/expression/unary/bitwise_complement.spec.ts b/src/webgpu/shader/validation/expression/unary/bitwise_complement.spec.ts new file mode 100644 index 000000000000..b5b8556b9d51 --- /dev/null +++ b/src/webgpu/shader/validation/expression/unary/bitwise_complement.spec.ts @@ -0,0 +1,114 @@ +export const description = ` +Validation tests for bitwise complement expressions. +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { keysOf, objectsToRecord } from '../../../../../common/util/data_tables.js'; +import { kAllScalarsAndVectors, scalarTypeOf, Type } from '../../../../util/conversion.js'; +import { ShaderValidationTest } from '../../shader_validation_test.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +// A list of scalar and vector types. +const kScalarAndVectorTypes = objectsToRecord(kAllScalarsAndVectors); + +g.test('scalar_vector') + .desc( + ` + Validates that scalar and vector bitwise complement expressions are only accepted for integers. + ` + ) + .params(u => u.combine('type', keysOf(kScalarAndVectorTypes)).beginSubcases()) + .beforeAllSubcases(t => { + if (scalarTypeOf(kScalarAndVectorTypes[t.params.type]) === Type.f16) { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(t => { + const type = kScalarAndVectorTypes[t.params.type]; + const elementTy = scalarTypeOf(type); + const hasF16 = elementTy === Type.f16; + const code = ` +${hasF16 ? 'enable f16;' : ''} +const rhs = ${type.create(0).wgsl()}; +const foo = ~rhs; +`; + + t.expectCompileResult([Type.abstractInt, Type.i32, Type.u32].includes(elementTy), code); + }); + +interface InvalidTypeConfig { + // An expression that produces a value of the target type. + expr: string; + // A function that converts an expression of the target type into a valid complement operand. + control: (x: string) => string; +} +const kInvalidTypes: Record = { + mat2x2f: { + expr: 'm', + control: e => `i32(${e}[0][0])`, + }, + + array: { + expr: 'arr', + control: e => `${e}[0]`, + }, + + ptr: { + expr: '(&u)', + control: e => `*${e}`, + }, + + atomic: { + expr: 'a', + control: e => `atomicLoad(&${e})`, + }, + + texture: { + expr: 't', + control: e => `i32(textureLoad(${e}, vec2(), 0).x)`, + }, + + sampler: { + expr: 's', + control: e => `i32(textureSampleLevel(t, ${e}, vec2(), 0).x)`, + }, + + struct: { + expr: 'str', + control: e => `${e}.u`, + }, +}; + +g.test('invalid_types') + .desc( + ` + Validates that bitwise complement expressions are never accepted for non-scalar and non-vector types. + ` + ) + .params(u => + u.combine('type', keysOf(kInvalidTypes)).combine('control', [true, false]).beginSubcases() + ) + .fn(t => { + const type = kInvalidTypes[t.params.type]; + const expr = t.params.control ? type.control(type.expr) : type.expr; + const code = ` +@group(0) @binding(0) var t : texture_2d; +@group(0) @binding(1) var s : sampler; +@group(0) @binding(2) var a : atomic; + +struct S { u : u32 } + +var u : u32; +var m : mat2x2f; +var arr : array; +var str : S; + +@compute @workgroup_size(1) +fn main() { + let foo = ~${expr}; +} +`; + + t.expectCompileResult(t.params.control, code); + }); From 1dacc16c25b60c80965cb7b972829a25135831e9 Mon Sep 17 00:00:00 2001 From: James Price Date: Tue, 12 Mar 2024 12:36:30 -0400 Subject: [PATCH 25/33] wgsl: Update value creation and type comparisons (#3482) We can now just pass `0` to type.create() regardless of the type. Type comparisons are also switched from `type.kind === ''` to `type === Type.`. --- .../expression/binary/and_or_xor.spec.ts | 6 +++--- .../expression/binary/comparison.spec.ts | 18 +++++++++--------- .../expression/unary/logical_negation.spec.ts | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/webgpu/shader/validation/expression/binary/and_or_xor.spec.ts b/src/webgpu/shader/validation/expression/binary/and_or_xor.spec.ts index b1b0f12004ac..ed0ce336c4f0 100644 --- a/src/webgpu/shader/validation/expression/binary/and_or_xor.spec.ts +++ b/src/webgpu/shader/validation/expression/binary/and_or_xor.spec.ts @@ -61,8 +61,8 @@ g.test('scalar_vector') const hasF16 = lhsElement === Type.f16 || rhsElement === Type.f16; const code = ` ${hasF16 ? 'enable f16;' : ''} -const lhs = ${lhs.create(lhsElement.kind === 'abstract-int' ? 0n : 0).wgsl()}; -const rhs = ${rhs.create(rhsElement.kind === 'abstract-int' ? 0n : 0).wgsl()}; +const lhs = ${lhs.create(0).wgsl()}; +const rhs = ${rhs.create(0).wgsl()}; const foo = lhs ${op.op} rhs; `; @@ -92,7 +92,7 @@ const foo = lhs ${op.op} rhs; valid = lhs.width === rhs.width && elementIsCompatible; } - if (lhsElement.kind === 'bool') { + if (lhsElement === Type.bool) { valid &&= op.supportsBool; } diff --git a/src/webgpu/shader/validation/expression/binary/comparison.spec.ts b/src/webgpu/shader/validation/expression/binary/comparison.spec.ts index bfba7adaa6b4..6721460f41a6 100644 --- a/src/webgpu/shader/validation/expression/binary/comparison.spec.ts +++ b/src/webgpu/shader/validation/expression/binary/comparison.spec.ts @@ -64,8 +64,8 @@ g.test('scalar_vector') const hasF16 = lhsElement === Type.f16 || rhsElement === Type.f16; const code = ` ${hasF16 ? 'enable f16;' : ''} -const lhs = ${lhs.create(lhsElement.kind === 'abstract-int' ? 0n : 0).wgsl()}; -const rhs = ${rhs.create(rhsElement.kind === 'abstract-int' ? 0n : 0).wgsl()}; +const lhs = ${lhs.create(0).wgsl()}; +const rhs = ${rhs.create(0).wgsl()}; const foo = lhs ${kComparisonOperators[t.params.op].op} rhs; `; @@ -73,16 +73,16 @@ const foo = lhs ${kComparisonOperators[t.params.op].op} rhs; // Determine if the element types are comparable. let elementIsCompatible = false; - if (lhsElement.kind === 'abstract-int') { + if (lhsElement === Type.abstractInt) { // Abstract integers are comparable to any other numeric type. - elementIsCompatible = rhsElement.kind !== 'bool'; - } else if (rhsElement.kind === 'abstract-int') { + elementIsCompatible = rhsElement !== Type.bool; + } else if (rhsElement === Type.abstractInt) { // Abstract integers are comparable to any other numeric type. - elementIsCompatible = lhsElement.kind !== 'bool'; - } else if (lhsElement.kind === 'abstract-float') { + elementIsCompatible = lhsElement !== Type.bool; + } else if (lhsElement === Type.abstractFloat) { // Abstract floats are comparable to any other float type. elementIsCompatible = isFloatType(rhsElement); - } else if (rhsElement.kind === 'abstract-float') { + } else if (rhsElement === Type.abstractFloat) { // Abstract floats are comparable to any other float type. elementIsCompatible = isFloatType(lhsElement); } else { @@ -98,7 +98,7 @@ const foo = lhs ${kComparisonOperators[t.params.op].op} rhs; valid = lhs.width === rhs.width && elementIsCompatible; } - if (lhsElement.kind === 'bool') { + if (lhsElement === Type.bool) { valid &&= kComparisonOperators[t.params.op].supportsBool; } diff --git a/src/webgpu/shader/validation/expression/unary/logical_negation.spec.ts b/src/webgpu/shader/validation/expression/unary/logical_negation.spec.ts index eda5e224dfd4..a85516ec3f88 100644 --- a/src/webgpu/shader/validation/expression/unary/logical_negation.spec.ts +++ b/src/webgpu/shader/validation/expression/unary/logical_negation.spec.ts @@ -30,7 +30,7 @@ g.test('scalar_vector') const hasF16 = elementTy === Type.f16; const code = ` ${hasF16 ? 'enable f16;' : ''} -const rhs = ${type.create(elementTy.kind === 'abstract-int' ? 0n : 0).wgsl()}; +const rhs = ${type.create(0).wgsl()}; const foo = !rhs; `; From 9db2fcf3ba89e90ca1a13bed512ba54954c8d96e Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Tue, 12 Mar 2024 13:07:20 -0400 Subject: [PATCH 26/33] wgsl: Implement AbstractFloat `determinant` execution tests (#3474) Fixes #2523 --- src/resources/cache/hashes.json | 186 +++++++++--------- .../webgpu/shader/execution/determinant.bin | Bin 6872 -> 10368 bytes src/unittests/floating_point.spec.ts | 2 +- .../call/builtin/determinant.cache.ts | 14 ++ .../call/builtin/determinant.spec.ts | 19 +- src/webgpu/util/floating_point.ts | 5 +- 6 files changed, 124 insertions(+), 102 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index a92aa14b76db..1dfb7379313f 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "1043cbb2", + "webgpu/shader/execution/binary/af_addition.bin": "f79f0374", "webgpu/shader/execution/binary/af_logical.bin": "9ab41597", - "webgpu/shader/execution/binary/af_division.bin": "9ec22a4e", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "1da2ca22", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "25303b8e", - "webgpu/shader/execution/binary/af_multiplication.bin": "70d8f01a", - "webgpu/shader/execution/binary/af_remainder.bin": "101c7835", - "webgpu/shader/execution/binary/af_subtraction.bin": "26791651", - "webgpu/shader/execution/binary/f16_addition.bin": "2d67db8f", + "webgpu/shader/execution/binary/af_division.bin": "770fc018", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "a401d81a", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "a9e51912", + "webgpu/shader/execution/binary/af_multiplication.bin": "8f7b5f84", + "webgpu/shader/execution/binary/af_remainder.bin": "133a1dd7", + "webgpu/shader/execution/binary/af_subtraction.bin": "9148f3ce", + "webgpu/shader/execution/binary/f16_addition.bin": "d58a9371", "webgpu/shader/execution/binary/f16_logical.bin": "80f9e754", - "webgpu/shader/execution/binary/f16_division.bin": "53e3bdaa", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "911d18f7", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "a306da0a", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "be77024a", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "ea813df5", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "1692bec1", - "webgpu/shader/execution/binary/f16_multiplication.bin": "7c618e51", - "webgpu/shader/execution/binary/f16_remainder.bin": "9de2be15", - "webgpu/shader/execution/binary/f16_subtraction.bin": "c77bff8a", - "webgpu/shader/execution/binary/f32_addition.bin": "d4282b93", + "webgpu/shader/execution/binary/f16_division.bin": "5d2bf113", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "531954a6", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "b871cd18", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "3603d1db", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "c97537aa", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "273b76c6", + "webgpu/shader/execution/binary/f16_multiplication.bin": "d98cd937", + "webgpu/shader/execution/binary/f16_remainder.bin": "6e5227f4", + "webgpu/shader/execution/binary/f16_subtraction.bin": "d243bbad", + "webgpu/shader/execution/binary/f32_addition.bin": "c288aef6", "webgpu/shader/execution/binary/f32_logical.bin": "47467130", - "webgpu/shader/execution/binary/f32_division.bin": "4d79a5f6", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "5972ccf0", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "8bc3f04a", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "7376da03", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c4785f3a", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "20d4b1", - "webgpu/shader/execution/binary/f32_multiplication.bin": "7586ed6d", - "webgpu/shader/execution/binary/f32_remainder.bin": "59f46306", - "webgpu/shader/execution/binary/f32_subtraction.bin": "4311ba10", + "webgpu/shader/execution/binary/f32_division.bin": "c9c76117", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "88eedbfa", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "9f76f2ac", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "1d667df9", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "aa253cf1", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "98a63b3a", + "webgpu/shader/execution/binary/f32_multiplication.bin": "dce8dd5b", + "webgpu/shader/execution/binary/f32_remainder.bin": "d3bdb9e3", + "webgpu/shader/execution/binary/f32_subtraction.bin": "8a48e67a", "webgpu/shader/execution/binary/i32_arithmetic.bin": "d345f9c7", "webgpu/shader/execution/binary/i32_comparison.bin": "6d4ae7e0", "webgpu/shader/execution/binary/u32_arithmetic.bin": "163f8a38", "webgpu/shader/execution/binary/u32_comparison.bin": "f6c1497b", - "webgpu/shader/execution/abs.bin": "63fa988e", - "webgpu/shader/execution/acos.bin": "47521b3e", - "webgpu/shader/execution/acosh.bin": "59762b6f", - "webgpu/shader/execution/asin.bin": "db0c6641", - "webgpu/shader/execution/asinh.bin": "f0329d7f", - "webgpu/shader/execution/atan.bin": "f6fea79e", - "webgpu/shader/execution/atan2.bin": "1c390d0f", - "webgpu/shader/execution/atanh.bin": "4c430905", - "webgpu/shader/execution/bitcast.bin": "2dfc8f9", - "webgpu/shader/execution/ceil.bin": "3319384e", - "webgpu/shader/execution/clamp.bin": "4873dd79", - "webgpu/shader/execution/cos.bin": "c16d181a", - "webgpu/shader/execution/cosh.bin": "137f4997", - "webgpu/shader/execution/cross.bin": "aebda306", - "webgpu/shader/execution/degrees.bin": "5001b340", - "webgpu/shader/execution/determinant.bin": "c461c715", - "webgpu/shader/execution/distance.bin": "d05e2f8d", - "webgpu/shader/execution/dot.bin": "7f8b7e13", - "webgpu/shader/execution/exp.bin": "d448dc91", - "webgpu/shader/execution/exp2.bin": "1d232c0e", - "webgpu/shader/execution/faceForward.bin": "87c0576c", - "webgpu/shader/execution/floor.bin": "c3a60f36", - "webgpu/shader/execution/fma.bin": "6b94384d", - "webgpu/shader/execution/fract.bin": "d49c3d50", - "webgpu/shader/execution/frexp.bin": "252e0615", - "webgpu/shader/execution/inverseSqrt.bin": "1deb1185", - "webgpu/shader/execution/ldexp.bin": "617e4cc4", - "webgpu/shader/execution/length.bin": "f7b51c96", - "webgpu/shader/execution/log.bin": "ae124706", - "webgpu/shader/execution/log2.bin": "c428e9f2", - "webgpu/shader/execution/max.bin": "a45914cd", - "webgpu/shader/execution/min.bin": "1017e86d", - "webgpu/shader/execution/mix.bin": "3f5ebb6e", - "webgpu/shader/execution/modf.bin": "fedc0c50", - "webgpu/shader/execution/normalize.bin": "60173a1e", + "webgpu/shader/execution/abs.bin": "e21a06df", + "webgpu/shader/execution/acos.bin": "2a92d3d5", + "webgpu/shader/execution/acosh.bin": "c2cc2e23", + "webgpu/shader/execution/asin.bin": "2027460e", + "webgpu/shader/execution/asinh.bin": "21bac67b", + "webgpu/shader/execution/atan.bin": "6422056c", + "webgpu/shader/execution/atan2.bin": "d8ade832", + "webgpu/shader/execution/atanh.bin": "de1dd54c", + "webgpu/shader/execution/bitcast.bin": "fdd0874", + "webgpu/shader/execution/ceil.bin": "6596b2e2", + "webgpu/shader/execution/clamp.bin": "9a92d1b7", + "webgpu/shader/execution/cos.bin": "647f5fda", + "webgpu/shader/execution/cosh.bin": "5f847611", + "webgpu/shader/execution/cross.bin": "117f2e0f", + "webgpu/shader/execution/degrees.bin": "cadaa756", + "webgpu/shader/execution/determinant.bin": "f2df9222", + "webgpu/shader/execution/distance.bin": "d6a161d1", + "webgpu/shader/execution/dot.bin": "c788d592", + "webgpu/shader/execution/exp.bin": "a76c67ca", + "webgpu/shader/execution/exp2.bin": "a5b443c5", + "webgpu/shader/execution/faceForward.bin": "75f1f377", + "webgpu/shader/execution/floor.bin": "8aea5f9c", + "webgpu/shader/execution/fma.bin": "8682909f", + "webgpu/shader/execution/fract.bin": "ff3283f0", + "webgpu/shader/execution/frexp.bin": "9639418e", + "webgpu/shader/execution/inverseSqrt.bin": "9de3fa15", + "webgpu/shader/execution/ldexp.bin": "43fde30e", + "webgpu/shader/execution/length.bin": "24b87c1a", + "webgpu/shader/execution/log.bin": "c8447a16", + "webgpu/shader/execution/log2.bin": "bcefcef6", + "webgpu/shader/execution/max.bin": "bbd522ab", + "webgpu/shader/execution/min.bin": "4687f034", + "webgpu/shader/execution/mix.bin": "e79fc2e8", + "webgpu/shader/execution/modf.bin": "feedecc3", + "webgpu/shader/execution/normalize.bin": "78e7c0", "webgpu/shader/execution/pack2x16float.bin": "e472a927", - "webgpu/shader/execution/pow.bin": "c5a2a9c1", - "webgpu/shader/execution/quantizeToF16.bin": "8d1cb7a6", - "webgpu/shader/execution/radians.bin": "94cbdf6c", - "webgpu/shader/execution/reflect.bin": "39a479a0", - "webgpu/shader/execution/refract.bin": "63e5b11a", - "webgpu/shader/execution/round.bin": "13bf324", - "webgpu/shader/execution/saturate.bin": "c368b1a7", - "webgpu/shader/execution/sign.bin": "5cd05b5c", - "webgpu/shader/execution/sin.bin": "85679e", - "webgpu/shader/execution/sinh.bin": "eaa61750", - "webgpu/shader/execution/smoothstep.bin": "bd06d7cc", - "webgpu/shader/execution/sqrt.bin": "997f5572", - "webgpu/shader/execution/step.bin": "87505049", - "webgpu/shader/execution/tan.bin": "9d7ca121", - "webgpu/shader/execution/tanh.bin": "8bfa913c", - "webgpu/shader/execution/transpose.bin": "7f8fa4c6", - "webgpu/shader/execution/trunc.bin": "9a054523", - "webgpu/shader/execution/unpack2x16float.bin": "ed71c71d", - "webgpu/shader/execution/unpack2x16snorm.bin": "89b4cd2d", - "webgpu/shader/execution/unpack2x16unorm.bin": "df3b3851", - "webgpu/shader/execution/unpack4x8snorm.bin": "aa10073", - "webgpu/shader/execution/unpack4x8unorm.bin": "2fe14b98", - "webgpu/shader/execution/unary/af_arithmetic.bin": "accfc1bd", - "webgpu/shader/execution/unary/af_assignment.bin": "cb3e120e", + "webgpu/shader/execution/pow.bin": "38d0ff7", + "webgpu/shader/execution/quantizeToF16.bin": "5496ab3", + "webgpu/shader/execution/radians.bin": "db736290", + "webgpu/shader/execution/reflect.bin": "b08379ba", + "webgpu/shader/execution/refract.bin": "d360cf82", + "webgpu/shader/execution/round.bin": "3d2c318e", + "webgpu/shader/execution/saturate.bin": "33892d9c", + "webgpu/shader/execution/sign.bin": "8a54e6ec", + "webgpu/shader/execution/sin.bin": "f0cd3a32", + "webgpu/shader/execution/sinh.bin": "4d1b0134", + "webgpu/shader/execution/smoothstep.bin": "a3b004be", + "webgpu/shader/execution/sqrt.bin": "dd8a5970", + "webgpu/shader/execution/step.bin": "eec1579d", + "webgpu/shader/execution/tan.bin": "612d43b", + "webgpu/shader/execution/tanh.bin": "6f95242c", + "webgpu/shader/execution/transpose.bin": "b1a9a7f7", + "webgpu/shader/execution/trunc.bin": "cdf5939e", + "webgpu/shader/execution/unpack2x16float.bin": "b992957a", + "webgpu/shader/execution/unpack2x16snorm.bin": "394e0942", + "webgpu/shader/execution/unpack2x16unorm.bin": "78f0ef3c", + "webgpu/shader/execution/unpack4x8snorm.bin": "926f3f3e", + "webgpu/shader/execution/unpack4x8unorm.bin": "3640a996", + "webgpu/shader/execution/unary/af_arithmetic.bin": "bf05a1b1", + "webgpu/shader/execution/unary/af_assignment.bin": "d711e244", "webgpu/shader/execution/unary/bool_conversion.bin": "d0c1e5a3", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "310a05dd", - "webgpu/shader/execution/unary/f16_conversion.bin": "9d94aa72", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "fe955d67", - "webgpu/shader/execution/unary/f32_conversion.bin": "918f6cb3", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "6095e89f", + "webgpu/shader/execution/unary/f16_conversion.bin": "76546200", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "a16e7cf4", + "webgpu/shader/execution/unary/f32_conversion.bin": "62604b96", "webgpu/shader/execution/unary/i32_arithmetic.bin": "a8649cbb", "webgpu/shader/execution/unary/i32_conversion.bin": "e5157a69", "webgpu/shader/execution/unary/u32_conversion.bin": "d07d0c20", "webgpu/shader/execution/unary/ai_assignment.bin": "f62c765c", "webgpu/shader/execution/binary/ai_arithmetic.bin": "43501242", "webgpu/shader/execution/unary/ai_arithmetic.bin": "8e448c53", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "7ea0df71", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "4919c460", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "33c10dad" + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "c57c159e", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "d21017f3", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "ddd2f83a" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/determinant.bin b/src/resources/cache/webgpu/shader/execution/determinant.bin index cef09cd67bd1e75a9778c97c4687d60e5b2c7c2a..d09c7df7e8aa5ef074640574b7a6398b17d15ec6 100644 GIT binary patch literal 10368 zcmeHMy>1*g5T3h}0j07E6<4S@7%E&kk)2AgD9Q3qVOWJb7lC6ST@)ipk#Y+^$d!Hw z1EP({Q4AM(gnIPZZOBiwN8iy8^fSGr z-_iQR_0H%QI)n9<)4yTl8(_acCXgvEqWK3j8xgvJ29Hf9jPmvz-y9Gv-ec$-a**S< zVBdP53n{;U1R2FAMzD zA<@-g9*+gyIxKX7e{)3i<|xOnfL{U6vN+7+e+&O_3;%QYAjIjUz`s5wdVRcw!S3g~ zey$VjjnCty`M*8aHgEU9+dc4gh_|y@!dvWs<7rFhxyQpP_t{6_GWg*6T3% zKC#w*)WbfE5za$%9BJRcrzv=cRd7Y#xqC5~YayZm$oge%;PKMO()jyGwW}4uK7210 zSZldf>A{uq*iJ(hJm()iw5mfb%t_ThEWW;%L_z&ByJjD*x zYL0)2`AeHqO8TP-z{1to#owR+8ua6Jaf8L)K z=lG17yc5E?Q*GIJSN!dFgYvQVZs0h=_X{jfjfl2c^VPV&9phbbd;xd@nNHdNnuoj> z-L=>4kHH>Uh-iRA+u6jf<0*E{!-SvMC&yQt+J^Hp`+ zlwpYe#OFbV`AnZP%n2g8ppz8SJZqk1n1@*XgdV1t#q-7U4D*Qo!skhfF;C6Y6k|{6 z5}(H@rg_o4@U^dN&nvZWz1O4nx}D$p9c18p@oYA8`OO&}+{_bYbQNe&if;ukz2=l0oAk@MZT@R6)%d^8 zAz$}V|JOOhC4z23Ueb@mzgPW=)+^}zmo3VF$HF&3H<6C5*HVeJBEcB))Kwrs#qoIl z4Zaw2b>NY^*6XWt(eZb$%-=1+-Lm1Fk@>^P!C!*g7M+}xCeZk*1-TOC}aord7KkxR} zoO(QTx4#|nw`0j&s&~8n(LKT6tN1&X@r8BnDCmm6Gr^ykzMS_bs50mI|L%IV@?HlT zZHTV+yPt2I9rrI?SD$O($AO;_c{0==w{De3Rw6c{tNn57w)iIN=>68lm6w~9JdkLi zy7zfY)`@)dGqkVswco~%yI}wvh@#EGlc|O$Fb+s;3>Va+(boV~D>Vff7 zv@X=fcNI7C`7UIBtQUUW>-LWa*X^q>{)AT67yjR&UdL8ijarI+&0fI6ds*!|PjzXh z>*r4zujXU5E}i>@6sPNmoj)$#!Fpz8V~pssU$ZMtbqhBx-ddg0eT?;v+|}w! z94))8;MG2u=aJWGKD6thUq|bu@8R5Ia(n34?6!he`^b2;o|Sbr^u@pCYp@$1&voaw zapUq~s6NNKoW~dDN!%qa^sS#4@S#60-dY_Gbzc0JxLD_XJOOWter5j7$b1-lKgfDD RT u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .combineWithParams([ // Extreme values, i.e. subnormals, very large magnitudes, and those lead to diff --git a/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts index 379b42b3e63c..4eab44009abe 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts @@ -78,7 +78,21 @@ const f16_cases = ([2, 3, 4] as const) ) .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', + FP.abstract.determinantInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + export const d = makeCaseCache('determinant', { ...f32_cases, ...f16_cases, + ...abstract_cases, }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts index 8b259eedb33e..638af80acaf5 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts @@ -9,9 +9,9 @@ Returns the determinant of e. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { Type } from '../../../../../util/conversion.js'; -import { allInputSources, run } from '../../expression.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); @@ -19,8 +19,19 @@ export const g = makeTestGroup(GPUTest); 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') diff --git a/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index 9b6fbeac39bd..36900e6da994 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -5128,10 +5128,7 @@ class FPAbstractTraits extends FPTraits { public readonly coshInterval = this.unimplementedScalarToInterval.bind(this, 'coshInterval'); public readonly crossInterval = this.crossIntervalImpl.bind(this); public readonly degreesInterval = this.degreesIntervalImpl.bind(this); - public readonly determinantInterval = this.unimplementedMatrixToInterval.bind( - this, - 'determinantInterval' - ); + public readonly determinantInterval = this.determinantIntervalImpl.bind(this); public readonly distanceInterval = this.unimplementedDistance.bind(this); public readonly divisionInterval = ( x: number | FPInterval, From 7af17da35eef7d472a11edaa5cf8b3741571ae20 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Tue, 12 Mar 2024 13:07:43 -0400 Subject: [PATCH 27/33] Further restrict AF matrix-matrix multiplication cases Additionally correct how selectNCases is implemented, so negative hashes do not occur. Fixes #3483 --- src/resources/cache/hashes.json | 204 +++++++++--------- .../execution/binary/af_matrix_addition.bin | Bin 631152 -> 175672 bytes .../af_matrix_matrix_multiplication.bin | Bin 1617464 -> 111192 bytes .../af_matrix_scalar_multiplication.bin | Bin 963656 -> 291528 bytes .../binary/af_matrix_subtraction.bin | Bin 644432 -> 181112 bytes .../af_matrix_vector_multiplication.bin | Bin 577904 -> 188608 bytes .../af_matrix_matrix_multiplication.cache.ts | 2 +- .../shader/execution/expression/case.ts | 4 +- 8 files changed, 106 insertions(+), 104 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 1dfb7379313f..2602b6a27686 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "f79f0374", - "webgpu/shader/execution/binary/af_logical.bin": "9ab41597", - "webgpu/shader/execution/binary/af_division.bin": "770fc018", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "a401d81a", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "a9e51912", - "webgpu/shader/execution/binary/af_multiplication.bin": "8f7b5f84", - "webgpu/shader/execution/binary/af_remainder.bin": "133a1dd7", - "webgpu/shader/execution/binary/af_subtraction.bin": "9148f3ce", - "webgpu/shader/execution/binary/f16_addition.bin": "d58a9371", - "webgpu/shader/execution/binary/f16_logical.bin": "80f9e754", - "webgpu/shader/execution/binary/f16_division.bin": "5d2bf113", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "531954a6", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "b871cd18", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "3603d1db", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "c97537aa", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "273b76c6", - "webgpu/shader/execution/binary/f16_multiplication.bin": "d98cd937", - "webgpu/shader/execution/binary/f16_remainder.bin": "6e5227f4", - "webgpu/shader/execution/binary/f16_subtraction.bin": "d243bbad", - "webgpu/shader/execution/binary/f32_addition.bin": "c288aef6", - "webgpu/shader/execution/binary/f32_logical.bin": "47467130", - "webgpu/shader/execution/binary/f32_division.bin": "c9c76117", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "88eedbfa", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "9f76f2ac", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "1d667df9", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "aa253cf1", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "98a63b3a", - "webgpu/shader/execution/binary/f32_multiplication.bin": "dce8dd5b", - "webgpu/shader/execution/binary/f32_remainder.bin": "d3bdb9e3", - "webgpu/shader/execution/binary/f32_subtraction.bin": "8a48e67a", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "d345f9c7", - "webgpu/shader/execution/binary/i32_comparison.bin": "6d4ae7e0", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "163f8a38", - "webgpu/shader/execution/binary/u32_comparison.bin": "f6c1497b", - "webgpu/shader/execution/abs.bin": "e21a06df", - "webgpu/shader/execution/acos.bin": "2a92d3d5", - "webgpu/shader/execution/acosh.bin": "c2cc2e23", - "webgpu/shader/execution/asin.bin": "2027460e", - "webgpu/shader/execution/asinh.bin": "21bac67b", - "webgpu/shader/execution/atan.bin": "6422056c", - "webgpu/shader/execution/atan2.bin": "d8ade832", - "webgpu/shader/execution/atanh.bin": "de1dd54c", - "webgpu/shader/execution/bitcast.bin": "fdd0874", - "webgpu/shader/execution/ceil.bin": "6596b2e2", - "webgpu/shader/execution/clamp.bin": "9a92d1b7", - "webgpu/shader/execution/cos.bin": "647f5fda", - "webgpu/shader/execution/cosh.bin": "5f847611", - "webgpu/shader/execution/cross.bin": "117f2e0f", - "webgpu/shader/execution/degrees.bin": "cadaa756", - "webgpu/shader/execution/determinant.bin": "f2df9222", - "webgpu/shader/execution/distance.bin": "d6a161d1", - "webgpu/shader/execution/dot.bin": "c788d592", - "webgpu/shader/execution/exp.bin": "a76c67ca", - "webgpu/shader/execution/exp2.bin": "a5b443c5", - "webgpu/shader/execution/faceForward.bin": "75f1f377", - "webgpu/shader/execution/floor.bin": "8aea5f9c", - "webgpu/shader/execution/fma.bin": "8682909f", - "webgpu/shader/execution/fract.bin": "ff3283f0", - "webgpu/shader/execution/frexp.bin": "9639418e", - "webgpu/shader/execution/inverseSqrt.bin": "9de3fa15", - "webgpu/shader/execution/ldexp.bin": "43fde30e", - "webgpu/shader/execution/length.bin": "24b87c1a", - "webgpu/shader/execution/log.bin": "c8447a16", - "webgpu/shader/execution/log2.bin": "bcefcef6", - "webgpu/shader/execution/max.bin": "bbd522ab", - "webgpu/shader/execution/min.bin": "4687f034", - "webgpu/shader/execution/mix.bin": "e79fc2e8", - "webgpu/shader/execution/modf.bin": "feedecc3", - "webgpu/shader/execution/normalize.bin": "78e7c0", - "webgpu/shader/execution/pack2x16float.bin": "e472a927", - "webgpu/shader/execution/pow.bin": "38d0ff7", - "webgpu/shader/execution/quantizeToF16.bin": "5496ab3", - "webgpu/shader/execution/radians.bin": "db736290", - "webgpu/shader/execution/reflect.bin": "b08379ba", - "webgpu/shader/execution/refract.bin": "d360cf82", - "webgpu/shader/execution/round.bin": "3d2c318e", - "webgpu/shader/execution/saturate.bin": "33892d9c", - "webgpu/shader/execution/sign.bin": "8a54e6ec", - "webgpu/shader/execution/sin.bin": "f0cd3a32", - "webgpu/shader/execution/sinh.bin": "4d1b0134", - "webgpu/shader/execution/smoothstep.bin": "a3b004be", - "webgpu/shader/execution/sqrt.bin": "dd8a5970", - "webgpu/shader/execution/step.bin": "eec1579d", - "webgpu/shader/execution/tan.bin": "612d43b", - "webgpu/shader/execution/tanh.bin": "6f95242c", - "webgpu/shader/execution/transpose.bin": "b1a9a7f7", - "webgpu/shader/execution/trunc.bin": "cdf5939e", - "webgpu/shader/execution/unpack2x16float.bin": "b992957a", - "webgpu/shader/execution/unpack2x16snorm.bin": "394e0942", - "webgpu/shader/execution/unpack2x16unorm.bin": "78f0ef3c", - "webgpu/shader/execution/unpack4x8snorm.bin": "926f3f3e", - "webgpu/shader/execution/unpack4x8unorm.bin": "3640a996", - "webgpu/shader/execution/unary/af_arithmetic.bin": "bf05a1b1", - "webgpu/shader/execution/unary/af_assignment.bin": "d711e244", + "webgpu/shader/execution/binary/af_addition.bin": "f50d0054", + "webgpu/shader/execution/binary/af_logical.bin": "ef38f267", + "webgpu/shader/execution/binary/af_division.bin": "dabbb1d1", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "8d41501a", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "2b5eb822", + "webgpu/shader/execution/binary/af_multiplication.bin": "6fb45595", + "webgpu/shader/execution/binary/af_remainder.bin": "25e662b1", + "webgpu/shader/execution/binary/af_subtraction.bin": "37703233", + "webgpu/shader/execution/binary/f16_addition.bin": "a712df38", + "webgpu/shader/execution/binary/f16_logical.bin": "e4f7d8", + "webgpu/shader/execution/binary/f16_division.bin": "4793560a", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "a079337b", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "65fe996", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "3f97acdd", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "1fc983d8", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "7e97d699", + "webgpu/shader/execution/binary/f16_multiplication.bin": "c77e8821", + "webgpu/shader/execution/binary/f16_remainder.bin": "e57bdbd7", + "webgpu/shader/execution/binary/f16_subtraction.bin": "85420ccf", + "webgpu/shader/execution/binary/f32_addition.bin": "d2790380", + "webgpu/shader/execution/binary/f32_logical.bin": "d8a1e4a4", + "webgpu/shader/execution/binary/f32_division.bin": "a75642db", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "5b0b0511", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "671efb98", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "bea0478c", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "e6fc9dfb", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "b6b1b664", + "webgpu/shader/execution/binary/f32_multiplication.bin": "8c8cf117", + "webgpu/shader/execution/binary/f32_remainder.bin": "43e749be", + "webgpu/shader/execution/binary/f32_subtraction.bin": "50da2154", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "ecb16a4f", + "webgpu/shader/execution/binary/i32_comparison.bin": "579cc94", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "e08964b4", + "webgpu/shader/execution/binary/u32_comparison.bin": "8c8538e1", + "webgpu/shader/execution/abs.bin": "5c17e46c", + "webgpu/shader/execution/acos.bin": "120bfdf", + "webgpu/shader/execution/acosh.bin": "942cc2bb", + "webgpu/shader/execution/asin.bin": "fe636cf0", + "webgpu/shader/execution/asinh.bin": "61d55466", + "webgpu/shader/execution/atan.bin": "cac44506", + "webgpu/shader/execution/atan2.bin": "3403e5f", + "webgpu/shader/execution/atanh.bin": "6221541c", + "webgpu/shader/execution/bitcast.bin": "945e5c2a", + "webgpu/shader/execution/ceil.bin": "ad590261", + "webgpu/shader/execution/clamp.bin": "fb7095fa", + "webgpu/shader/execution/cos.bin": "f76f3cfc", + "webgpu/shader/execution/cosh.bin": "23274e7d", + "webgpu/shader/execution/cross.bin": "d81d20b", + "webgpu/shader/execution/degrees.bin": "ad65e311", + "webgpu/shader/execution/determinant.bin": "eb512a79", + "webgpu/shader/execution/distance.bin": "868585b7", + "webgpu/shader/execution/dot.bin": "db38aa67", + "webgpu/shader/execution/exp.bin": "62705ef9", + "webgpu/shader/execution/exp2.bin": "54d0df5e", + "webgpu/shader/execution/faceForward.bin": "f7e3a12b", + "webgpu/shader/execution/floor.bin": "6083291e", + "webgpu/shader/execution/fma.bin": "3cb81190", + "webgpu/shader/execution/fract.bin": "d000d278", + "webgpu/shader/execution/frexp.bin": "15d2eb99", + "webgpu/shader/execution/inverseSqrt.bin": "98598f6b", + "webgpu/shader/execution/ldexp.bin": "7cda090b", + "webgpu/shader/execution/length.bin": "a092226a", + "webgpu/shader/execution/log.bin": "4afb8069", + "webgpu/shader/execution/log2.bin": "5c10d479", + "webgpu/shader/execution/max.bin": "38cb6596", + "webgpu/shader/execution/min.bin": "715e13fc", + "webgpu/shader/execution/mix.bin": "67a69982", + "webgpu/shader/execution/modf.bin": "e9d534e0", + "webgpu/shader/execution/normalize.bin": "ec5df722", + "webgpu/shader/execution/pack2x16float.bin": "e4852967", + "webgpu/shader/execution/pow.bin": "8b43d36d", + "webgpu/shader/execution/quantizeToF16.bin": "34152620", + "webgpu/shader/execution/radians.bin": "9953e412", + "webgpu/shader/execution/reflect.bin": "59fae21b", + "webgpu/shader/execution/refract.bin": "79650096", + "webgpu/shader/execution/round.bin": "b4533213", + "webgpu/shader/execution/saturate.bin": "37ca84d0", + "webgpu/shader/execution/sign.bin": "c2e029fd", + "webgpu/shader/execution/sin.bin": "14319b5", + "webgpu/shader/execution/sinh.bin": "bfa704c1", + "webgpu/shader/execution/smoothstep.bin": "6370f69a", + "webgpu/shader/execution/sqrt.bin": "98926bdc", + "webgpu/shader/execution/step.bin": "7375ba92", + "webgpu/shader/execution/tan.bin": "9bc439ef", + "webgpu/shader/execution/tanh.bin": "a3a298d2", + "webgpu/shader/execution/transpose.bin": "e9f7ab2e", + "webgpu/shader/execution/trunc.bin": "49dfcdee", + "webgpu/shader/execution/unpack2x16float.bin": "d93977ea", + "webgpu/shader/execution/unpack2x16snorm.bin": "764c0f13", + "webgpu/shader/execution/unpack2x16unorm.bin": "cf870fc1", + "webgpu/shader/execution/unpack4x8snorm.bin": "3ed6a27e", + "webgpu/shader/execution/unpack4x8unorm.bin": "8df9108c", + "webgpu/shader/execution/unary/af_arithmetic.bin": "80129d46", + "webgpu/shader/execution/unary/af_assignment.bin": "966a724a", "webgpu/shader/execution/unary/bool_conversion.bin": "d0c1e5a3", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "6095e89f", - "webgpu/shader/execution/unary/f16_conversion.bin": "76546200", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "a16e7cf4", - "webgpu/shader/execution/unary/f32_conversion.bin": "62604b96", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "408620de", + "webgpu/shader/execution/unary/f16_conversion.bin": "46c02cf1", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "378a4095", + "webgpu/shader/execution/unary/f32_conversion.bin": "4743152f", "webgpu/shader/execution/unary/i32_arithmetic.bin": "a8649cbb", "webgpu/shader/execution/unary/i32_conversion.bin": "e5157a69", "webgpu/shader/execution/unary/u32_conversion.bin": "d07d0c20", "webgpu/shader/execution/unary/ai_assignment.bin": "f62c765c", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "43501242", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "a82361ec", "webgpu/shader/execution/unary/ai_arithmetic.bin": "8e448c53", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "c57c159e", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "d21017f3", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "ddd2f83a" + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "d55eea17", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "31afee59", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "255e4937" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_matrix_addition.bin b/src/resources/cache/webgpu/shader/execution/binary/af_matrix_addition.bin index 634f518e0d1262f906cbb4cdcc9792969fe60583..ba9d123cbf412eb03ee8bb82721af7464d9c92e7 100644 GIT binary patch delta 5550 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∈vB_cu?ep9Q*( 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 z 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 TBXhsS4K}~4S()*ymLdK>etiX> delta 53205 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 diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.cache.ts index c7540083d6ff..e539ee1db5da 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.cache.ts @@ -11,7 +11,7 @@ const mat_mat_cases = ([2, 3, 4] as const) [`mat${k}x${rows}_mat${cols}x${k}`]: () => { return selectNCases( 'binary/af_matrix_matrix_multiplication', - 50, + 10, FP.abstract.generateMatrixPairToMatrixCases( sparseMatrixF64Range(k, rows), sparseMatrixF64Range(cols, k), diff --git a/src/webgpu/shader/execution/expression/case.ts b/src/webgpu/shader/execution/expression/case.ts index 063b58da4267..d837b1c32fb4 100644 --- a/src/webgpu/shader/execution/expression/case.ts +++ b/src/webgpu/shader/execution/expression/case.ts @@ -69,7 +69,9 @@ export function selectNCases(dis: string, n: number, cases: Case[]): Case[] { return cases; } const dis_crc32 = crc32(dis); - return cases.filter(c => n * (0xffff_ffff / count) > (crc32(c.input.toString()) ^ dis_crc32)); + return cases.filter( + c => Math.trunc((n / count) * 0xffff_ffff) > (crc32(c.input.toString()) ^ dis_crc32) >>> 0 + ); } /** From 53158b8d0d34e9668671f614b8044e86815f1fc2 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 12 Mar 2024 16:44:23 +0000 Subject: [PATCH 28/33] shader/execution: Fix composite abstract runtime indexing Extends the fix of #3379 to cover composite types. --- src/webgpu/shader/execution/expression/expression.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webgpu/shader/execution/expression/expression.ts b/src/webgpu/shader/execution/expression/expression.ts index 50885c0d3594..0359e6e901a1 100644 --- a/src/webgpu/shader/execution/expression/expression.ts +++ b/src/webgpu/shader/execution/expression/expression.ts @@ -14,6 +14,7 @@ import { isAbstractType, scalarTypeOf, ArrayType, + elementTypeOf, } from '../../../util/conversion.js'; import { align } from '../../../util/math.js'; @@ -618,7 +619,7 @@ function basicExpressionShaderBody( // Constant eval ////////////////////////////////////////////////////////////////////////// let body = ''; - if (parameterTypes.some(isAbstractType)) { + if (parameterTypes.some(ty => isAbstractType(elementTypeOf(ty)))) { // Directly assign the expression to the output, to avoid an // intermediate store, which will concretize the value early body = cases From 3c2c107f704904ccaf9b7a89b2c8441095b44f2d Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Tue, 12 Mar 2024 13:55:24 -0700 Subject: [PATCH 29/33] Restore ?worker and ?worker=1 to mean ?worker=dedicated (#3485) The parsing for `?worker` and `?worker=1` changed with the addition of `?worker=shared` but these should keep being treated the way they were before. --- docs/intro/developing.md | 2 +- src/common/internal/query/query.ts | 4 +- src/common/runtime/helper/options.ts | 34 ++- src/common/runtime/standalone.ts | 2 +- src/common/runtime/wpt.ts | 4 +- src/resources/cache/hashes.json | 216 +++++++++--------- src/webgpu/web_platform/worker/worker.spec.ts | 7 +- 7 files changed, 147 insertions(+), 122 deletions(-) diff --git a/docs/intro/developing.md b/docs/intro/developing.md index 23da946b1e0c..c706e0c1c1b8 100644 --- a/docs/intro/developing.md +++ b/docs/intro/developing.md @@ -56,7 +56,7 @@ The following url parameters change how the harness runs: - `runnow=1` runs all matching tests on page load. - `debug=1` enables verbose debug logging from tests. -- `worker=dedicated` runs the tests on a dedicated worker instead of the main thread. +- `worker=dedicated` (or `worker` or `worker=1`) runs the tests on a dedicated worker instead of the main thread. - `worker=shared` runs the tests on a shared worker instead of the main thread. - `worker=service` runs the tests on a service worker instead of the main thread. - `power_preference=low-power` runs most tests passing `powerPreference: low-power` to `requestAdapter` diff --git a/src/common/internal/query/query.ts b/src/common/internal/query/query.ts index ad7cf246e9d9..676ac46d3888 100644 --- a/src/common/internal/query/query.ts +++ b/src/common/internal/query/query.ts @@ -1,5 +1,5 @@ import { TestParams } from '../../framework/fixture.js'; -import { optionString } from '../../runtime/helper/options.js'; +import { optionWorkerMode } from '../../runtime/helper/options.js'; import { assert, unreachable } from '../../util/util.js'; import { Expectation } from '../logging/result.js'; @@ -193,7 +193,7 @@ Expectation should be of the form path/to/cts.https.html?debug=0&q=suite:test_pa ); const params = expectationURL.searchParams; - if (optionString('worker', params) !== optionString('worker', wptURL.searchParams)) { + if (optionWorkerMode('worker', params) !== optionWorkerMode('worker', wptURL.searchParams)) { continue; } diff --git a/src/common/runtime/helper/options.ts b/src/common/runtime/helper/options.ts index 67fd00372d39..38000341d945 100644 --- a/src/common/runtime/helper/options.ts +++ b/src/common/runtime/helper/options.ts @@ -1,3 +1,5 @@ +import { unreachable } from '../../util/util.js'; + let windowURL: URL | undefined = undefined; function getWindowURL() { if (windowURL === undefined) { @@ -6,6 +8,7 @@ function getWindowURL() { return windowURL; } +/** Parse a runner option that is always boolean-typed. False if missing or '0'. */ export function optionEnabled( opt: string, searchParams: URLSearchParams = getWindowURL().searchParams @@ -14,6 +17,7 @@ export function optionEnabled( return val !== null && val !== '0'; } +/** Parse a runner option that is always string-typed. If the option is missing, returns `''`. */ export function optionString( opt: string, searchParams: URLSearchParams = getWindowURL().searchParams @@ -21,20 +25,40 @@ export function optionString( return searchParams.get(opt) || ''; } +/** Runtime modes for whether to run tests in a worker. '0' means no worker. */ +type WorkerMode = '0' | 'dedicated' | 'service' | 'shared'; +/** Parse a runner option for different worker modes (as in `?worker=shared`). */ +export function optionWorkerMode( + opt: string, + searchParams: URLSearchParams = getWindowURL().searchParams +): WorkerMode { + const value = searchParams.get(opt); + if (value === null || value === '0') { + return '0'; + } else if (value === 'service') { + return 'service'; + } else if (value === 'shared') { + return 'shared'; + } else if (value === '' || value === '1' || value === 'dedicated') { + return 'dedicated'; + } + unreachable('invalid worker= option value'); +} + /** * The possible options for the tests. */ export interface CTSOptions { - worker?: 'dedicated' | 'shared' | 'service' | ''; + worker: WorkerMode; debug: boolean; compatibility: boolean; forceFallbackAdapter: boolean; unrollConstEvalLoops: boolean; - powerPreference?: GPUPowerPreference | ''; + powerPreference: GPUPowerPreference | ''; } export const kDefaultCTSOptions: CTSOptions = { - worker: '', + worker: '0', debug: true, compatibility: false, forceFallbackAdapter: false, @@ -63,9 +87,9 @@ export type OptionsInfos = Record; export const kCTSOptionsInfo: OptionsInfos = { worker: { description: 'run in a worker', - parser: optionString, + parser: optionWorkerMode, selectValueDescriptions: [ - { value: '', description: 'no worker' }, + { value: '0', description: 'no worker' }, { value: 'dedicated', description: 'dedicated worker' }, { value: 'shared', description: 'shared worker' }, { value: 'service', description: 'service worker' }, diff --git a/src/common/runtime/standalone.ts b/src/common/runtime/standalone.ts index 62090ee5dc92..385de9937e09 100644 --- a/src/common/runtime/standalone.ts +++ b/src/common/runtime/standalone.ts @@ -550,7 +550,7 @@ function keyValueToPairs([k, v]: [string, ParamValue]): [string, string][] { */ function prepareParams(params: Record): string { const pairsArrays = Object.entries(params) - .filter(([, v]) => !!v) + .filter(([, v]) => !!v && v !== '0') .map(keyValueToPairs); const pairs = pairsArrays.flat(); return new URLSearchParams(pairs).toString(); diff --git a/src/common/runtime/wpt.ts b/src/common/runtime/wpt.ts index 56ff9649e045..79ed1b5924dd 100644 --- a/src/common/runtime/wpt.ts +++ b/src/common/runtime/wpt.ts @@ -8,7 +8,7 @@ import { parseQuery } from '../internal/query/parseQuery.js'; import { parseExpectationsForTestQuery, relativeQueryString } from '../internal/query/query.js'; import { assert } from '../util/util.js'; -import { optionEnabled, optionString } from './helper/options.js'; +import { optionEnabled, optionWorkerMode } from './helper/options.js'; import { TestDedicatedWorker, TestServiceWorker, TestSharedWorker } from './helper/test_worker.js'; // testharness.js API (https://web-platform-tests.org/writing-tests/testharness-api.html) @@ -31,7 +31,7 @@ setup({ }); void (async () => { - const workerString = optionString('worker'); + const workerString = optionWorkerMode('worker'); const dedicatedWorker = workerString === 'dedicated' ? new TestDedicatedWorker() : undefined; const sharedWorker = workerString === 'shared' ? new TestSharedWorker() : undefined; const serviceWorker = workerString === 'service' ? new TestServiceWorker() : undefined; diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 2602b6a27686..9692310ae56e 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "f50d0054", - "webgpu/shader/execution/binary/af_logical.bin": "ef38f267", - "webgpu/shader/execution/binary/af_division.bin": "dabbb1d1", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "8d41501a", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "2b5eb822", - "webgpu/shader/execution/binary/af_multiplication.bin": "6fb45595", - "webgpu/shader/execution/binary/af_remainder.bin": "25e662b1", - "webgpu/shader/execution/binary/af_subtraction.bin": "37703233", - "webgpu/shader/execution/binary/f16_addition.bin": "a712df38", - "webgpu/shader/execution/binary/f16_logical.bin": "e4f7d8", - "webgpu/shader/execution/binary/f16_division.bin": "4793560a", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "a079337b", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "65fe996", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "3f97acdd", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "1fc983d8", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "7e97d699", - "webgpu/shader/execution/binary/f16_multiplication.bin": "c77e8821", - "webgpu/shader/execution/binary/f16_remainder.bin": "e57bdbd7", - "webgpu/shader/execution/binary/f16_subtraction.bin": "85420ccf", - "webgpu/shader/execution/binary/f32_addition.bin": "d2790380", - "webgpu/shader/execution/binary/f32_logical.bin": "d8a1e4a4", - "webgpu/shader/execution/binary/f32_division.bin": "a75642db", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "5b0b0511", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "671efb98", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "bea0478c", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "e6fc9dfb", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "b6b1b664", - "webgpu/shader/execution/binary/f32_multiplication.bin": "8c8cf117", - "webgpu/shader/execution/binary/f32_remainder.bin": "43e749be", - "webgpu/shader/execution/binary/f32_subtraction.bin": "50da2154", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "ecb16a4f", - "webgpu/shader/execution/binary/i32_comparison.bin": "579cc94", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "e08964b4", - "webgpu/shader/execution/binary/u32_comparison.bin": "8c8538e1", - "webgpu/shader/execution/abs.bin": "5c17e46c", - "webgpu/shader/execution/acos.bin": "120bfdf", - "webgpu/shader/execution/acosh.bin": "942cc2bb", - "webgpu/shader/execution/asin.bin": "fe636cf0", - "webgpu/shader/execution/asinh.bin": "61d55466", - "webgpu/shader/execution/atan.bin": "cac44506", - "webgpu/shader/execution/atan2.bin": "3403e5f", - "webgpu/shader/execution/atanh.bin": "6221541c", - "webgpu/shader/execution/bitcast.bin": "945e5c2a", - "webgpu/shader/execution/ceil.bin": "ad590261", - "webgpu/shader/execution/clamp.bin": "fb7095fa", - "webgpu/shader/execution/cos.bin": "f76f3cfc", - "webgpu/shader/execution/cosh.bin": "23274e7d", - "webgpu/shader/execution/cross.bin": "d81d20b", - "webgpu/shader/execution/degrees.bin": "ad65e311", - "webgpu/shader/execution/determinant.bin": "eb512a79", - "webgpu/shader/execution/distance.bin": "868585b7", - "webgpu/shader/execution/dot.bin": "db38aa67", - "webgpu/shader/execution/exp.bin": "62705ef9", - "webgpu/shader/execution/exp2.bin": "54d0df5e", - "webgpu/shader/execution/faceForward.bin": "f7e3a12b", - "webgpu/shader/execution/floor.bin": "6083291e", - "webgpu/shader/execution/fma.bin": "3cb81190", - "webgpu/shader/execution/fract.bin": "d000d278", - "webgpu/shader/execution/frexp.bin": "15d2eb99", - "webgpu/shader/execution/inverseSqrt.bin": "98598f6b", - "webgpu/shader/execution/ldexp.bin": "7cda090b", - "webgpu/shader/execution/length.bin": "a092226a", - "webgpu/shader/execution/log.bin": "4afb8069", - "webgpu/shader/execution/log2.bin": "5c10d479", - "webgpu/shader/execution/max.bin": "38cb6596", - "webgpu/shader/execution/min.bin": "715e13fc", - "webgpu/shader/execution/mix.bin": "67a69982", - "webgpu/shader/execution/modf.bin": "e9d534e0", - "webgpu/shader/execution/normalize.bin": "ec5df722", - "webgpu/shader/execution/pack2x16float.bin": "e4852967", - "webgpu/shader/execution/pow.bin": "8b43d36d", - "webgpu/shader/execution/quantizeToF16.bin": "34152620", - "webgpu/shader/execution/radians.bin": "9953e412", - "webgpu/shader/execution/reflect.bin": "59fae21b", - "webgpu/shader/execution/refract.bin": "79650096", - "webgpu/shader/execution/round.bin": "b4533213", - "webgpu/shader/execution/saturate.bin": "37ca84d0", - "webgpu/shader/execution/sign.bin": "c2e029fd", - "webgpu/shader/execution/sin.bin": "14319b5", - "webgpu/shader/execution/sinh.bin": "bfa704c1", - "webgpu/shader/execution/smoothstep.bin": "6370f69a", - "webgpu/shader/execution/sqrt.bin": "98926bdc", - "webgpu/shader/execution/step.bin": "7375ba92", - "webgpu/shader/execution/tan.bin": "9bc439ef", - "webgpu/shader/execution/tanh.bin": "a3a298d2", - "webgpu/shader/execution/transpose.bin": "e9f7ab2e", - "webgpu/shader/execution/trunc.bin": "49dfcdee", - "webgpu/shader/execution/unpack2x16float.bin": "d93977ea", - "webgpu/shader/execution/unpack2x16snorm.bin": "764c0f13", - "webgpu/shader/execution/unpack2x16unorm.bin": "cf870fc1", - "webgpu/shader/execution/unpack4x8snorm.bin": "3ed6a27e", - "webgpu/shader/execution/unpack4x8unorm.bin": "8df9108c", - "webgpu/shader/execution/unary/af_arithmetic.bin": "80129d46", - "webgpu/shader/execution/unary/af_assignment.bin": "966a724a", - "webgpu/shader/execution/unary/bool_conversion.bin": "d0c1e5a3", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "408620de", - "webgpu/shader/execution/unary/f16_conversion.bin": "46c02cf1", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "378a4095", - "webgpu/shader/execution/unary/f32_conversion.bin": "4743152f", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "a8649cbb", - "webgpu/shader/execution/unary/i32_conversion.bin": "e5157a69", - "webgpu/shader/execution/unary/u32_conversion.bin": "d07d0c20", - "webgpu/shader/execution/unary/ai_assignment.bin": "f62c765c", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "a82361ec", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "8e448c53", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "d55eea17", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "31afee59", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "255e4937" + "webgpu/shader/execution/binary/af_addition.bin": "f93f6f2", + "webgpu/shader/execution/binary/af_logical.bin": "412fdd40", + "webgpu/shader/execution/binary/af_division.bin": "7b359c01", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "ad3e51de", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "10ae4166", + "webgpu/shader/execution/binary/af_multiplication.bin": "f3eb97ee", + "webgpu/shader/execution/binary/af_remainder.bin": "1c08510d", + "webgpu/shader/execution/binary/af_subtraction.bin": "1ea78ded", + "webgpu/shader/execution/binary/f16_addition.bin": "97c6c220", + "webgpu/shader/execution/binary/f16_logical.bin": "2d07c65d", + "webgpu/shader/execution/binary/f16_division.bin": "fd11268e", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "6e8f752c", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "ebfe95b8", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "21577c69", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "ca569737", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "8d7a02b5", + "webgpu/shader/execution/binary/f16_multiplication.bin": "aee2b921", + "webgpu/shader/execution/binary/f16_remainder.bin": "f9397246", + "webgpu/shader/execution/binary/f16_subtraction.bin": "8f80b4b4", + "webgpu/shader/execution/binary/f32_addition.bin": "3b09cfd7", + "webgpu/shader/execution/binary/f32_logical.bin": "ab23c69a", + "webgpu/shader/execution/binary/f32_division.bin": "2e8d775b", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "7276a303", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "26789d70", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "beb34505", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "1b426bf8", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "ee372ef3", + "webgpu/shader/execution/binary/f32_multiplication.bin": "a58fb275", + "webgpu/shader/execution/binary/f32_remainder.bin": "4fca1678", + "webgpu/shader/execution/binary/f32_subtraction.bin": "a77fcae2", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "483b584e", + "webgpu/shader/execution/binary/i32_comparison.bin": "a1310d6d", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "4ed255b", + "webgpu/shader/execution/binary/u32_comparison.bin": "2766735c", + "webgpu/shader/execution/abs.bin": "74007d00", + "webgpu/shader/execution/acos.bin": "71afa785", + "webgpu/shader/execution/acosh.bin": "7eb972b3", + "webgpu/shader/execution/asin.bin": "cf1f15dc", + "webgpu/shader/execution/asinh.bin": "77d8090c", + "webgpu/shader/execution/atan.bin": "f984effa", + "webgpu/shader/execution/atan2.bin": "f71eeef0", + "webgpu/shader/execution/atanh.bin": "f70ef8f3", + "webgpu/shader/execution/bitcast.bin": "61596841", + "webgpu/shader/execution/ceil.bin": "d3912020", + "webgpu/shader/execution/clamp.bin": "3af7b577", + "webgpu/shader/execution/cos.bin": "a1b9931a", + "webgpu/shader/execution/cosh.bin": "9d4816da", + "webgpu/shader/execution/cross.bin": "75ee9e89", + "webgpu/shader/execution/degrees.bin": "1c88f619", + "webgpu/shader/execution/determinant.bin": "16acfafa", + "webgpu/shader/execution/distance.bin": "1098d127", + "webgpu/shader/execution/dot.bin": "e1a8d3bc", + "webgpu/shader/execution/exp.bin": "cdb0e2b9", + "webgpu/shader/execution/exp2.bin": "665ec45a", + "webgpu/shader/execution/faceForward.bin": "f22e0c0a", + "webgpu/shader/execution/floor.bin": "ede6258", + "webgpu/shader/execution/fma.bin": "34c7474", + "webgpu/shader/execution/fract.bin": "3e0d009a", + "webgpu/shader/execution/frexp.bin": "23465618", + "webgpu/shader/execution/inverseSqrt.bin": "487cf433", + "webgpu/shader/execution/ldexp.bin": "20d14a1", + "webgpu/shader/execution/length.bin": "95b9dd44", + "webgpu/shader/execution/log.bin": "13bdb23", + "webgpu/shader/execution/log2.bin": "3a5cbd55", + "webgpu/shader/execution/max.bin": "7e1f71fc", + "webgpu/shader/execution/min.bin": "6dc2bb2d", + "webgpu/shader/execution/mix.bin": "d019504f", + "webgpu/shader/execution/modf.bin": "8f3f56ec", + "webgpu/shader/execution/normalize.bin": "480fa5d8", + "webgpu/shader/execution/pack2x16float.bin": "1d32fd43", + "webgpu/shader/execution/pow.bin": "992dd6fd", + "webgpu/shader/execution/quantizeToF16.bin": "457045cb", + "webgpu/shader/execution/radians.bin": "4660460e", + "webgpu/shader/execution/reflect.bin": "d49b4a70", + "webgpu/shader/execution/refract.bin": "81d973ab", + "webgpu/shader/execution/round.bin": "a9617b62", + "webgpu/shader/execution/saturate.bin": "8616be85", + "webgpu/shader/execution/sign.bin": "4747c867", + "webgpu/shader/execution/sin.bin": "b60c310f", + "webgpu/shader/execution/sinh.bin": "8c24825e", + "webgpu/shader/execution/smoothstep.bin": "c5730ac6", + "webgpu/shader/execution/sqrt.bin": "44d4bf9b", + "webgpu/shader/execution/step.bin": "183935c1", + "webgpu/shader/execution/tan.bin": "5c8ab74a", + "webgpu/shader/execution/tanh.bin": "f233ceba", + "webgpu/shader/execution/transpose.bin": "86a6b61c", + "webgpu/shader/execution/trunc.bin": "7f07d161", + "webgpu/shader/execution/unpack2x16float.bin": "cfe367ce", + "webgpu/shader/execution/unpack2x16snorm.bin": "fcfe4174", + "webgpu/shader/execution/unpack2x16unorm.bin": "f419844a", + "webgpu/shader/execution/unpack4x8snorm.bin": "c3343320", + "webgpu/shader/execution/unpack4x8unorm.bin": "24618d08", + "webgpu/shader/execution/unary/af_arithmetic.bin": "ae3cf04d", + "webgpu/shader/execution/unary/af_assignment.bin": "823b0b98", + "webgpu/shader/execution/unary/bool_conversion.bin": "25551e87", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "b1604cf7", + "webgpu/shader/execution/unary/f16_conversion.bin": "6c9e1961", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "13fc8cf0", + "webgpu/shader/execution/unary/f32_conversion.bin": "3a4e8649", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "ca6bdc9e", + "webgpu/shader/execution/unary/i32_conversion.bin": "44881769", + "webgpu/shader/execution/unary/u32_conversion.bin": "535edb49", + "webgpu/shader/execution/unary/ai_assignment.bin": "607105c7", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "5718501b", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "9aff9753", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "c24c52ce", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "7df31c69", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "eb2e28a" } \ No newline at end of file diff --git a/src/webgpu/web_platform/worker/worker.spec.ts b/src/webgpu/web_platform/worker/worker.spec.ts index deed5d75e232..14fe135633c9 100644 --- a/src/webgpu/web_platform/worker/worker.spec.ts +++ b/src/webgpu/web_platform/worker/worker.spec.ts @@ -1,9 +1,10 @@ export const description = ` Tests WebGPU is available in a dedicated worker and a shared worker. -Note: The CTS test can be run respectively in a dedicated worker and a shared worker by -passing in worker=dedicated and worker=shared as a query parameter. These tests -are specifically to check that WebGPU is available in a dedicated worker and a shared worker. +Note: Any CTS test can be run in a worker by passing ?worker=dedicated, ?worker=shared, +?worker=service as a query parameter. The tests in this file are specifically to check +that WebGPU is available in each worker type. When run in combination with a ?worker flag, +they will test workers created from other workers (where APIs exist to do so). TODO[2]: Figure out how to make these tests run in service workers (not actually important unless service workers gain the ability to launch other workers). From 0771e27eff930099af8484f185825fb44cae2963 Mon Sep 17 00:00:00 2001 From: Greggman Date: Wed, 13 Mar 2024 09:50:41 +0900 Subject: [PATCH 30/33] Test `textureSample` WGSL validation (#3478) * Test `textureSample` WGSL validation --- src/resources/cache/hashes.json | 216 ++++++++--------- .../cache/webgpu/shader/execution/bitcast.bin | Bin 2221448 -> 2221448 bytes src/webgpu/listing_meta.json | 4 + .../call/builtin/textureSample.spec.ts | 218 ++++++++++++++++++ src/webgpu/util/conversion.ts | 50 ++++ 5 files changed, 380 insertions(+), 108 deletions(-) create mode 100644 src/webgpu/shader/validation/expression/call/builtin/textureSample.spec.ts diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 9692310ae56e..db69ef779b10 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "f93f6f2", - "webgpu/shader/execution/binary/af_logical.bin": "412fdd40", - "webgpu/shader/execution/binary/af_division.bin": "7b359c01", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "ad3e51de", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "10ae4166", - "webgpu/shader/execution/binary/af_multiplication.bin": "f3eb97ee", - "webgpu/shader/execution/binary/af_remainder.bin": "1c08510d", - "webgpu/shader/execution/binary/af_subtraction.bin": "1ea78ded", - "webgpu/shader/execution/binary/f16_addition.bin": "97c6c220", - "webgpu/shader/execution/binary/f16_logical.bin": "2d07c65d", - "webgpu/shader/execution/binary/f16_division.bin": "fd11268e", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "6e8f752c", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "ebfe95b8", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "21577c69", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "ca569737", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "8d7a02b5", - "webgpu/shader/execution/binary/f16_multiplication.bin": "aee2b921", - "webgpu/shader/execution/binary/f16_remainder.bin": "f9397246", - "webgpu/shader/execution/binary/f16_subtraction.bin": "8f80b4b4", - "webgpu/shader/execution/binary/f32_addition.bin": "3b09cfd7", - "webgpu/shader/execution/binary/f32_logical.bin": "ab23c69a", - "webgpu/shader/execution/binary/f32_division.bin": "2e8d775b", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "7276a303", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "26789d70", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "beb34505", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "1b426bf8", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "ee372ef3", - "webgpu/shader/execution/binary/f32_multiplication.bin": "a58fb275", - "webgpu/shader/execution/binary/f32_remainder.bin": "4fca1678", - "webgpu/shader/execution/binary/f32_subtraction.bin": "a77fcae2", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "483b584e", - "webgpu/shader/execution/binary/i32_comparison.bin": "a1310d6d", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "4ed255b", - "webgpu/shader/execution/binary/u32_comparison.bin": "2766735c", - "webgpu/shader/execution/abs.bin": "74007d00", - "webgpu/shader/execution/acos.bin": "71afa785", - "webgpu/shader/execution/acosh.bin": "7eb972b3", - "webgpu/shader/execution/asin.bin": "cf1f15dc", - "webgpu/shader/execution/asinh.bin": "77d8090c", - "webgpu/shader/execution/atan.bin": "f984effa", - "webgpu/shader/execution/atan2.bin": "f71eeef0", - "webgpu/shader/execution/atanh.bin": "f70ef8f3", - "webgpu/shader/execution/bitcast.bin": "61596841", - "webgpu/shader/execution/ceil.bin": "d3912020", - "webgpu/shader/execution/clamp.bin": "3af7b577", - "webgpu/shader/execution/cos.bin": "a1b9931a", - "webgpu/shader/execution/cosh.bin": "9d4816da", - "webgpu/shader/execution/cross.bin": "75ee9e89", - "webgpu/shader/execution/degrees.bin": "1c88f619", - "webgpu/shader/execution/determinant.bin": "16acfafa", - "webgpu/shader/execution/distance.bin": "1098d127", - "webgpu/shader/execution/dot.bin": "e1a8d3bc", - "webgpu/shader/execution/exp.bin": "cdb0e2b9", - "webgpu/shader/execution/exp2.bin": "665ec45a", - "webgpu/shader/execution/faceForward.bin": "f22e0c0a", - "webgpu/shader/execution/floor.bin": "ede6258", - "webgpu/shader/execution/fma.bin": "34c7474", - "webgpu/shader/execution/fract.bin": "3e0d009a", - "webgpu/shader/execution/frexp.bin": "23465618", - "webgpu/shader/execution/inverseSqrt.bin": "487cf433", - "webgpu/shader/execution/ldexp.bin": "20d14a1", - "webgpu/shader/execution/length.bin": "95b9dd44", - "webgpu/shader/execution/log.bin": "13bdb23", - "webgpu/shader/execution/log2.bin": "3a5cbd55", - "webgpu/shader/execution/max.bin": "7e1f71fc", - "webgpu/shader/execution/min.bin": "6dc2bb2d", - "webgpu/shader/execution/mix.bin": "d019504f", - "webgpu/shader/execution/modf.bin": "8f3f56ec", - "webgpu/shader/execution/normalize.bin": "480fa5d8", - "webgpu/shader/execution/pack2x16float.bin": "1d32fd43", - "webgpu/shader/execution/pow.bin": "992dd6fd", - "webgpu/shader/execution/quantizeToF16.bin": "457045cb", - "webgpu/shader/execution/radians.bin": "4660460e", - "webgpu/shader/execution/reflect.bin": "d49b4a70", - "webgpu/shader/execution/refract.bin": "81d973ab", - "webgpu/shader/execution/round.bin": "a9617b62", - "webgpu/shader/execution/saturate.bin": "8616be85", - "webgpu/shader/execution/sign.bin": "4747c867", - "webgpu/shader/execution/sin.bin": "b60c310f", - "webgpu/shader/execution/sinh.bin": "8c24825e", - "webgpu/shader/execution/smoothstep.bin": "c5730ac6", - "webgpu/shader/execution/sqrt.bin": "44d4bf9b", - "webgpu/shader/execution/step.bin": "183935c1", - "webgpu/shader/execution/tan.bin": "5c8ab74a", - "webgpu/shader/execution/tanh.bin": "f233ceba", - "webgpu/shader/execution/transpose.bin": "86a6b61c", - "webgpu/shader/execution/trunc.bin": "7f07d161", - "webgpu/shader/execution/unpack2x16float.bin": "cfe367ce", - "webgpu/shader/execution/unpack2x16snorm.bin": "fcfe4174", - "webgpu/shader/execution/unpack2x16unorm.bin": "f419844a", - "webgpu/shader/execution/unpack4x8snorm.bin": "c3343320", - "webgpu/shader/execution/unpack4x8unorm.bin": "24618d08", - "webgpu/shader/execution/unary/af_arithmetic.bin": "ae3cf04d", - "webgpu/shader/execution/unary/af_assignment.bin": "823b0b98", - "webgpu/shader/execution/unary/bool_conversion.bin": "25551e87", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "b1604cf7", - "webgpu/shader/execution/unary/f16_conversion.bin": "6c9e1961", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "13fc8cf0", - "webgpu/shader/execution/unary/f32_conversion.bin": "3a4e8649", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "ca6bdc9e", - "webgpu/shader/execution/unary/i32_conversion.bin": "44881769", - "webgpu/shader/execution/unary/u32_conversion.bin": "535edb49", - "webgpu/shader/execution/unary/ai_assignment.bin": "607105c7", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "5718501b", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "9aff9753", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "c24c52ce", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "7df31c69", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "eb2e28a" + "webgpu/shader/execution/binary/af_addition.bin": "91af48f0", + "webgpu/shader/execution/binary/af_logical.bin": "ffb5a83f", + "webgpu/shader/execution/binary/af_division.bin": "73039eea", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "e13e9e6e", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "5dc56318", + "webgpu/shader/execution/binary/af_multiplication.bin": "33cb4a90", + "webgpu/shader/execution/binary/af_remainder.bin": "b06c2cd5", + "webgpu/shader/execution/binary/af_subtraction.bin": "d5c5a9de", + "webgpu/shader/execution/binary/f16_addition.bin": "41c253b", + "webgpu/shader/execution/binary/f16_logical.bin": "65cdc6f", + "webgpu/shader/execution/binary/f16_division.bin": "4e0dd3f3", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "7131d2e", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "27105bab", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "e4edd8d1", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "3f8f0d6", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "4379fdb1", + "webgpu/shader/execution/binary/f16_multiplication.bin": "f1ce631d", + "webgpu/shader/execution/binary/f16_remainder.bin": "c3400825", + "webgpu/shader/execution/binary/f16_subtraction.bin": "c84c09b4", + "webgpu/shader/execution/binary/f32_addition.bin": "d1ce98cf", + "webgpu/shader/execution/binary/f32_logical.bin": "d40c1c0", + "webgpu/shader/execution/binary/f32_division.bin": "27a33c63", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "84296a9c", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "a601448a", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "f025741a", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "db1d203c", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "a7456950", + "webgpu/shader/execution/binary/f32_multiplication.bin": "e6a47586", + "webgpu/shader/execution/binary/f32_remainder.bin": "31d588d", + "webgpu/shader/execution/binary/f32_subtraction.bin": "4b79931f", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "90a3514a", + "webgpu/shader/execution/binary/i32_comparison.bin": "840b09e6", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "e3f04e97", + "webgpu/shader/execution/binary/u32_comparison.bin": "dc1398d3", + "webgpu/shader/execution/abs.bin": "1ff1f454", + "webgpu/shader/execution/acos.bin": "539c5984", + "webgpu/shader/execution/acosh.bin": "e1155c5f", + "webgpu/shader/execution/asin.bin": "a17f7afb", + "webgpu/shader/execution/asinh.bin": "dbb0f8a", + "webgpu/shader/execution/atan.bin": "80b2e232", + "webgpu/shader/execution/atan2.bin": "19853e04", + "webgpu/shader/execution/atanh.bin": "1dd56fe0", + "webgpu/shader/execution/bitcast.bin": "83ba0a2d", + "webgpu/shader/execution/ceil.bin": "51abcdee", + "webgpu/shader/execution/clamp.bin": "8b77f1be", + "webgpu/shader/execution/cos.bin": "486fa019", + "webgpu/shader/execution/cosh.bin": "b796f446", + "webgpu/shader/execution/cross.bin": "934a9528", + "webgpu/shader/execution/degrees.bin": "e767ec2b", + "webgpu/shader/execution/determinant.bin": "a82df7ed", + "webgpu/shader/execution/distance.bin": "189f7e7", + "webgpu/shader/execution/dot.bin": "48b08425", + "webgpu/shader/execution/exp.bin": "7a9b65c9", + "webgpu/shader/execution/exp2.bin": "40857179", + "webgpu/shader/execution/faceForward.bin": "a4d3d7fd", + "webgpu/shader/execution/floor.bin": "78ddd3e0", + "webgpu/shader/execution/fma.bin": "8fe936c7", + "webgpu/shader/execution/fract.bin": "c9527ef2", + "webgpu/shader/execution/frexp.bin": "66d03941", + "webgpu/shader/execution/inverseSqrt.bin": "b6242d36", + "webgpu/shader/execution/ldexp.bin": "a0827b94", + "webgpu/shader/execution/length.bin": "23579568", + "webgpu/shader/execution/log.bin": "43822075", + "webgpu/shader/execution/log2.bin": "18e01bd3", + "webgpu/shader/execution/max.bin": "3754c33c", + "webgpu/shader/execution/min.bin": "33935035", + "webgpu/shader/execution/mix.bin": "f67ad46a", + "webgpu/shader/execution/modf.bin": "edb80b8c", + "webgpu/shader/execution/normalize.bin": "167d7c79", + "webgpu/shader/execution/pack2x16float.bin": "d7ef3cf5", + "webgpu/shader/execution/pow.bin": "b3f33461", + "webgpu/shader/execution/quantizeToF16.bin": "c41ec650", + "webgpu/shader/execution/radians.bin": "74748445", + "webgpu/shader/execution/reflect.bin": "e2b029bd", + "webgpu/shader/execution/refract.bin": "7916ffc2", + "webgpu/shader/execution/round.bin": "8d428840", + "webgpu/shader/execution/saturate.bin": "6de2e522", + "webgpu/shader/execution/sign.bin": "7ae6222a", + "webgpu/shader/execution/sin.bin": "4178e4ca", + "webgpu/shader/execution/sinh.bin": "7c104d99", + "webgpu/shader/execution/smoothstep.bin": "c461cdc6", + "webgpu/shader/execution/sqrt.bin": "bf85c8fb", + "webgpu/shader/execution/step.bin": "adb873a1", + "webgpu/shader/execution/tan.bin": "a1777146", + "webgpu/shader/execution/tanh.bin": "ceeebb22", + "webgpu/shader/execution/transpose.bin": "f41b25c8", + "webgpu/shader/execution/trunc.bin": "c71488f3", + "webgpu/shader/execution/unpack2x16float.bin": "a763e4a1", + "webgpu/shader/execution/unpack2x16snorm.bin": "e414f14b", + "webgpu/shader/execution/unpack2x16unorm.bin": "488b474a", + "webgpu/shader/execution/unpack4x8snorm.bin": "d4fd9078", + "webgpu/shader/execution/unpack4x8unorm.bin": "7aa3c88e", + "webgpu/shader/execution/unary/af_arithmetic.bin": "14207fe0", + "webgpu/shader/execution/unary/af_assignment.bin": "ddb98d17", + "webgpu/shader/execution/unary/bool_conversion.bin": "f37ea003", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "26b20f68", + "webgpu/shader/execution/unary/f16_conversion.bin": "5e3a4bce", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "c30910", + "webgpu/shader/execution/unary/f32_conversion.bin": "7e716fd3", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "25cf27d1", + "webgpu/shader/execution/unary/i32_conversion.bin": "276dcf69", + "webgpu/shader/execution/unary/u32_conversion.bin": "acac2172", + "webgpu/shader/execution/unary/ai_assignment.bin": "c876d431", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "36a1d65b", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "89e34dcf", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "4116ac44", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "b2738fb2", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "4d3df8a8" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/bitcast.bin b/src/resources/cache/webgpu/shader/execution/bitcast.bin index d3954903ac89afcfbe5049a356005ed02cf10aae..3c24c88e1ffbc42f87639912bacab411154e6d63 100644 GIT binary patch delta 151 zcmWN=M-6~L006yPYE?jRVw)gr0)tx&a delta 152 zcmWN=%MpSw6hP4l3d%o-f*(*Rf!897WKSO%h7G$EpiObl%&~u?EEgxU8};PHcu@>^ r;E^FCo_J==gef!TELc*r;)OM@ys=@+J0E=V#g0AS{P4SZ8Gh>y9V9$( diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 2db6cc8785dd..f5e899581b47 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1928,6 +1928,10 @@ "webgpu:shader,validation,expression,call,builtin,sqrt:values:*": { "subcaseMS": 0.302 }, "webgpu:shader,validation,expression,call,builtin,tan:integer_argument:*": { "subcaseMS": 1.734 }, "webgpu:shader,validation,expression,call,builtin,tan:values:*": { "subcaseMS": 0.350 }, + "webgpu:shader,validation,expression,call,builtin,textureSample:array_index_argument:*": { "subcaseMS": 1.888 }, + "webgpu:shader,validation,expression,call,builtin,textureSample:coords_argument:*": { "subcaseMS": 1.342 }, + "webgpu:shader,validation,expression,call,builtin,textureSample:offset_argument,non_const:*": { "subcaseMS": 1.604 }, + "webgpu:shader,validation,expression,call,builtin,textureSample:offset_argument:*": { "subcaseMS": 1.401 }, "webgpu:shader,validation,expression,call,builtin,unpack4xI8:bad_args:*": { "subcaseMS": 121.263 }, "webgpu:shader,validation,expression,call,builtin,unpack4xI8:must_use:*": { "subcaseMS": 35.200 }, "webgpu:shader,validation,expression,call,builtin,unpack4xI8:supported:*": { "subcaseMS": 24.150 }, diff --git a/src/webgpu/shader/validation/expression/call/builtin/textureSample.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/textureSample.spec.ts new file mode 100644 index 000000000000..0d63c059d719 --- /dev/null +++ b/src/webgpu/shader/validation/expression/call/builtin/textureSample.spec.ts @@ -0,0 +1,218 @@ +const builtin = 'textureSample'; +export const description = ` +Validation tests for the ${builtin}() builtin. + +* test textureSample coords parameter must be correct type +* test textureSample array_index parameter must be correct type +* test textureSample coords parameter must be correct type +* test textureSample offset parameter must be correct type +* test textureSample offset parameter must be a const-expression +* test textureSample offset parameter must be between -8 and +7 inclusive +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; +import { + Type, + kAllScalarsAndVectors, + isConvertible, + ScalarType, + VectorType, +} from '../../../../../util/conversion.js'; +import { ShaderValidationTest } from '../../../shader_validation_test.js'; + +type TextureSampleArguments = { + coordsArgType: ScalarType | VectorType; + hasArrayIndexArg?: boolean; + offsetArgType?: VectorType; +}; + +const kValidTextureSampleParameterTypes: { [n: string]: TextureSampleArguments } = { + 'texture_1d': { coordsArgType: Type.f32 }, + 'texture_2d': { coordsArgType: Type.vec2f, offsetArgType: Type.vec2i }, + 'texture_2d_array': { + coordsArgType: Type.vec2f, + hasArrayIndexArg: true, + offsetArgType: Type.vec2i, + }, + 'texture_3d': { coordsArgType: Type.vec3f, offsetArgType: Type.vec3i }, + 'texture_cube': { coordsArgType: Type.vec3f }, + 'texture_cube_array': { coordsArgType: Type.vec3f, hasArrayIndexArg: true }, + texture_depth_2d: { coordsArgType: Type.vec2f, offsetArgType: Type.vec2i }, + texture_depth_2d_array: { coordsArgType: Type.vec2f, hasArrayIndexArg: true }, + texture_depth_cube: { coordsArgType: Type.vec3f }, + texture_depth_cube_array: { coordsArgType: Type.vec3f, hasArrayIndexArg: true }, +} as const; + +const kTextureTypes = keysOf(kValidTextureSampleParameterTypes); +const kValuesTypes = objectsToRecord(kAllScalarsAndVectors); + +export const g = makeTestGroup(ShaderValidationTest); + +g.test('coords_argument') + .desc( + ` +Validates that only incorrect coords arguments are rejected by ${builtin} +` + ) + .params(u => + u + .combine('textureType', keysOf(kValidTextureSampleParameterTypes)) + .combine('coordType', keysOf(kValuesTypes)) + .beginSubcases() + .combine('value', [-1, 0, 1] as const) + .expand('offset', ({ textureType }) => { + const offset = kValidTextureSampleParameterTypes[textureType].offsetArgType; + return offset ? [false, true] : [false]; + }) + ) + .fn(t => { + const { textureType, coordType, offset, value } = t.params; + const coordArgType = kValuesTypes[coordType]; + const { + offsetArgType, + coordsArgType: coordsRequiredType, + hasArrayIndexArg, + } = kValidTextureSampleParameterTypes[textureType]; + + const coordWGSL = coordArgType.create(value).wgsl(); + const arrayWGSL = hasArrayIndexArg ? ', 0' : ''; + const offsetWGSL = offset ? `, ${offsetArgType?.create(0).wgsl()}` : ''; + + const code = ` +@group(0) @binding(0) var s: sampler; +@group(0) @binding(1) var t: ${textureType}; +@fragment fn fs() -> @location(0) vec4f { + let v = textureSample(t, s, ${coordWGSL}${arrayWGSL}${offsetWGSL}); + return vec4f(0); +} +`; + const expectSuccess = isConvertible(coordArgType, coordsRequiredType); + t.expectCompileResult(expectSuccess, code); + }); + +g.test('array_index_argument') + .desc( + ` +Validates that only incorrect array_index arguments are rejected by ${builtin} +` + ) + .params(u => + u + .combine('textureType', kTextureTypes) + // filter out types with no array_index + .filter( + ({ textureType }) => !!kValidTextureSampleParameterTypes[textureType].hasArrayIndexArg + ) + .combine('arrayIndexType', keysOf(kValuesTypes)) + .beginSubcases() + .combine('value', [-9, -8, 0, 7, 8]) + ) + .fn(t => { + const { textureType, arrayIndexType, value } = t.params; + const arrayIndexArgType = kValuesTypes[arrayIndexType]; + const args = [arrayIndexArgType.create(value)]; + const { coordsArgType, offsetArgType } = kValidTextureSampleParameterTypes[textureType]; + + const coordWGSL = coordsArgType.create(0).wgsl(); + const arrayWGSL = args.map(arg => arg.wgsl()).join(', '); + const offsetWGSL = offsetArgType ? `, ${offsetArgType.create(0).wgsl()}` : ''; + + const code = ` +@group(0) @binding(0) var s: sampler; +@group(0) @binding(1) var t: ${textureType}; +@fragment fn fs() -> @location(0) vec4f { + let v = textureSample(t, s, ${coordWGSL}, ${arrayWGSL}${offsetWGSL}); + return vec4f(0); +} +`; + const expectSuccess = + isConvertible(arrayIndexArgType, Type.i32) || isConvertible(arrayIndexArgType, Type.u32); + t.expectCompileResult(expectSuccess, code); + }); + +g.test('offset_argument') + .desc( + ` +Validates that only incorrect offset arguments are rejected by ${builtin} +` + ) + .params(u => + u + .combine('textureType', kTextureTypes) + // filter out types with no offset + .filter( + ({ textureType }) => + kValidTextureSampleParameterTypes[textureType].offsetArgType !== undefined + ) + .combine('offsetType', keysOf(kValuesTypes)) + .beginSubcases() + .combine('value', [-9, -8, 0, 7, 8]) + ) + .fn(t => { + const { textureType, offsetType, value } = t.params; + const offsetArgType = kValuesTypes[offsetType]; + const args = [offsetArgType.create(value)]; + const { + coordsArgType, + hasArrayIndexArg, + offsetArgType: offsetRequiredType, + } = kValidTextureSampleParameterTypes[textureType]; + + const coordWGSL = coordsArgType.create(0).wgsl(); + const arrayWGSL = hasArrayIndexArg ? ', 0' : ''; + const offsetWGSL = args.map(arg => arg.wgsl()).join(', '); + + const code = ` +@group(0) @binding(0) var s: sampler; +@group(0) @binding(1) var t: ${textureType}; +@fragment fn fs() -> @location(0) vec4f { + let v = textureSample(t, s, ${coordWGSL}${arrayWGSL}, ${offsetWGSL}); + return vec4f(0); +} +`; + const expectSuccess = + isConvertible(offsetArgType, offsetRequiredType!) && value >= -8 && value <= 7; + t.expectCompileResult(expectSuccess, code); + }); + +g.test('offset_argument,non_const') + .desc( + ` +Validates that only non-const offset arguments are rejected by ${builtin} +` + ) + .params(u => + u + .combine('textureType', kTextureTypes) + .combine('varType', ['c', 'u', 'l']) + // filter out types with no offset + .filter( + ({ textureType }) => + kValidTextureSampleParameterTypes[textureType].offsetArgType !== undefined + ) + ) + .fn(t => { + const { textureType, varType } = t.params; + const { coordsArgType, hasArrayIndexArg, offsetArgType } = + kValidTextureSampleParameterTypes[textureType]; + + const coordWGSL = coordsArgType.create(0).wgsl(); + const arrayWGSL = hasArrayIndexArg ? ', 0' : ''; + const offsetWGSL = `${offsetArgType}(${varType})`; + const castWGSL = offsetArgType!.elementType.toString(); + + const code = ` +@group(0) @binding(0) var s: sampler; +@group(0) @binding(1) var t: ${textureType}; +@group(0) @binding(2) var u: ${offsetArgType}; +@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f { + const c = 1; + let l = ${offsetArgType}(${castWGSL}(p.x)); + let v = textureSample(t, s, ${coordWGSL}${arrayWGSL}, ${offsetWGSL}); + return vec4f(0); +} +`; + const expectSuccess = varType === 'c'; + t.expectCompileResult(expectSuccess, code); + }); diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 15557b3d4c57..4925899811b5 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -2234,6 +2234,56 @@ export function isFloatType(ty: Type): boolean { return false; } +/** @returns true if an argument of type 'src' can be used for a parameter of type 'dst' */ +export function isConvertible(src: Type, dst: Type) { + if (src === dst) { + return true; + } + + const widthOf = (ty: Type) => { + return ty instanceof VectorType ? ty.width : 1; + }; + + if (widthOf(src) !== widthOf(dst)) { + return false; + } + + const elSrc = scalarTypeOf(src); + const elDst = scalarTypeOf(dst); + + switch (elSrc.kind) { + case 'abstract-float': + switch (elDst.kind) { + case 'abstract-float': + case 'f16': + case 'f32': + case 'f64': + return true; + default: + return false; + } + case 'abstract-int': + switch (elDst.kind) { + case 'abstract-int': + case 'abstract-float': + case 'f16': + case 'f32': + case 'f64': + case 'u16': + case 'u32': + case 'u8': + case 'i16': + case 'i32': + case 'i8': + return true; + default: + return false; + } + default: + return false; + } +} + /// All floating-point scalar types const kFloatScalars = [Type.abstractFloat, Type.f32, Type.f16] as const; From 392da47f9ba72b78189ed0035d1aabf15dbc7187 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 13 Mar 2024 11:33:37 +0000 Subject: [PATCH 31/33] shader/execution: Fix struct padding for `f16`s `wgslMembers()` was using an `i32` to pad the structure to its alignment, however `i32` has an alignment and size of 4 bytes. If the structure holds an odd number of `f16` scalars, then `i32` cannot be used to account fo this padding due to the 4-byte size and alignment. Pad with `f16` if the padding is an odd multiple of 2 bytes. --- src/webgpu/shader/execution/expression/expression.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/webgpu/shader/execution/expression/expression.ts b/src/webgpu/shader/execution/expression/expression.ts index 0359e6e901a1..e86d068c6daa 100644 --- a/src/webgpu/shader/execution/expression/expression.ts +++ b/src/webgpu/shader/execution/expression/expression.ts @@ -158,7 +158,10 @@ function wgslMembers(members: Type[], source: InputSource, memberName: (i: numbe }); const padding = layout.stride - layout.size; if (padding > 0) { - lines.push(` @size(${padding}) padding : i32,`); + // Pad with a 'f16' if the padding requires an odd multiple of 2 bytes. + // This is required as 'i32' has an alignment and size of 4 bytes. + const ty = (padding & 2) !== 0 ? 'f16' : 'i32'; + lines.push(` @size(${padding}) padding : ${ty},`); } return lines.join('\n'); } From 022bbf5bb747543dbb9aae5cfdf9e05cda950fdb Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Wed, 13 Mar 2024 12:25:49 -0400 Subject: [PATCH 32/33] Remove ULP & Absolute Error support for AF (#3487) For AbstractFloat, builtins that have absolute or ULP errors, (or inherit their error) should defer to the intervals for f32, so replacing the underlying intervals with stubs that assert instead. --- src/resources/cache/hashes.json | 186 +++++++++--------- .../cache/webgpu/shader/execution/bitcast.bin | Bin 2221448 -> 2221448 bytes src/unittests/floating_point.spec.ts | 42 ++-- src/webgpu/util/floating_point.ts | 18 +- 4 files changed, 117 insertions(+), 129 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index db69ef779b10..6a616d571a9e 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "91af48f0", + "webgpu/shader/execution/binary/af_addition.bin": "24160909", "webgpu/shader/execution/binary/af_logical.bin": "ffb5a83f", - "webgpu/shader/execution/binary/af_division.bin": "73039eea", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "e13e9e6e", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "5dc56318", - "webgpu/shader/execution/binary/af_multiplication.bin": "33cb4a90", - "webgpu/shader/execution/binary/af_remainder.bin": "b06c2cd5", - "webgpu/shader/execution/binary/af_subtraction.bin": "d5c5a9de", - "webgpu/shader/execution/binary/f16_addition.bin": "41c253b", + "webgpu/shader/execution/binary/af_division.bin": "c230ac78", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "9079a042", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "6b55102f", + "webgpu/shader/execution/binary/af_multiplication.bin": "4fc3b0d6", + "webgpu/shader/execution/binary/af_remainder.bin": "366caec6", + "webgpu/shader/execution/binary/af_subtraction.bin": "49a16db4", + "webgpu/shader/execution/binary/f16_addition.bin": "3fb1ee09", "webgpu/shader/execution/binary/f16_logical.bin": "65cdc6f", - "webgpu/shader/execution/binary/f16_division.bin": "4e0dd3f3", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "7131d2e", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "27105bab", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "e4edd8d1", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "3f8f0d6", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "4379fdb1", - "webgpu/shader/execution/binary/f16_multiplication.bin": "f1ce631d", - "webgpu/shader/execution/binary/f16_remainder.bin": "c3400825", - "webgpu/shader/execution/binary/f16_subtraction.bin": "c84c09b4", - "webgpu/shader/execution/binary/f32_addition.bin": "d1ce98cf", + "webgpu/shader/execution/binary/f16_division.bin": "1b5bd44a", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "fdea291a", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "e727482e", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "24d70bdd", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "7acd3c3b", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "9e01e0cf", + "webgpu/shader/execution/binary/f16_multiplication.bin": "c24b705c", + "webgpu/shader/execution/binary/f16_remainder.bin": "2764c7e1", + "webgpu/shader/execution/binary/f16_subtraction.bin": "d4014a38", + "webgpu/shader/execution/binary/f32_addition.bin": "b6707259", "webgpu/shader/execution/binary/f32_logical.bin": "d40c1c0", - "webgpu/shader/execution/binary/f32_division.bin": "27a33c63", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "84296a9c", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "a601448a", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "f025741a", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "db1d203c", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "a7456950", - "webgpu/shader/execution/binary/f32_multiplication.bin": "e6a47586", - "webgpu/shader/execution/binary/f32_remainder.bin": "31d588d", - "webgpu/shader/execution/binary/f32_subtraction.bin": "4b79931f", + "webgpu/shader/execution/binary/f32_division.bin": "e19e30a6", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "6a13d6d6", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "4c5cb0a2", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "6b00d17f", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "319d3ae1", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "1c4893ca", + "webgpu/shader/execution/binary/f32_multiplication.bin": "f7dbbc8", + "webgpu/shader/execution/binary/f32_remainder.bin": "fb5dd3fe", + "webgpu/shader/execution/binary/f32_subtraction.bin": "aeaca568", "webgpu/shader/execution/binary/i32_arithmetic.bin": "90a3514a", "webgpu/shader/execution/binary/i32_comparison.bin": "840b09e6", "webgpu/shader/execution/binary/u32_arithmetic.bin": "e3f04e97", "webgpu/shader/execution/binary/u32_comparison.bin": "dc1398d3", - "webgpu/shader/execution/abs.bin": "1ff1f454", - "webgpu/shader/execution/acos.bin": "539c5984", - "webgpu/shader/execution/acosh.bin": "e1155c5f", - "webgpu/shader/execution/asin.bin": "a17f7afb", - "webgpu/shader/execution/asinh.bin": "dbb0f8a", - "webgpu/shader/execution/atan.bin": "80b2e232", - "webgpu/shader/execution/atan2.bin": "19853e04", - "webgpu/shader/execution/atanh.bin": "1dd56fe0", - "webgpu/shader/execution/bitcast.bin": "83ba0a2d", - "webgpu/shader/execution/ceil.bin": "51abcdee", - "webgpu/shader/execution/clamp.bin": "8b77f1be", - "webgpu/shader/execution/cos.bin": "486fa019", - "webgpu/shader/execution/cosh.bin": "b796f446", - "webgpu/shader/execution/cross.bin": "934a9528", - "webgpu/shader/execution/degrees.bin": "e767ec2b", - "webgpu/shader/execution/determinant.bin": "a82df7ed", - "webgpu/shader/execution/distance.bin": "189f7e7", - "webgpu/shader/execution/dot.bin": "48b08425", - "webgpu/shader/execution/exp.bin": "7a9b65c9", - "webgpu/shader/execution/exp2.bin": "40857179", - "webgpu/shader/execution/faceForward.bin": "a4d3d7fd", - "webgpu/shader/execution/floor.bin": "78ddd3e0", - "webgpu/shader/execution/fma.bin": "8fe936c7", - "webgpu/shader/execution/fract.bin": "c9527ef2", - "webgpu/shader/execution/frexp.bin": "66d03941", - "webgpu/shader/execution/inverseSqrt.bin": "b6242d36", - "webgpu/shader/execution/ldexp.bin": "a0827b94", - "webgpu/shader/execution/length.bin": "23579568", - "webgpu/shader/execution/log.bin": "43822075", - "webgpu/shader/execution/log2.bin": "18e01bd3", - "webgpu/shader/execution/max.bin": "3754c33c", - "webgpu/shader/execution/min.bin": "33935035", - "webgpu/shader/execution/mix.bin": "f67ad46a", - "webgpu/shader/execution/modf.bin": "edb80b8c", - "webgpu/shader/execution/normalize.bin": "167d7c79", + "webgpu/shader/execution/abs.bin": "2b663377", + "webgpu/shader/execution/acos.bin": "a888c67f", + "webgpu/shader/execution/acosh.bin": "5383658b", + "webgpu/shader/execution/asin.bin": "a580a322", + "webgpu/shader/execution/asinh.bin": "31388535", + "webgpu/shader/execution/atan.bin": "c6f4c771", + "webgpu/shader/execution/atan2.bin": "3b9a37a1", + "webgpu/shader/execution/atanh.bin": "7295a313", + "webgpu/shader/execution/bitcast.bin": "73e0bea6", + "webgpu/shader/execution/ceil.bin": "24bd6be9", + "webgpu/shader/execution/clamp.bin": "8ed55492", + "webgpu/shader/execution/cos.bin": "4d36fa0", + "webgpu/shader/execution/cosh.bin": "a7da3a3", + "webgpu/shader/execution/cross.bin": "6ce2660b", + "webgpu/shader/execution/degrees.bin": "53a0849", + "webgpu/shader/execution/determinant.bin": "873e78af", + "webgpu/shader/execution/distance.bin": "bfce09a0", + "webgpu/shader/execution/dot.bin": "566b6e55", + "webgpu/shader/execution/exp.bin": "d8142d49", + "webgpu/shader/execution/exp2.bin": "52a403d1", + "webgpu/shader/execution/faceForward.bin": "f90dacbe", + "webgpu/shader/execution/floor.bin": "c7465b11", + "webgpu/shader/execution/fma.bin": "db3497d4", + "webgpu/shader/execution/fract.bin": "e65c5bd1", + "webgpu/shader/execution/frexp.bin": "5ce112f4", + "webgpu/shader/execution/inverseSqrt.bin": "84ea0a57", + "webgpu/shader/execution/ldexp.bin": "9d11f120", + "webgpu/shader/execution/length.bin": "dd759736", + "webgpu/shader/execution/log.bin": "5f77a59d", + "webgpu/shader/execution/log2.bin": "57fff45e", + "webgpu/shader/execution/max.bin": "c7cdd54f", + "webgpu/shader/execution/min.bin": "422be325", + "webgpu/shader/execution/mix.bin": "957e7d92", + "webgpu/shader/execution/modf.bin": "946d1f4f", + "webgpu/shader/execution/normalize.bin": "2ac02e80", "webgpu/shader/execution/pack2x16float.bin": "d7ef3cf5", - "webgpu/shader/execution/pow.bin": "b3f33461", - "webgpu/shader/execution/quantizeToF16.bin": "c41ec650", - "webgpu/shader/execution/radians.bin": "74748445", - "webgpu/shader/execution/reflect.bin": "e2b029bd", - "webgpu/shader/execution/refract.bin": "7916ffc2", - "webgpu/shader/execution/round.bin": "8d428840", - "webgpu/shader/execution/saturate.bin": "6de2e522", - "webgpu/shader/execution/sign.bin": "7ae6222a", - "webgpu/shader/execution/sin.bin": "4178e4ca", - "webgpu/shader/execution/sinh.bin": "7c104d99", - "webgpu/shader/execution/smoothstep.bin": "c461cdc6", - "webgpu/shader/execution/sqrt.bin": "bf85c8fb", - "webgpu/shader/execution/step.bin": "adb873a1", - "webgpu/shader/execution/tan.bin": "a1777146", - "webgpu/shader/execution/tanh.bin": "ceeebb22", - "webgpu/shader/execution/transpose.bin": "f41b25c8", - "webgpu/shader/execution/trunc.bin": "c71488f3", - "webgpu/shader/execution/unpack2x16float.bin": "a763e4a1", - "webgpu/shader/execution/unpack2x16snorm.bin": "e414f14b", - "webgpu/shader/execution/unpack2x16unorm.bin": "488b474a", - "webgpu/shader/execution/unpack4x8snorm.bin": "d4fd9078", - "webgpu/shader/execution/unpack4x8unorm.bin": "7aa3c88e", - "webgpu/shader/execution/unary/af_arithmetic.bin": "14207fe0", - "webgpu/shader/execution/unary/af_assignment.bin": "ddb98d17", + "webgpu/shader/execution/pow.bin": "cd492166", + "webgpu/shader/execution/quantizeToF16.bin": "58bac06c", + "webgpu/shader/execution/radians.bin": "6ac08f50", + "webgpu/shader/execution/reflect.bin": "3c260554", + "webgpu/shader/execution/refract.bin": "7e952d7c", + "webgpu/shader/execution/round.bin": "f6b9bda1", + "webgpu/shader/execution/saturate.bin": "1c22f301", + "webgpu/shader/execution/sign.bin": "3ce55105", + "webgpu/shader/execution/sin.bin": "d48e7f2a", + "webgpu/shader/execution/sinh.bin": "f227c1d1", + "webgpu/shader/execution/smoothstep.bin": "6bdb4309", + "webgpu/shader/execution/sqrt.bin": "cd576d27", + "webgpu/shader/execution/step.bin": "dd584686", + "webgpu/shader/execution/tan.bin": "5ae50f61", + "webgpu/shader/execution/tanh.bin": "fe7a619d", + "webgpu/shader/execution/transpose.bin": "469edd7e", + "webgpu/shader/execution/trunc.bin": "8d3a05de", + "webgpu/shader/execution/unpack2x16float.bin": "e897c5ac", + "webgpu/shader/execution/unpack2x16snorm.bin": "450d5402", + "webgpu/shader/execution/unpack2x16unorm.bin": "306b3bf9", + "webgpu/shader/execution/unpack4x8snorm.bin": "fc1bd4c3", + "webgpu/shader/execution/unpack4x8unorm.bin": "763288cc", + "webgpu/shader/execution/unary/af_arithmetic.bin": "a39d4121", + "webgpu/shader/execution/unary/af_assignment.bin": "6ed540e8", "webgpu/shader/execution/unary/bool_conversion.bin": "f37ea003", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "26b20f68", - "webgpu/shader/execution/unary/f16_conversion.bin": "5e3a4bce", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "c30910", - "webgpu/shader/execution/unary/f32_conversion.bin": "7e716fd3", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "9cadc739", + "webgpu/shader/execution/unary/f16_conversion.bin": "49c7b38e", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "28da577a", + "webgpu/shader/execution/unary/f32_conversion.bin": "fcdfdd08", "webgpu/shader/execution/unary/i32_arithmetic.bin": "25cf27d1", "webgpu/shader/execution/unary/i32_conversion.bin": "276dcf69", "webgpu/shader/execution/unary/u32_conversion.bin": "acac2172", "webgpu/shader/execution/unary/ai_assignment.bin": "c876d431", "webgpu/shader/execution/binary/ai_arithmetic.bin": "36a1d65b", "webgpu/shader/execution/unary/ai_arithmetic.bin": "89e34dcf", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "4116ac44", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "b2738fb2", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "4d3df8a8" + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "3fd8797b", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "bf99158a", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "8bcc7c30" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/bitcast.bin b/src/resources/cache/webgpu/shader/execution/bitcast.bin index 3c24c88e1ffbc42f87639912bacab411154e6d63..d3954903ac89afcfbe5049a356005ed02cf10aae 100644 GIT binary patch delta 152 zcmWN=%MpSw6hP4l3d%o-f*(*Rf!897WKSO%h7G$EpiObl%&~u?EEgxU8};PHcu@>^ r;E^FCo_J==gef!TELc*r;)OM@ys=@+J0E=V#g0AS{P4SZ8Gh>y9V9$( delta 151 zcmWN=M-6~L006yPYE?jRVw)gr0)tx&a diff --git a/src/unittests/floating_point.spec.ts b/src/unittests/floating_point.spec.ts index 472207d185a1..0c5c32ecd2e3 100644 --- a/src/unittests/floating_point.spec.ts +++ b/src/unittests/floating_point.spec.ts @@ -1836,27 +1836,24 @@ interface AbsoluteErrorCase { const kSmallAbsoluteErrorValue = { f32: 2 ** -11, // Builtin cos and sin has a absolute error 2**-11 for f32 f16: 2 ** -7, // Builtin cos and sin has a absolute error 2**-7 for f16 - abstract: 2 ** -11, // Builtin cos and sin has a absolute error 2**-11 for AbstractFloat } as const; // A large absolute error value is a representable value x that much smaller than maximum // positive, but positive.max - x is still exactly representable. const kLargeAbsoluteErrorValue = { f32: 2 ** 110, // f32.positive.max - 2**110 = 3.4028104e+38 = 0x7f7fffbf in f32 f16: 2 ** 10, // f16.positive.max - 2**10 = 64480 = 0x7bdf in f16 - abstract: 2 ** 977, // f64.positive.man - 2**977 = 1.79769e+308 = 0x7fefffffffffffbf in f64 } as const; // A subnormal absolute error value is a subnormal representable value x of kind, which ensures // that positive.subnormal.min +/- x is still exactly representable. const kSubnormalAbsoluteErrorValue = { f32: 2 ** -140, // f32 0x00000200 f16: 2 ** -20, // f16 0x0010 - abstract: 2 ** -1065, // f64 0x0000_0000_0000_0200 } as const; g.test('absoluteErrorInterval') .params(u => u - .combine('trait', ['f32', 'f16', 'abstract'] as const) + .combine('trait', ['f32', 'f16'] as const) .beginSubcases() .expandWithParams(p => { const trait = FP[p.trait]; @@ -1865,26 +1862,6 @@ g.test('absoluteErrorInterval') const largeErr = kLargeAbsoluteErrorValue[p.trait]; const subnormalErr = kSubnormalAbsoluteErrorValue[p.trait]; - // Additional testing for non-f64 values, since JS number is f64 internally - // prettier-ignore - const additionalSubnormal64bit = p.trait !== 'abstract' ? - [ - // 64-bit subnormals, expected to be treated as 0.0 or smallest subnormal of kind. - { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: 0, expected: [0, constants.positive.subnormal.min] }, - { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: subnormalErr, expected: [-subnormalErr, constants.positive.subnormal.min + subnormalErr] }, - // Note that f32 minimum subnormal is so smaller than 1.0, adding them together may result in the f64 results 1.0. - { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: 1, expected: [-1, constants.positive.subnormal.min + 1] }, - { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: 0, expected: [0, constants.positive.subnormal.min] }, - { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: subnormalErr, expected: [-subnormalErr, constants.positive.subnormal.min + subnormalErr] }, - { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: 1, expected: [-1, constants.positive.subnormal.min + 1] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: 0, expected: [constants.negative.subnormal.max, 0] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: subnormalErr, expected: [constants.negative.subnormal.max - subnormalErr, subnormalErr] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 0, expected: [constants.negative.subnormal.max, 0] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: subnormalErr, expected: [constants.negative.subnormal.max - subnormalErr, subnormalErr] }, - { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, - ] as const : [] as const; - // prettier-ignore return [ // Edge Cases @@ -1940,7 +1917,20 @@ g.test('absoluteErrorInterval') { value: -2, error: smallErr, expected: [-2 - smallErr, -2 + smallErr] }, { value: -2, error: 1, expected: [-3, -1] }, - ...additionalSubnormal64bit, + // 64-bit subnormals, expected to be treated as 0.0 or smallest subnormal of kind. + { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: 0, expected: [0, constants.positive.subnormal.min] }, + { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: subnormalErr, expected: [-subnormalErr, constants.positive.subnormal.min + subnormalErr] }, + // Note that f32 minimum subnormal is so smaller than 1.0, adding them together may result in the f64 results 1.0. + { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: 1, expected: [-1, constants.positive.subnormal.min + 1] }, + { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: 0, expected: [0, constants.positive.subnormal.min] }, + { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: subnormalErr, expected: [-subnormalErr, constants.positive.subnormal.min + subnormalErr] }, + { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), error: 1, expected: [-1, constants.positive.subnormal.min + 1] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: 0, expected: [constants.negative.subnormal.max, 0] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: subnormalErr, expected: [constants.negative.subnormal.max - subnormalErr, subnormalErr] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 0, expected: [constants.negative.subnormal.max, 0] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: subnormalErr, expected: [constants.negative.subnormal.max - subnormalErr, subnormalErr] }, + { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, ]; }) ) @@ -2102,7 +2092,7 @@ const kULPErrorValue = { g.test('ulpInterval') .params(u => u - .combine('trait', ['abstract', 'f32', 'f16'] as const) + .combine('trait', ['f32', 'f16'] as const) .beginSubcases() .expandWithParams(p => { const trait = kFPTraitForULP[p.trait]; diff --git a/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index 36900e6da994..7887d82aaf70 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -1047,14 +1047,14 @@ export abstract class FPTraits { unreachable(`'refract' is not yet implemented for '${this.kind}'`); } - /** Version of absoluteErrorInterval that always returns the unboundedInterval */ - protected unboundedAbsoluteErrorInterval(_n: number, _error_range: number): FPInterval { - return this.constants().unboundedInterval; + /** Stub for absolute errors */ + protected unimplementedAbsoluteErrorInterval(_n: number, _error_range: number): FPInterval { + unreachable(`Absolute Error is not implement for '${this.kind}'`); } - /** Version of ulpInterval that always returns the unboundedInterval */ - protected unboundedUlpInterval(_n: number, _numULP: number): FPInterval { - return this.constants().unboundedInterval; + /** Stub for ULP errors */ + protected unimplementedUlpInterval(_n: number, _numULP: number): FPInterval { + unreachable(`ULP Error is not implement for '${this.kind}'`); } // Utilities - Defined by subclass @@ -5091,12 +5091,10 @@ class FPAbstractTraits extends FPTraits { public readonly sparseMatrixRange = sparseMatrixF64Range; // Framework - Fundamental Error Intervals - Overrides - public readonly absoluteErrorInterval = this.absoluteErrorIntervalImpl.bind(this); + public readonly absoluteErrorInterval = this.unimplementedAbsoluteErrorInterval.bind(this); // Should use FP.f32 instead public readonly correctlyRoundedInterval = this.correctlyRoundedIntervalImpl.bind(this); public readonly correctlyRoundedMatrix = this.correctlyRoundedMatrixImpl.bind(this); - public readonly ulpInterval = (n: number, numULP: number): FPInterval => { - return this.toInterval(kF32Traits.ulpInterval(n, numULP)); - }; + public readonly ulpInterval = this.unimplementedUlpInterval.bind(this); // Should use FP.f32 instead // Framework - API - Overrides public readonly absInterval = this.absIntervalImpl.bind(this); From 3b0f7f1658e778b649bf631e279f47da88c4db12 Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 13 Mar 2024 16:04:17 -0400 Subject: [PATCH 33/33] wgsl: valdate dot4I8Packed, dot4U8Packed on bool and bool vec (#3491) Issue: #3180 --- .../validation/expression/call/builtin/dot4I8Packed.spec.ts | 2 ++ .../validation/expression/call/builtin/dot4U8Packed.spec.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/webgpu/shader/validation/expression/call/builtin/dot4I8Packed.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/dot4I8Packed.spec.ts index 12a73ae0c602..56c5cf5403eb 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/dot4I8Packed.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/dot4I8Packed.spec.ts @@ -19,6 +19,8 @@ const kBadArgs = { '1f32': '(1u,2f)', '1bool': '(1u,true)', '1vec2u': '(1u,vec2u())', + bool_bool: '(false,true)', + bool2_bool2: '(vec2(),vec2(false,true))', }; export const g = makeTestGroup(ShaderValidationTest); diff --git a/src/webgpu/shader/validation/expression/call/builtin/dot4U8Packed.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/dot4U8Packed.spec.ts index 18fb97707da1..1d240af5a81b 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/dot4U8Packed.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/dot4U8Packed.spec.ts @@ -19,6 +19,8 @@ const kBadArgs = { '1f32': '(1u,2f)', '1bool': '(1u,true)', '1vec2u': '(1u,vec2u())', + bool_bool: '(false,true)', + bool2_bool2: '(vec2(),vec2(false,true))', }; export const g = makeTestGroup(ShaderValidationTest);