From 0e38887a789eb9199a9d440fcd7ba34a0fcb50ae Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Thu, 25 Jan 2024 12:55:18 -0500 Subject: [PATCH] Add support for AbstractInts as inputs (#3306) This PR adds in abstract ints to the Scalar type system, and wires up being able to write them as inputs as literals in shaders. Since they are abstract values they will never be supported as buffer inputs. Returning the values via buffers will be handled in a seperate PR. I have implemented the tests for AbstractInt -> i32 & u32 conversion to confirm the implementation operates as expected. There is a substantial number of changes related to the fact that the existing code assumes all numerics are `number`s, but AbstractInts need to be represented as BigInts. I have also cleaned up a couple of places related to the AbstractFloat implementation where there wasn't full testing coverage, etc. Issue #3302 --- src/common/util/util.ts | 1 + src/resources/cache/hashes.json | 209 ++++++++-------- .../cache/webgpu/shader/execution/bitcast.bin | Bin 2221448 -> 2221448 bytes .../shader/execution/unary/ai_assignment.bin | Bin 0 -> 3680 bytes src/unittests/serialization.spec.ts | 69 ++++++ src/webgpu/gpu_test.ts | 3 +- src/webgpu/listing_meta.json | 3 + .../call/builtin/atomics/atomicAdd.spec.ts | 4 +- .../call/builtin/atomics/atomicAnd.spec.ts | 4 +- .../atomics/atomicCompareExchangeWeak.spec.ts | 8 +- .../builtin/atomics/atomicExchange.spec.ts | 8 +- .../call/builtin/atomics/atomicLoad.spec.ts | 4 +- .../call/builtin/atomics/atomicMax.spec.ts | 4 +- .../call/builtin/atomics/atomicMin.spec.ts | 4 +- .../call/builtin/atomics/atomicOr.spec.ts | 4 +- .../call/builtin/atomics/atomicStore.spec.ts | 8 +- .../call/builtin/atomics/atomicSub.spec.ts | 4 +- .../call/builtin/atomics/atomicXor.spec.ts | 4 +- .../call/builtin/atomics/harness.ts | 7 +- .../shader/execution/expression/expression.ts | 35 ++- .../expression/unary/ai_assignment.cache.ts | 16 ++ .../expression/unary/ai_assignment.spec.ts | 51 ++++ .../memory_model/memory_model_setup.ts | 6 +- src/webgpu/util/binary_stream.ts | 10 + src/webgpu/util/check_contents.ts | 33 +-- src/webgpu/util/constants.ts | 12 + src/webgpu/util/conversion.ts | 233 ++++++++++++------ src/webgpu/util/pretty_diff_tables.ts | 37 ++- src/webgpu/util/texture/texel_view.ts | 13 +- src/webgpu/util/texture/texture_ok.ts | 16 +- 30 files changed, 550 insertions(+), 260 deletions(-) create mode 100644 src/resources/cache/webgpu/shader/execution/unary/ai_assignment.bin create mode 100644 src/webgpu/shader/execution/expression/unary/ai_assignment.cache.ts create mode 100644 src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts 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 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/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 0000000000000000000000000000000000000000..07fb9153f51f9d43d81c06d5cb32e61f34062b3d GIT binary patch literal 3680 zcmZYBeQ1?s9Eb7SdDqctn(u|8wMLLH(;AM-k~D=RwT7BlQ&A~ft5};8(af||Oe$1t zu2iPwQfHbhEvNn=!z_F$Yfj4+O;V#p8Dxc{?DyOCd{0CNwq5(|-q|=i=es8(gwWRB z3PWzV!#+N4Zy|K>cQ0PGTL`JvPAnYXa$VZJ<+qpTrQKVeQ$IiL-txMFuhZ@=|8>Zu z#rnD4@(&+wPP@0f$AR@rblzLuv9$0dxwrhSviz6j-txM-iA&|)@+DJCm&v{5XGgxb zT<$GDTAHy!?k%5qspJ*8x4i75zgNn=<%5rHc~$N$Up(N+*X7>wqK46{ze(;bAMjcGcjVsk z=dv2B)EK z`&;O-4y z$-U*5N;1Edd&~DPT76FLE$??`=ta4=y!Z3JHOsx_?-!l8B=?p#R@GgWd&@Tus=p%l zmb>}QcZrX!TNVDjweFRDinmFBAr)xcwk!Ec$~P^2-nJ|4H0N!*(oW;HU1_Ir+pgqC zrvi=JcBP%hZM!nKqg0@A+b*AHyQKn++jd1cjoWrzBd2lOu50BqZrhbSV5vakwq5OY zp2lsvGUYUG+eO&#N%N^~SMoro0*%{tb=LjRxNTP#IgQ(Pb(7P$ZC4LDjoWthlGC_t z*Nt)-x9#d9r*Ye^zH%C0cqIR3IgQ(PWy@*2IIHniIgO7$bo4ekjoWq&l+*Z^O;-lX zX?*^yVYzY|x9v)v(5XPFH9}6~wq2v;H2!^M%L8&6x9u7$ zr}2JWd*;h&+_r0yoW>9A-Y`W@bhezvZM%x(G`_3oM6sO4 zzp1J#lhgR7K^l%!pmE!-MLO^8FY>bF8jL?A=Q!iJdI!ece{f}ulVxV!Z$^1KF>-E_` Dhgc8S literal 0 HcmV?d00001 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: