diff --git a/src/common/util/util.ts b/src/common/util/util.ts index 9433aaddb05f..e78c6d0c9b74 100644 --- a/src/common/util/util.ts +++ b/src/common/util/util.ts @@ -304,6 +304,7 @@ const TypedArrayBufferViewInstances = [ new Float16Array(), new Float32Array(), new Float64Array(), + new BigInt64Array(), ] as const; export type TypedArrayBufferView = (typeof TypedArrayBufferViewInstances)[number]; diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index d7b53aa05f21..3cfb01bd1585 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,106 +1,107 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "2c5e7574", - "webgpu/shader/execution/binary/af_logical.bin": "2885252a", - "webgpu/shader/execution/binary/af_division.bin": "de2c92cc", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "3c01149a", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "bd4616a3", - "webgpu/shader/execution/binary/af_multiplication.bin": "30337a92", - "webgpu/shader/execution/binary/af_remainder.bin": "b974c5d4", - "webgpu/shader/execution/binary/af_subtraction.bin": "e532bbbe", - "webgpu/shader/execution/binary/f16_addition.bin": "36beac4c", - "webgpu/shader/execution/binary/f16_logical.bin": "48b5847", - "webgpu/shader/execution/binary/f16_division.bin": "5377812e", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "2f778220", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "8f7db49c", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "ccc6898f", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "66ab0554", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "c570c9d1", - "webgpu/shader/execution/binary/f16_multiplication.bin": "eb1180d7", - "webgpu/shader/execution/binary/f16_remainder.bin": "85b6f6f2", - "webgpu/shader/execution/binary/f16_subtraction.bin": "17cb0c61", - "webgpu/shader/execution/binary/f32_addition.bin": "7ed11615", - "webgpu/shader/execution/binary/f32_logical.bin": "49eb7a68", - "webgpu/shader/execution/binary/f32_division.bin": "73dbed8a", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "72980509", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "58aa220c", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "f8b0e4a8", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "7878810e", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "58d640f4", - "webgpu/shader/execution/binary/f32_multiplication.bin": "16e09d10", - "webgpu/shader/execution/binary/f32_remainder.bin": "a6c6a8fa", - "webgpu/shader/execution/binary/f32_subtraction.bin": "6fa482ff", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "9428fcd7", - "webgpu/shader/execution/binary/i32_comparison.bin": "11d07528", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "243cb8a1", - "webgpu/shader/execution/binary/u32_comparison.bin": "272ae0fa", - "webgpu/shader/execution/abs.bin": "aa2f77b4", - "webgpu/shader/execution/acos.bin": "223ed115", - "webgpu/shader/execution/acosh.bin": "e2b339c8", - "webgpu/shader/execution/asin.bin": "b09c87", - "webgpu/shader/execution/asinh.bin": "a269a791", - "webgpu/shader/execution/atan.bin": "72435ef", - "webgpu/shader/execution/atan2.bin": "c766ee54", - "webgpu/shader/execution/atanh.bin": "ca85d013", - "webgpu/shader/execution/bitcast.bin": "f1dfaef7", - "webgpu/shader/execution/ceil.bin": "956505f5", - "webgpu/shader/execution/clamp.bin": "f411b304", - "webgpu/shader/execution/cos.bin": "40a28b62", - "webgpu/shader/execution/cosh.bin": "ad54c793", - "webgpu/shader/execution/cross.bin": "520c3390", - "webgpu/shader/execution/degrees.bin": "c847ea4f", - "webgpu/shader/execution/determinant.bin": "c601a65d", - "webgpu/shader/execution/distance.bin": "466b0741", - "webgpu/shader/execution/dot.bin": "8150a481", - "webgpu/shader/execution/exp.bin": "47940592", - "webgpu/shader/execution/exp2.bin": "ee99106", - "webgpu/shader/execution/faceForward.bin": "71fd946c", - "webgpu/shader/execution/floor.bin": "dc1e0116", - "webgpu/shader/execution/fma.bin": "f8a5fcd0", - "webgpu/shader/execution/fract.bin": "d64b4648", - "webgpu/shader/execution/frexp.bin": "fcbd25a7", - "webgpu/shader/execution/inverseSqrt.bin": "b9516be3", - "webgpu/shader/execution/ldexp.bin": "cea8acfb", - "webgpu/shader/execution/length.bin": "f2c1b5d4", - "webgpu/shader/execution/log.bin": "50f2df6", - "webgpu/shader/execution/log2.bin": "3fed9ca1", - "webgpu/shader/execution/max.bin": "c687421f", - "webgpu/shader/execution/min.bin": "93d6f7dd", - "webgpu/shader/execution/mix.bin": "90fc51a1", - "webgpu/shader/execution/modf.bin": "ffa60832", - "webgpu/shader/execution/normalize.bin": "edab0498", - "webgpu/shader/execution/pack2x16float.bin": "157b9079", - "webgpu/shader/execution/pow.bin": "7013e734", - "webgpu/shader/execution/quantizeToF16.bin": "d1b7619f", - "webgpu/shader/execution/radians.bin": "35d5c4a4", - "webgpu/shader/execution/reflect.bin": "8c78147", - "webgpu/shader/execution/refract.bin": "f6797744", - "webgpu/shader/execution/round.bin": "87053dcd", - "webgpu/shader/execution/saturate.bin": "a6478965", - "webgpu/shader/execution/sign.bin": "6764de33", - "webgpu/shader/execution/sin.bin": "2a231111", - "webgpu/shader/execution/sinh.bin": "f1aa29f4", - "webgpu/shader/execution/smoothstep.bin": "446216ba", - "webgpu/shader/execution/sqrt.bin": "8686bbeb", - "webgpu/shader/execution/step.bin": "c7f41980", - "webgpu/shader/execution/tan.bin": "3839e7f0", - "webgpu/shader/execution/tanh.bin": "e98e67a7", - "webgpu/shader/execution/transpose.bin": "4e422cd2", - "webgpu/shader/execution/trunc.bin": "abc8cd37", - "webgpu/shader/execution/unpack2x16float.bin": "9d44b030", - "webgpu/shader/execution/unpack2x16snorm.bin": "2c62ccb1", - "webgpu/shader/execution/unpack2x16unorm.bin": "953c2189", - "webgpu/shader/execution/unpack4x8snorm.bin": "d7afd331", - "webgpu/shader/execution/unpack4x8unorm.bin": "1de18638", - "webgpu/shader/execution/unary/af_arithmetic.bin": "d81659dd", - "webgpu/shader/execution/unary/af_assignment.bin": "3c291894", - "webgpu/shader/execution/unary/bool_conversion.bin": "a772b7a8", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "42a82294", - "webgpu/shader/execution/unary/f16_conversion.bin": "187f2a7d", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "1e8f5ba5", - "webgpu/shader/execution/unary/f32_conversion.bin": "44626171", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "af2d5073", - "webgpu/shader/execution/unary/i32_complement.bin": "d2454830", - "webgpu/shader/execution/unary/i32_conversion.bin": "fff42cc4", - "webgpu/shader/execution/unary/u32_complement.bin": "f999c865", - "webgpu/shader/execution/unary/u32_conversion.bin": "a2f40c7e" + "webgpu/shader/execution/binary/af_addition.bin": "eba1476", + "webgpu/shader/execution/binary/af_logical.bin": "2fdff104", + "webgpu/shader/execution/binary/af_division.bin": "df548fe", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "33fce0f", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "d91874cd", + "webgpu/shader/execution/binary/af_multiplication.bin": "befd840b", + "webgpu/shader/execution/binary/af_remainder.bin": "3e618e69", + "webgpu/shader/execution/binary/af_subtraction.bin": "14237c9b", + "webgpu/shader/execution/binary/f16_addition.bin": "331cde43", + "webgpu/shader/execution/binary/f16_logical.bin": "2495b3b8", + "webgpu/shader/execution/binary/f16_division.bin": "11c2c421", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "49514138", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "2016658", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "c76b299d", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "4bec10fa", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "6192b162", + "webgpu/shader/execution/binary/f16_multiplication.bin": "a0d0527b", + "webgpu/shader/execution/binary/f16_remainder.bin": "b9839e66", + "webgpu/shader/execution/binary/f16_subtraction.bin": "ebf7f0f4", + "webgpu/shader/execution/binary/f32_addition.bin": "2f161871", + "webgpu/shader/execution/binary/f32_logical.bin": "b792bcce", + "webgpu/shader/execution/binary/f32_division.bin": "89cf19e6", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "a4fd125f", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "e73d99e2", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "81f12724", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "dfabd8ab", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "3cbcfe99", + "webgpu/shader/execution/binary/f32_multiplication.bin": "82ce5ae", + "webgpu/shader/execution/binary/f32_remainder.bin": "7eea3091", + "webgpu/shader/execution/binary/f32_subtraction.bin": "c4518267", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "35db562d", + "webgpu/shader/execution/binary/i32_comparison.bin": "3aea1709", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "309aa9e3", + "webgpu/shader/execution/binary/u32_comparison.bin": "74afab9", + "webgpu/shader/execution/abs.bin": "cfde2529", + "webgpu/shader/execution/acos.bin": "eb561980", + "webgpu/shader/execution/acosh.bin": "e4f1c0f0", + "webgpu/shader/execution/asin.bin": "a2fa8235", + "webgpu/shader/execution/asinh.bin": "96567f9d", + "webgpu/shader/execution/atan.bin": "6c853cbd", + "webgpu/shader/execution/atan2.bin": "82c97086", + "webgpu/shader/execution/atanh.bin": "5e60f704", + "webgpu/shader/execution/bitcast.bin": "87c146d5", + "webgpu/shader/execution/ceil.bin": "21383e91", + "webgpu/shader/execution/clamp.bin": "1c96b539", + "webgpu/shader/execution/cos.bin": "956b7a3d", + "webgpu/shader/execution/cosh.bin": "f2bb8e16", + "webgpu/shader/execution/cross.bin": "2a5f9027", + "webgpu/shader/execution/degrees.bin": "46294f0d", + "webgpu/shader/execution/determinant.bin": "23aa0a3e", + "webgpu/shader/execution/distance.bin": "2b461512", + "webgpu/shader/execution/dot.bin": "e38ea290", + "webgpu/shader/execution/exp.bin": "4455696f", + "webgpu/shader/execution/exp2.bin": "bb006599", + "webgpu/shader/execution/faceForward.bin": "2bbbb0bd", + "webgpu/shader/execution/floor.bin": "9b0d6e07", + "webgpu/shader/execution/fma.bin": "33bcd52", + "webgpu/shader/execution/fract.bin": "2ab6d19f", + "webgpu/shader/execution/frexp.bin": "8dce4a7a", + "webgpu/shader/execution/inverseSqrt.bin": "7c799f30", + "webgpu/shader/execution/ldexp.bin": "ff53724f", + "webgpu/shader/execution/length.bin": "348b0fe5", + "webgpu/shader/execution/log.bin": "d4537242", + "webgpu/shader/execution/log2.bin": "c4010158", + "webgpu/shader/execution/max.bin": "c72c2f27", + "webgpu/shader/execution/min.bin": "da1cc040", + "webgpu/shader/execution/mix.bin": "7124ff44", + "webgpu/shader/execution/modf.bin": "cce9a8fb", + "webgpu/shader/execution/normalize.bin": "2aa96726", + "webgpu/shader/execution/pack2x16float.bin": "3d47cf41", + "webgpu/shader/execution/pow.bin": "ced94069", + "webgpu/shader/execution/quantizeToF16.bin": "4d558863", + "webgpu/shader/execution/radians.bin": "539694e5", + "webgpu/shader/execution/reflect.bin": "fc996f65", + "webgpu/shader/execution/refract.bin": "d5c12e24", + "webgpu/shader/execution/round.bin": "8153f0e3", + "webgpu/shader/execution/saturate.bin": "35cfdcb0", + "webgpu/shader/execution/sign.bin": "b845eb5f", + "webgpu/shader/execution/sin.bin": "ca416640", + "webgpu/shader/execution/sinh.bin": "48a248a2", + "webgpu/shader/execution/smoothstep.bin": "a4262b99", + "webgpu/shader/execution/sqrt.bin": "ecb66d2f", + "webgpu/shader/execution/step.bin": "441a1ff0", + "webgpu/shader/execution/tan.bin": "139b44d2", + "webgpu/shader/execution/tanh.bin": "6f034864", + "webgpu/shader/execution/transpose.bin": "ce29e902", + "webgpu/shader/execution/trunc.bin": "78326103", + "webgpu/shader/execution/unpack2x16float.bin": "7d7f403e", + "webgpu/shader/execution/unpack2x16snorm.bin": "3cb1dd10", + "webgpu/shader/execution/unpack2x16unorm.bin": "e393809e", + "webgpu/shader/execution/unpack4x8snorm.bin": "3c7c876", + "webgpu/shader/execution/unpack4x8unorm.bin": "3cf4adca", + "webgpu/shader/execution/unary/af_arithmetic.bin": "36ca2efd", + "webgpu/shader/execution/unary/af_assignment.bin": "5952912c", + "webgpu/shader/execution/unary/bool_conversion.bin": "70142326", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "dccd3bb1", + "webgpu/shader/execution/unary/f16_conversion.bin": "b66d4a94", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "19370c80", + "webgpu/shader/execution/unary/f32_conversion.bin": "1c842ed", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "7e5dfb12", + "webgpu/shader/execution/unary/i32_complement.bin": "f49af0fa", + "webgpu/shader/execution/unary/i32_conversion.bin": "c7859be0", + "webgpu/shader/execution/unary/u32_complement.bin": "e32b513d", + "webgpu/shader/execution/unary/u32_conversion.bin": "ea2e6707", + "webgpu/shader/execution/unary/ai_assignment.bin": "cb143977" } \ 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 3c24c88e1ffb..d3954903ac89 100644 Binary files a/src/resources/cache/webgpu/shader/execution/bitcast.bin and b/src/resources/cache/webgpu/shader/execution/bitcast.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/ai_assignment.bin b/src/resources/cache/webgpu/shader/execution/unary/ai_assignment.bin new file mode 100644 index 000000000000..07fb9153f51f Binary files /dev/null and b/src/resources/cache/webgpu/shader/execution/unary/ai_assignment.bin differ diff --git a/src/unittests/serialization.spec.ts b/src/unittests/serialization.spec.ts index 9717ba3ecf84..ea6ed5e42f10 100644 --- a/src/unittests/serialization.spec.ts +++ b/src/unittests/serialization.spec.ts @@ -16,6 +16,8 @@ import { } from '../webgpu/util/compare.js'; import { kValue } from '../webgpu/util/constants.js'; import { + abstractFloat, + abstractInt, bool, deserializeValue, f16, @@ -61,6 +63,18 @@ g.test('value').fn(t => { u8(kValue.u8.max - 1), u8(kValue.u8.max - 0), + abstractInt(kValue.i64.negative.min), + abstractInt(kValue.i64.negative.min + 1n), + abstractInt(kValue.i64.negative.min + 2n), + abstractInt(kValue.i64.negative.max - 2n), + abstractInt(kValue.i64.negative.max - 1n), + abstractInt(kValue.i64.positive.min), + abstractInt(kValue.i64.positive.min + 1n), + abstractInt(kValue.i64.positive.min + 2n), + abstractInt(kValue.i64.positive.max - 2n), + abstractInt(kValue.i64.positive.max - 1n), + abstractInt(kValue.i64.positive.max), + i32(kValue.i32.negative.min + 0), i32(kValue.i32.negative.min + 1), i32(kValue.i32.negative.min + 2), @@ -97,6 +111,21 @@ g.test('value').fn(t => { i8(kValue.i8.positive.max - 1), i8(kValue.i8.positive.max - 0), + abstractFloat(0), + abstractFloat(-0), + abstractFloat(1), + abstractFloat(-1), + abstractFloat(0.5), + abstractFloat(-0.5), + abstractFloat(kValue.f64.positive.max), + abstractFloat(kValue.f64.positive.min), + abstractFloat(kValue.f64.positive.subnormal.max), + abstractFloat(kValue.f64.positive.subnormal.min), + abstractFloat(kValue.f64.negative.subnormal.max), + abstractFloat(kValue.f64.negative.subnormal.min), + abstractFloat(kValue.f64.positive.infinity), + abstractFloat(kValue.f64.negative.infinity), + f32(0), f32(-0), f32(1), @@ -134,6 +163,13 @@ g.test('value').fn(t => { vec3(u32(1), u32(2), u32(3)), vec4(bool(false), bool(true), bool(false), bool(true)), + toMatrix( + [ + [0.0, 1.0], + [2.0, 3.0], + ], + abstractFloat + ), toMatrix( [ [0.0, 1.0], @@ -148,6 +184,13 @@ g.test('value').fn(t => { ], f16 ), + toMatrix( + [ + [0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + ], + abstractFloat + ), toMatrix( [ [0.0, 1.0, 2.0, 3.0], @@ -163,6 +206,14 @@ g.test('value').fn(t => { ], f16 ), + toMatrix( + [ + [0.0, 1.0, 2.0], + [3.0, 4.0, 5.0], + [6.0, 7.0, 8.0], + ], + abstractFloat + ), toMatrix( [ [0.0, 1.0, 2.0], @@ -179,6 +230,15 @@ g.test('value').fn(t => { ], f16 ), + toMatrix( + [ + [0.0, 1.0], + [2.0, 3.0], + [4.0, 5.0], + [6.0, 7.0], + ], + abstractFloat + ), toMatrix( [ [0.0, 1.0], @@ -197,6 +257,15 @@ g.test('value').fn(t => { ], f16 ), + toMatrix( + [ + [0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + [8.0, 9.0, 10.0, 11.0], + [12.0, 13.0, 14.0, 15.0], + ], + abstractFloat + ), toMatrix( [ [0.0, 1.0, 2.0, 3.0], diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts index 57ae22904e2f..f6b72908fba3 100644 --- a/src/webgpu/gpu_test.ts +++ b/src/webgpu/gpu_test.ts @@ -788,7 +788,8 @@ export class GPUTestBase extends Fixture { slice = 0, layout, generateWarningOnly = false, - checkElementsBetweenFn = (act, [a, b]) => checkElementsBetween(act, [i => a[i], i => b[i]]), + checkElementsBetweenFn = (act, [a, b]) => + checkElementsBetween(act, [i => a[i] as number, i => b[i] as number]), }: { exp: [TypedArrayBufferView, TypedArrayBufferView]; slice?: number; diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 016584be6d2d..ac0678301f59 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1527,6 +1527,9 @@ "webgpu:shader,execution,expression,unary,af_assignment:abstract:*": { "subcaseMS": 788.400 }, "webgpu:shader,execution,expression,unary,af_assignment:f16:*": { "subcaseMS": 1.000 }, "webgpu:shader,execution,expression,unary,af_assignment:f32:*": { "subcaseMS": 42.000 }, + "webgpu:shader,execution,expression,unary,ai_assignment:abstract:*": { "subcaseMS": 0.000 }, + "webgpu:shader,execution,expression,unary,ai_assignment:i32:*": { "subcaseMS": 0.000 }, + "webgpu:shader,execution,expression,unary,ai_assignment:u32:*": { "subcaseMS": 0.000 }, "webgpu:shader,execution,expression,unary,bool_conversion:bool:*": { "subcaseMS": 8.357 }, "webgpu:shader,execution,expression,unary,bool_conversion:f16:*": { "subcaseMS": 44.794 }, "webgpu:shader,execution,expression,unary,bool_conversion:f32:*": { "subcaseMS": 41.276 }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAdd.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAdd.spec.ts index 37d3ce529204..187a5554498e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAdd.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAdd.spec.ts @@ -35,7 +35,7 @@ fn atomicAdd(atomic_ptr: ptr, read_write>, v: T) -> T u .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -72,7 +72,7 @@ fn atomicAdd(atomic_ptr: ptr, read_write>, v: T) -> T u .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { // Allocate one extra element to ensure it doesn't get modified diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAnd.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAnd.spec.ts index ed5cfa84a3b8..ad05bd851dac 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAnd.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicAnd.spec.ts @@ -38,7 +38,7 @@ fn atomicAnd(atomic_ptr: ptr, read_write>, v: T) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -91,7 +91,7 @@ fn atomicAnd(atomic_ptr: ptr, read_write>, v: T) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize; diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicCompareExchangeWeak.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicCompareExchangeWeak.spec.ts index 85cc5cce4dec..79e0597af675 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicCompareExchangeWeak.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicCompareExchangeWeak.spec.ts @@ -48,7 +48,7 @@ struct __atomic_compare_exchange_result { .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(async t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -187,7 +187,7 @@ struct __atomic_compare_exchange_result { .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(async t => { const numInvocations = t.params.workgroupSize; @@ -333,7 +333,7 @@ struct __atomic_compare_exchange_result { .params(u => u .combine('workgroupSize', onlyWorkgroupSizes) // - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(async t => { const numInvocations = t.params.workgroupSize; @@ -555,7 +555,7 @@ struct __atomic_compare_exchange_result { .params(u => u .combine('workgroupSize', onlyWorkgroupSizes) // - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(async t => { const numInvocations = t.params.workgroupSize; diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicExchange.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicExchange.spec.ts index 540ac16b073d..00b6ddb7e33f 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicExchange.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicExchange.spec.ts @@ -26,7 +26,7 @@ fn atomicExchange(atomic_ptr: ptr, read_write>, v: T) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -125,7 +125,7 @@ fn atomicLoad(atomic_ptr: ptr, read_write>) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize; @@ -236,7 +236,7 @@ fn atomicExchange(atomic_ptr: ptr, read_write>, v: T) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(async t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -350,7 +350,7 @@ fn atomicLoad(atomic_ptr: ptr, read_write>) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(async t => { const numInvocations = t.params.workgroupSize; diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicLoad.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicLoad.spec.ts index 2aac7bb9b92e..23ca127caed9 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicLoad.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicLoad.spec.ts @@ -26,7 +26,7 @@ fn atomicLoad(atomic_ptr: ptr, read_write>) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -117,7 +117,7 @@ fn atomicLoad(atomic_ptr: ptr, read_write>) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize; diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMax.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMax.spec.ts index 066d67301813..86bb8b460dc3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMax.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMax.spec.ts @@ -35,7 +35,7 @@ fn atomicMax(atomic_ptr: ptr, read_write>, v: T) -> T u .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -72,7 +72,7 @@ fn atomicMax(atomic_ptr: ptr, read_write>, v: T) -> T u .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { // Allocate one extra element to ensure it doesn't get modified diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMin.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMin.spec.ts index ad880c418260..d9a42d15ee14 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMin.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicMin.spec.ts @@ -35,7 +35,7 @@ fn atomicMin(atomic_ptr: ptr, read_write>, v: T) -> T u .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { // Allocate one extra element to ensure it doesn't get modified @@ -71,7 +71,7 @@ fn atomicMin(atomic_ptr: ptr, read_write>, v: T) -> T u .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { // Allocate one extra element to ensure it doesn't get modified diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicOr.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicOr.spec.ts index 3892d41b3849..8ddba24385a1 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicOr.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicOr.spec.ts @@ -38,7 +38,7 @@ fn atomicOr(atomic_ptr: ptr, read_write>, v: T) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -90,7 +90,7 @@ fn atomicOr(atomic_ptr: ptr, read_write>, v: T) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize; diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicStore.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicStore.spec.ts index 18ff72975d84..4d226e312b74 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicStore.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicStore.spec.ts @@ -32,7 +32,7 @@ fn atomicStore(atomic_ptr: ptr, read_write>, v: T) .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -72,7 +72,7 @@ fn atomicStore(atomic_ptr: ptr, read_write>, v: T) .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize; @@ -117,7 +117,7 @@ one of the values written. .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(async t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -210,7 +210,7 @@ one of the values written. .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(async t => { const numInvocations = t.params.workgroupSize; diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicSub.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicSub.spec.ts index 6cea190299ad..8fdced1df2a8 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicSub.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicSub.spec.ts @@ -35,7 +35,7 @@ fn atomicSub(atomic_ptr: ptr, read_write>, v: T) -> T u .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -72,7 +72,7 @@ fn atomicSub(atomic_ptr: ptr, read_write>, v: T) -> T u .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { // Allocate one extra element to ensure it doesn't get modified diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicXor.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicXor.spec.ts index 99192fd9fe37..224004359091 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicXor.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/atomicXor.spec.ts @@ -38,7 +38,7 @@ fn atomicXor(atomic_ptr: ptr, read_write>, v: T) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize * t.params.dispatchSize; @@ -91,7 +91,7 @@ fn atomicXor(atomic_ptr: ptr, read_write>, v: T) -> T .combine('workgroupSize', workgroupSizes) .combine('dispatchSize', dispatchSizes) .combine('mapId', keysOf(kMapId)) - .combine('scalarType', ['u32', 'i32']) + .combine('scalarType', ['u32', 'i32'] as const) ) .fn(t => { const numInvocations = t.params.workgroupSize; diff --git a/src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts b/src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts index ed02467f80b2..e12cf537cd32 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts @@ -25,15 +25,14 @@ export const kMapId = { }, }; -export function typedArrayCtor(scalarType: string): TypedArrayBufferViewConstructor { +export function typedArrayCtor( + scalarType: 'u32' | 'i32' +): TypedArrayBufferViewConstructor { switch (scalarType) { case 'u32': return Uint32Array; case 'i32': return Int32Array; - default: - assert(false, 'Atomic variables can only by u32 or i32'); - return Uint8Array; } } diff --git a/src/webgpu/shader/execution/expression/expression.ts b/src/webgpu/shader/execution/expression/expression.ts index d59456c5596a..436d99f2288a 100644 --- a/src/webgpu/shader/execution/expression/expression.ts +++ b/src/webgpu/shader/execution/expression/expression.ts @@ -47,6 +47,10 @@ export type Config = { // Helper for returning the stride for a given Type function valueStride(ty: Type): number { + assert( + scalarTypeOf(ty).kind !== 'abstract-int', + `Outputting 'abstract-int' values currently not supported` + ); // AbstractFloats are passed out of the shader via a struct of 2x u32s and // unpacking containers as arrays if (scalarTypeOf(ty).kind === 'abstract-float') { @@ -139,7 +143,7 @@ function valueStride(ty: Type): number { return 16; } -// Helper for summing up all of the stride values for an array of Types +// 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); } @@ -148,6 +152,7 @@ function valueStrides(tys: Type[]): number { function storageType(ty: Type): Type { if (ty instanceof ScalarType) { assert(ty.kind !== 'f64', `No storage type defined for 'f64' values`); + assert(ty.kind !== 'abstract-int', `Storage of 'abstract-int' values not yet implemented`); assert( ty.kind !== 'abstract-float', `Custom handling is implemented for 'abstract-float' values` @@ -165,16 +170,21 @@ function storageType(ty: Type): Type { // Helper for converting a value of the type 'ty' from the storage type. function fromStorage(ty: Type, expr: string): string { if (ty instanceof ScalarType) { - assert(ty.kind !== 'abstract-float', `AbstractFloat values should not be in input storage`); + assert(ty.kind !== 'abstract-int', `'abstract-int' values should not be in input storage`); + assert(ty.kind !== 'abstract-float', `'abstract-float' values should not be in input storage`); assert(ty.kind !== 'f64', `'No storage type defined for 'f64' values`); if (ty.kind === 'bool') { return `${expr} != 0u`; } } if (ty instanceof VectorType) { + assert( + ty.elementType.kind !== 'abstract-int', + `'abstract-int' values cannot appear in input storage` + ); assert( ty.elementType.kind !== 'abstract-float', - `AbstractFloat values cannot appear in input storage` + `'abstract-float' values cannot appear in input storage` ); assert(ty.elementType.kind !== 'f64', `'No storage type defined for 'f64' values`); if (ty.elementType.kind === 'bool') { @@ -187,9 +197,10 @@ function fromStorage(ty: Type, expr: string): string { // Helper for converting a value of the type 'ty' to the storage type. function toStorage(ty: Type, expr: string): string { if (ty instanceof ScalarType) { + assert(ty.kind !== 'abstract-int', `Storage of 'abstract-int' values not yet implemented`); assert( ty.kind !== 'abstract-float', - `AbstractFloat values have custom code for writing to storage` + `'abstract-float' values have custom code for writing to storage` ); assert(ty.kind !== 'f64', `No storage type defined for 'f64' values`); if (ty.kind === 'bool') { @@ -197,9 +208,13 @@ function toStorage(ty: Type, expr: string): string { } } if (ty instanceof VectorType) { + assert( + ty.elementType.kind !== 'abstract-int', + `Storage of 'abstract-int' values not yet implemented` + ); assert( ty.elementType.kind !== 'abstract-float', - `AbstractFloat values have custom code for writing to storage` + `'abstract-float' values have custom code for writing to storage` ); assert(ty.elementType.kind !== 'f64', `'No storage type defined for 'f64' values`); if (ty.elementType.kind === 'bool') { @@ -459,6 +474,10 @@ export type ShaderBuilder = ( * Helper that returns the WGSL to declare the output storage buffer for a shader */ function wgslOutputs(resultType: Type, count: number): string { + assert( + scalarTypeOf(resultType).kind !== 'abstract-int', + `Outputting 'abstract-int' values not yet implemented` + ); let output_struct = undefined; if (scalarTypeOf(resultType).kind !== 'abstract-float') { output_struct = ` @@ -568,6 +587,10 @@ function basicExpressionShaderBody( cases: CaseList, inputSource: InputSource ): string { + assert( + scalarTypeOf(resultType).kind !== 'abstract-int', + `Outputting 'abstract-int' values not yet implemented` + ); assert( scalarTypeOf(resultType).kind !== 'abstract-float', `abstractFloatShaderBuilder should be used when result type is 'abstract-float` @@ -925,7 +948,7 @@ export function abstractFloatShaderBuilder(expressionBuilder: ExpressionBuilder) cases: CaseList, inputSource: InputSource ) => { - assert(inputSource === 'const', 'AbstractFloat results are only defined for const-eval'); + assert(inputSource === 'const', `'abstract-float' results are only defined for const-eval`); assert( scalarTypeOf(resultType).kind === 'abstract-float', `Expected resultType of 'abstract-float', received '${scalarTypeOf(resultType).kind}' instead` diff --git a/src/webgpu/shader/execution/expression/unary/ai_assignment.cache.ts b/src/webgpu/shader/execution/expression/unary/ai_assignment.cache.ts new file mode 100644 index 000000000000..9e610962c7a1 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/ai_assignment.cache.ts @@ -0,0 +1,16 @@ +import { abstractInt, i32, u32 } from '../../../../util/conversion.js'; +import { fullI32Range, fullU32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/ai_assignment', { + i32: () => { + return fullI32Range().map(n => { + return { input: abstractInt(BigInt(n)), expected: i32(n) }; + }); + }, + u32: () => { + return fullU32Range().map(n => { + return { input: abstractInt(BigInt(n)), expected: u32(n) }; + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts b/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts new file mode 100644 index 000000000000..bc2ea19848d5 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts @@ -0,0 +1,51 @@ +export const description = ` +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 { ShaderBuilder, basicExpressionBuilder, onlyConstInputSource, run } from '../expression.js'; + +import { d } from './ai_assignment.cache.js'; + +function concrete_assignment(): ShaderBuilder { + return basicExpressionBuilder(value => `${value}`); +} +export const g = makeTestGroup(GPUTest); + +g.test('abstract') + .specURL('https://www.w3.org/TR/WGSL/#abstract-types') + .desc( + ` +testing that extracting abstract ints works +` + ) + .params(u => u.combine('inputSource', onlyConstInputSource)) + .unimplemented(); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#i32-builtin') + .desc( + ` +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); + }); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#u32-builtin') + .desc( + ` +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); + }); diff --git a/src/webgpu/shader/execution/memory_model/memory_model_setup.ts b/src/webgpu/shader/execution/memory_model/memory_model_setup.ts index 20b9d8ee9769..de79bbe703f7 100644 --- a/src/webgpu/shader/execution/memory_model/memory_model_setup.ts +++ b/src/webgpu/shader/execution/memory_model/memory_model_setup.ts @@ -485,8 +485,8 @@ export class MemoryModelTester { * If the weak index's value is not 0, it means the test has observed a behavior disallowed by the memory model and * is considered a test failure. */ - protected checkResult(weakIndex: number): (i: number, v: number) => boolean { - return function (i: number, v: number): boolean { + protected checkResult(weakIndex: number): (i: number, v: number | bigint) => boolean { + return function (i: number, v: number | bigint): boolean { if (i === weakIndex && v > 0) { return false; } @@ -495,7 +495,7 @@ export class MemoryModelTester { } /** Returns a printer function that visualizes the results of checking the test results. */ - protected resultPrinter(weakIndex: number): (i: number) => string | number { + protected resultPrinter(weakIndex: number): (i: number) => string | number | bigint { return function (i: number): string | number { if (i === weakIndex) { return 0; diff --git a/src/webgpu/util/binary_stream.ts b/src/webgpu/util/binary_stream.ts index a6512020e631..2531521dc44e 100644 --- a/src/webgpu/util/binary_stream.ts +++ b/src/webgpu/util/binary_stream.ts @@ -85,6 +85,16 @@ export default class BinaryStream { return this.view.getInt16(this.alignedOffset(2), /* littleEndian */ true); } + /** writeI64() writes a bitint to the buffer at the next 64-bit aligned offset */ + writeI64(value: bigint) { + this.view.setBigInt64(this.alignedOffset(8), value, /* littleEndian */ true); + } + + /** readI64() reads a bigint from the buffer at the next 64-bit aligned offset */ + readI64(): bigint { + return this.view.getBigInt64(this.alignedOffset(8), /* littleEndian */ true); + } + /** writeI32() writes a int32 to the buffer at the next 32-bit aligned offset */ writeI32(value: number) { this.view.setInt32(this.alignedOffset(4), value, /* littleEndian */ true); diff --git a/src/webgpu/util/check_contents.ts b/src/webgpu/util/check_contents.ts index 298e7ae4a9e9..cda9c828a037 100644 --- a/src/webgpu/util/check_contents.ts +++ b/src/webgpu/util/check_contents.ts @@ -19,7 +19,7 @@ import { generatePrettyTable } from './pretty_diff_tables.js'; /** Generate an expected value at `index`, to test for equality with the actual value. */ export type CheckElementsGenerator = (index: number) => number; /** Check whether the actual `value` at `index` is as expected. */ -export type CheckElementsPredicate = (index: number, value: number) => boolean; +export type CheckElementsPredicate = (index: number, value: number | bigint) => boolean; /** * Provides a pretty-printing implementation for a particular CheckElementsPredicate. * This is an array; each element provides info to print an additional row in the error message. @@ -29,9 +29,9 @@ export type CheckElementsSupplementalTableRows = Array<{ leftHeader: string; /** * Get the value for a cell in the table with element index `index`. - * May be a string or a number; a number will be formatted according to the TypedArray type used. + * May be a string or numeric (number | bigint); numerics will be formatted according to the TypedArray type used. */ - getValueForCell: (index: number) => number | string; + getValueForCell: (index: number) => string | number | bigint; }>; /** @@ -221,13 +221,17 @@ function failCheckElements({ const printElementsEnd = Math.min(size, failedElementsLast + 2); const printElementsCount = printElementsEnd - printElementsStart; - const numberToString = printAsFloat - ? (n: number) => n.toPrecision(4) - : (n: number) => intToPaddedHex(n, { byteLength: ctor.BYTES_PER_ELEMENT }); + const numericToString = (val: number | bigint): string => { + if (typeof val === 'number' && printAsFloat) { + return val.toPrecision(4); + } + return intToPaddedHex(val, { byteLength: ctor.BYTES_PER_ELEMENT }); + }; + const numberPrefix = printAsFloat ? '' : '0x:'; const printActual = actual.subarray(printElementsStart, printElementsEnd); - const printExpected: Array> = []; + const printExpected: Array> = []; if (predicatePrinter) { for (const { leftHeader, getValueForCell: cell } of predicatePrinter) { printExpected.push( @@ -246,7 +250,7 @@ function failCheckElements({ const opts = { fillToWidth: 120, - numberToString, + numericToString, }; const msg = `Array had unexpected contents at indices ${failedElementsFirst} through ${failedElementsLast}. Starting at index ${printElementsStart}: @@ -263,10 +267,11 @@ ${generatePrettyTable(opts, [ // Helper helpers /** Convert an integral `number` into a hex string, padded to the specified `byteLength`. */ -function intToPaddedHex(number: number, { byteLength }: { byteLength: number }) { - assert(Number.isInteger(number), 'number must be integer'); - let s = Math.abs(number).toString(16); - if (byteLength) s = s.padStart(byteLength * 2, '0'); - if (number < 0) s = '-' + s; - return s; +function intToPaddedHex(val: number | bigint, { byteLength }: { byteLength: number }) { + assert(Number.isInteger(val), 'number must be integer'); + const is_negative = typeof val === 'number' ? val < 0 : val < 0n; + let str = (is_negative ? -val : val).toString(16); + if (byteLength) str = str.padStart(byteLength * 2, '0'); + if (is_negative) str = '-' + str; + return str; } diff --git a/src/webgpu/util/constants.ts b/src/webgpu/util/constants.ts index 5ee819c64e4f..b7426114a497 100644 --- a/src/webgpu/util/constants.ts +++ b/src/webgpu/util/constants.ts @@ -242,6 +242,18 @@ export const kBit = { } as const; export const kValue = { + // Limits of i64 + i64: { + positive: { + min: BigInt(0n), + max: BigInt(9223372036854775807n), + }, + negative: { + min: BigInt(-9223372036854775808n), + max: BigInt(0n), + }, + }, + // Limits of i32 i32: { positive: { diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index d98367447d7d..b2cc56dcf0b0 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -84,6 +84,7 @@ const workingDataI16 = new Int16Array(workingData); const workingDataI32 = new Int32Array(workingData); const workingDataI8 = new Int8Array(workingData); const workingDataF64 = new Float64Array(workingData); +const workingDataI64 = new BigInt64Array(workingData); const workingDataView = new DataView(workingData); /** @@ -584,6 +585,7 @@ export type ScalarKind = | 'u32' | 'u16' | 'u8' + | 'abstract-int' | 'i32' | 'i16' | 'i8' @@ -610,31 +612,44 @@ export class ScalarType { } /** Constructs a Scalar of this type with `value` */ - public create(value: number): Scalar { - switch (this.kind) { - case 'abstract-float': - return abstractFloat(value); - case 'f64': - return f64(value); - case 'f32': - return f32(value); - case 'f16': - return f16(value); - case 'u32': - return u32(value); - case 'u16': - return u16(value); - case 'u8': - return u8(value); - case 'i32': - return i32(value); - case 'i16': - return i16(value); - case 'i8': - return i8(value); - case 'bool': - return bool(value !== 0); + public create(value: number | bigint): Scalar { + switch (typeof value) { + case 'number': + switch (this.kind) { + case 'abstract-float': + return abstractFloat(value); + case 'f64': + return f64(value); + case 'f32': + return f32(value); + case 'f16': + return f16(value); + case 'u32': + return u32(value); + case 'u16': + return u16(value); + case 'u8': + return u8(value); + case 'i32': + return i32(value); + case 'i16': + return i16(value); + case 'i8': + return i8(value); + case 'bool': + return bool(value !== 0); + } + break; + case 'bigint': + switch (this.kind) { + case 'abstract-int': + return abstractInt(value); + case 'bool': + return bool(value !== 0n); + } + break; } + unreachable(`Scalar<${this.kind}>.create() does not support ${typeof value}`); } } @@ -754,14 +769,26 @@ export function TypeMat(cols: number, rows: number, elementType: ScalarType): Ma /** 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; + /** Copy bytes from `buf` at `offset` into the working data, then read it out using `workingDataOut` */ -function valueFromBytes(workingDataOut: TypedArrayBufferView, buf: Uint8Array, offset: number) { +function valueFromBytes( + workingDataOut: A, + buf: Uint8Array, + offset: number +): ArrayElementType { for (let i = 0; i < workingDataOut.BYTES_PER_ELEMENT; ++i) { workingDataU8[i] = buf[offset + i]; } - return workingDataOut[0]; + return workingDataOut[0] as ArrayElementType; } +export const TypeAbstractInt = 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) => i32(valueFromBytes(workingDataI32, buf, offset)) ); @@ -815,6 +842,8 @@ export function scalarType(kind: ScalarKind): ScalarType { return TypeU16; case 'u8': return TypeU8; + case 'abstract-int': + return TypeAbstractInt; case 'i32': return TypeI32; case 'i16': @@ -869,7 +898,7 @@ export function scalarTypeOf(ty: Type): ScalarType { } /** ScalarValue is the JS type that can be held by a Scalar */ -type ScalarValue = boolean | number; +type ScalarValue = boolean | number | bigint; /** Class that encapsulates a single scalar value of various types. */ export class Scalar { @@ -894,6 +923,10 @@ 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; @@ -910,24 +943,34 @@ export class Scalar { const str = `${x}`; return str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; }; - if (isFinite(this.value as number)) { - switch (this.type.kind) { - case 'abstract-float': - return `${withPoint(this.value as number)}`; - case 'f64': - return `${withPoint(this.value as number)}`; - case 'f32': - return `${withPoint(this.value as number)}f`; - case 'f16': - return `${withPoint(this.value as number)}h`; - case 'u32': - return `${this.value}u`; - case 'i32': - return `i32(${this.value})`; - case 'bool': + + switch (typeof this.value) { + case 'bigint': + if (this.type.kind === 'abstract-int') { 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}`; } + throw new Error( `scalar of value ${this.value} and type ${this.type} has no WGSL representation` ); @@ -988,10 +1031,10 @@ export interface ScalarBuilder { /** 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( +function scalarFromValue( type: ScalarType, - workingDataArray: TypedArrayBufferView, - value: number + 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; @@ -1038,6 +1081,10 @@ export const f32Bits = (bits: number): Scalar => export const f16Bits = (bits: number): Scalar => scalarFromBits(TypeF16, workingDataU16, workingDataF16, bits); +/** Create an AbstractInt from a numeric value, a JS `bigint`. */ +export const abstractInt = (value: bigint): Scalar => + scalarFromValue(TypeAbstractInt, workingDataI64, value); + /** Create an i32 from a numeric value, a JS `number`. */ export const i32 = (value: number): Scalar => scalarFromValue(TypeI32, workingDataI32, value); @@ -1314,6 +1361,7 @@ enum SerializedScalarKind { I16, I8, Bool, + AbstractInt, } /** serializeScalarKind() serializes a ScalarKind to a BinaryStream */ @@ -1340,6 +1388,9 @@ function serializeScalarKind(s: BinaryStream, v: ScalarKind) { case 'u8': s.writeU8(SerializedScalarKind.U8); return; + case 'abstract-int': + s.writeU8(SerializedScalarKind.AbstractInt); + return; case 'i32': s.writeU8(SerializedScalarKind.I32); return; @@ -1353,6 +1404,7 @@ function serializeScalarKind(s: BinaryStream, v: ScalarKind) { s.writeU8(SerializedScalarKind.Bool); return; } + unreachable(`Do not know what to write scalar kind = ${v}`); } /** deserializeScalarKind() deserializes a ScalarKind from a BinaryStream */ @@ -1373,6 +1425,8 @@ function deserializeScalarKind(s: BinaryStream): ScalarKind { return 'u16'; case SerializedScalarKind.U8: return 'u8'; + case SerializedScalarKind.AbstractInt: + return 'abstract-int'; case SerializedScalarKind.I32: return 'i32'; case SerializedScalarKind.I16: @@ -1395,40 +1449,55 @@ enum SerializedValueKind { /** serializeValue() serializes a Value to a BinaryStream */ export function serializeValue(s: BinaryStream, v: Value) { const serializeScalar = (scalar: Scalar, kind: ScalarKind) => { - switch (kind) { - case 'abstract-float': - s.writeF64(scalar.value as number); - return; - case 'f64': - s.writeF64(scalar.value as number); - return; - case 'f32': - s.writeF32(scalar.value as number); - return; - case 'f16': - s.writeF16(scalar.value as number); - return; - case 'u32': - s.writeU32(scalar.value as number); - return; - case 'u16': - s.writeU16(scalar.value as number); - return; - case 'u8': - s.writeU8(scalar.value as number); - return; - case 'i32': - s.writeI32(scalar.value as number); - return; - case 'i16': - s.writeI16(scalar.value as number); - return; - case 'i8': - s.writeI8(scalar.value as number); - return; - case 'bool': - s.writeBool(scalar.value as boolean); - return; + switch (typeof scalar.value) { + case 'number': + switch (kind) { + case 'abstract-float': + s.writeF64(scalar.value); + return; + case 'f64': + s.writeF64(scalar.value); + return; + case 'f32': + s.writeF32(scalar.value); + return; + case 'f16': + s.writeF16(scalar.value); + return; + case 'u32': + s.writeU32(scalar.value); + return; + case 'u16': + s.writeU16(scalar.value); + return; + case 'u8': + s.writeU8(scalar.value); + return; + case 'i32': + s.writeI32(scalar.value); + return; + case 'i16': + s.writeI16(scalar.value); + return; + case 'i8': + s.writeI8(scalar.value); + return; + } + break; + case 'bigint': + switch (kind) { + case 'abstract-int': + s.writeI64(scalar.value); + return; + } + break; + case 'boolean': + switch (kind) { + case 'bool': + s.writeBool(scalar.value); + return; + } + break; } }; @@ -1481,6 +1550,8 @@ export function deserializeValue(s: BinaryStream): Value { return u16(s.readU16()); case 'u8': return u8(s.readU8()); + case 'abstract-int': + return abstractInt(s.readI64()); case 'i32': return i32(s.readI32()); case 'i16': @@ -1533,7 +1604,7 @@ export function isFloatValue(v: Value): boolean { */ export function isAbstractType(ty: Type): boolean { if (ty instanceof ScalarType) { - return ty.kind === 'abstract-float'; + return ty.kind === 'abstract-float' || ty.kind === 'abstract-int'; } return false; } diff --git a/src/webgpu/util/pretty_diff_tables.ts b/src/webgpu/util/pretty_diff_tables.ts index af98ab7ecfc9..8dab839c1f5e 100644 --- a/src/webgpu/util/pretty_diff_tables.ts +++ b/src/webgpu/util/pretty_diff_tables.ts @@ -1,5 +1,27 @@ import { range } from '../../common/util/util.js'; +/** + * @returns a function that converts numerics to strings, depending on if they + * should be treated as integers or not. + */ +export function numericToStringBuilder(is_integer: boolean): (n: number | bigint) => string { + if (is_integer) { + return (val: number | bigint): string => { + if (typeof val === 'number') { + return val.toFixed(); + } + return val.toString(); + }; + } + + return (val: number | bigint): string => { + if (typeof val === 'number') { + return val.toPrecision(6); + } + return val.toString(); + }; +} + /** * Pretty-prints a "table" of cell values (each being `number | string`), right-aligned. * Each row may be any iterator, including lazily-generated (potentially infinite) rows. @@ -12,8 +34,11 @@ import { range } from '../../common/util/util.js'; * Each remaining argument provides one row for the table. */ export function generatePrettyTable( - { fillToWidth, numberToString }: { fillToWidth: number; numberToString: (n: number) => string }, - rows: ReadonlyArray> + { + fillToWidth, + numericToString, + }: { fillToWidth: number; numericToString: (n: number | bigint) => string }, + rows: ReadonlyArray> ): string { const rowStrings = range(rows.length, () => ''); let totalTableWidth = 0; @@ -23,7 +48,13 @@ export function generatePrettyTable( for (;;) { const cellsForColumn = iters.map(iter => { const r = iter.next(); // Advance the iterator for each row, in lock-step. - return r.done ? undefined : typeof r.value === 'number' ? numberToString(r.value) : r.value; + if (r.done) { + return undefined; + } + if (typeof r.value === 'number' || typeof r.value === 'bigint') { + return numericToString(r.value); + } + return r.value; }); if (cellsForColumn.every(cell => cell === undefined)) break; diff --git a/src/webgpu/util/texture/texel_view.ts b/src/webgpu/util/texture/texel_view.ts index fea23b674e50..b5f5265d7f33 100644 --- a/src/webgpu/util/texture/texel_view.ts +++ b/src/webgpu/util/texture/texel_view.ts @@ -1,6 +1,6 @@ import { assert, memcpy } from '../../../common/util/util.js'; import { kTextureFormatInfo, EncodableTextureFormat } from '../../format_info.js'; -import { generatePrettyTable } from '../pretty_diff_tables.js'; +import { generatePrettyTable, numericToStringBuilder } from '../pretty_diff_tables.js'; import { reifyExtent3D, reifyOrigin3D } from '../unions.js'; import { fullSubrectCoordinates } from './base.js'; @@ -166,10 +166,9 @@ export class TexelView { const info = kTextureFormatInfo[this.format]; const repr = kTexelRepresentationInfo[this.format]; - const integerSampleType = info.sampleType === 'uint' || info.sampleType === 'sint'; - const numberToString = integerSampleType - ? (n: number) => n.toFixed() - : (n: number) => n.toPrecision(6); + const numericToString = numericToStringBuilder( + info.sampleType === 'uint' || info.sampleType === 'sint' + ); const componentOrderStr = repr.componentOrder.join(',') + ':'; const subrectCoords = [...fullSubrectCoordinates(subrectOrigin, subrectSize)]; @@ -188,13 +187,13 @@ export class TexelView { yield* [' act. colors', '==', componentOrderStr]; for (const coords of subrectCoords) { const pixel = t.color(coords); - yield `${repr.componentOrder.map(ch => numberToString(pixel[ch]!)).join(',')}`; + yield `${repr.componentOrder.map(ch => numericToString(pixel[ch]!)).join(',')}`; } })(this); const opts = { fillToWidth: 120, - numberToString, + numericToString, }; return `${generatePrettyTable(opts, [printCoords, printActualBytes, printActualColors])}`; } diff --git a/src/webgpu/util/texture/texture_ok.ts b/src/webgpu/util/texture/texture_ok.ts index 7b85489246a7..5b0f1df7b343 100644 --- a/src/webgpu/util/texture/texture_ok.ts +++ b/src/webgpu/util/texture/texture_ok.ts @@ -2,7 +2,7 @@ import { assert, ErrorWithExtra, unreachable } from '../../../common/util/util.j import { kTextureFormatInfo, EncodableTextureFormat } from '../../format_info.js'; import { GPUTest } from '../../gpu_test.js'; import { numbersApproximatelyEqual } from '../conversion.js'; -import { generatePrettyTable } from '../pretty_diff_tables.js'; +import { generatePrettyTable, numericToStringBuilder } from '../pretty_diff_tables.js'; import { reifyExtent3D, reifyOrigin3D } from '../unions.js'; import { fullSubrectCoordinates } from './base.js'; @@ -223,11 +223,9 @@ export function findFailedPixels( const info = kTextureFormatInfo[format]; const repr = kTexelRepresentationInfo[format]; - - const integerSampleType = info.sampleType === 'uint' || info.sampleType === 'sint'; - const numberToString = integerSampleType - ? (n: number) => n.toFixed() - : (n: number) => n.toPrecision(6); + const numericToString = numericToStringBuilder( + info.sampleType === 'uint' || info.sampleType === 'sint' + ); const componentOrderStr = repr.componentOrder.join(',') + ':'; @@ -245,14 +243,14 @@ export function findFailedPixels( yield* [' act. colors', '==', componentOrderStr]; for (const coords of failedPixels) { const pixel = actTexelView.color(coords); - yield `${repr.componentOrder.map(ch => numberToString(pixel[ch]!)).join(',')}`; + yield `${repr.componentOrder.map(ch => numericToString(pixel[ch]!)).join(',')}`; } })(); const printExpectedColors = (function* () { yield* [' exp. colors', '==', componentOrderStr]; for (const coords of failedPixels) { const pixel = expTexelView.color(coords); - yield `${repr.componentOrder.map(ch => numberToString(pixel[ch]!)).join(',')}`; + yield `${repr.componentOrder.map(ch => numericToString(pixel[ch]!)).join(',')}`; } })(); const printActualULPs = (function* () { @@ -272,7 +270,7 @@ export function findFailedPixels( const opts = { fillToWidth: 120, - numberToString, + numericToString, }; return `\ between ${lowerCorner} and ${upperCorner} inclusive: