diff --git a/docs/case_cache.md b/docs/case_cache.md index 8e53834ce473..c3ba8718b5dd 100644 --- a/docs/case_cache.md +++ b/docs/case_cache.md @@ -9,8 +9,8 @@ for multiple CTS runs. ## File cache To speed up execution of the CTS, the CTS git repo holds holds pre-computed -test cases, serialized in a set of gzip-compressed binary files under -[`src/resources/cache`](../src/resources/cache). +test cases, generated from `*.cache.ts` files and serialized in a set of binary +files under [`src/resources/cache`](../src/resources/cache). These files are regenerated by [`src/common/tools/gen_cache.ts`](../src/common/tools/gen_cache.ts) which can be run with `npx grunt run:generate-cache`. @@ -24,10 +24,11 @@ output, and compares this hash to the hash stored in [`src/resources/cache/hashes.json`](`../src/resources/cache/hashes.json`). Only those cache files with differing hashes are rebuilt. -Since source changes will sometimes change the hash without changing the generated cache, -sometimes the cache will be regenerated unnecessarily. **This is OK, but try to avoid committing -no-op regenerations - this will happen if your version of Node produces different gzip outputs -than the original committer's Node did for the same input.** +Transitive imports easily grow, and these can cause unnecessary rebuilds of the cache. +To help avoid unnecessary rebuilds, files that are known to not be used by the cache can be +annotated with a `MUST_NOT_BE_IMPORTED_BY_DATA_CACHE` comment anywhere in the file. If a file with +this comment is transitively imported by a `.cache.ts` file, then the cache generation tool will +error with a trace of the imports from the `.cache.ts` file to the file with this comment. The cache files are copied from [`src/resources/cache`](../src/resources/cache) to the `resources/cache` subdirectory of the @@ -46,13 +47,15 @@ will build the cases during CTS execution and store the results in an in-memory To add test cases to the cache: -1. Import `makeCaseCache` from [`'case_cache.js'`](../src/webgpu/shader/execution/expression/case_cache.ts); +1. Create a new my_file.cache.ts file. + +2. In that file, import `makeCaseCache` from [`'case_cache.js'`](../src/webgpu/shader/execution/expression/case_cache.ts); ```ts import { makeCaseCache } from '../case_cache.js'; // your relative path may vary ``` -2. Declare an exported global variable with the name `d`, assigned with the return value of `makeCaseCache()`: +3. Declare an exported global variable with the name `d`, assigned with the return value of `makeCaseCache()`: ```ts export const d = makeCaseCache('unique/path/of/your/cache/file', { @@ -65,12 +68,14 @@ export const d = makeCaseCache('unique/path/of/your/cache/file', { }); ``` -3. To load the cases from the cache, use `d.get();` +4. To use the cached cases in a my_file.spec.ts file, import `d` from my_file.cache.js, and use `d.get();` ```ts +import { d } from './my_file.cache.js'; + const cases = await d.get('name_of_your_case'); // cases will either be loaded from the cache file, loaded from the in-memory // LRU, or built on the fly. ``` -4. Run `npx grunt run generate-cache` to generate the new cache file. +5. Run `npx grunt run generate-cache` to generate the new cache file. diff --git a/src/common/framework/data_cache.ts b/src/common/framework/data_cache.ts index fea2bd203fae..c1e3a889beb3 100644 --- a/src/common/framework/data_cache.ts +++ b/src/common/framework/data_cache.ts @@ -187,11 +187,11 @@ export interface Cacheable { /** * serialize() encodes `data` to a binary representation so that it can be stored in a cache file. */ - serialize(data: Data): Promise; + serialize(data: Data): Uint8Array; /** * deserialize() is the inverse of serialize(), decoding the binary representation back to a Data * object. */ - deserialize(binary: Uint8Array): Promise; + deserialize(binary: Uint8Array): Data; } diff --git a/src/common/tools/gen_cache.ts b/src/common/tools/gen_cache.ts index 0ffbb373a0aa..d8309ebcb1c1 100644 --- a/src/common/tools/gen_cache.ts +++ b/src/common/tools/gen_cache.ts @@ -89,7 +89,7 @@ dataCache.setStore({ }); setIsBuildingDataCache(); -const specFileSuffix = __filename.endsWith('.ts') ? '.spec.ts' : '.spec.js'; +const cacheFileSuffix = __filename.endsWith('.ts') ? '.cache.ts' : '.cache.js'; /** * @returns a list of all the files under 'dir' that has the given extension @@ -105,6 +105,7 @@ function glob(dir: string, ext: string) { files.push(`${file}/${child}`); } } + if (path.endsWith(ext) && fs.statSync(path).isFile()) { files.push(file); } @@ -112,6 +113,19 @@ function glob(dir: string, ext: string) { return files; } +/** + * Exception type thrown by SourceHasher.hashFile() when a file annotated with + * MUST_NOT_BE_IMPORTED_BY_DATA_CACHE is transitively imported by a .cache.ts file. + */ +class InvalidImportException { + constructor(path: string) { + this.stack = [path]; + } + toString(): string { + return `invalid transitive import for cache:\n ${this.stack.join('\n ')}`; + } + readonly stack: string[]; +} /** * SourceHasher is a utility for producing a hash of a source .ts file and its imported source files. */ @@ -141,8 +155,19 @@ class SourceHasher { const normalized = content.replace('\r\n', '\n'); let hash = crc32(normalized); for (const importPath of parseImports(path, normalized)) { - const importHash = this.hashFile(importPath); - hash = this.hashCombine(hash, importHash); + try { + const importHash = this.hashFile(importPath); + hash = this.hashCombine(hash, importHash); + } catch (ex) { + if (ex instanceof InvalidImportException) { + ex.stack.push(path); + throw ex; + } + } + } + + if (content.includes('MUST_NOT_BE_IMPORTED_BY_DATA_CACHE')) { + throw new InvalidImportException(path); } this.hashes.set(path, hash); @@ -180,7 +205,7 @@ async function build(suiteDir: string) { } // Crawl files and convert paths to be POSIX-style, relative to suiteDir. - const filesToEnumerate = glob(suiteDir, specFileSuffix) + const filesToEnumerate = glob(suiteDir, cacheFileSuffix) .map(p => `${suiteDir}/${p}`) .sort(); @@ -221,7 +246,7 @@ and } console.log(`building '${outPath}'`); const data = await cacheable.build(); - const serialized = await cacheable.serialize(data); + const serialized = cacheable.serialize(data); fs.mkdirSync(path.dirname(outPath), { recursive: true }); fs.writeFileSync(outPath, serialized, 'binary'); fileHashes[cacheable.path] = fileHash; diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index faf84a6bd0b9..171297b815c2 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,106 +1,106 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "8e47fc86", - "webgpu/shader/execution/binary/af_logical.bin": "348bc279", - "webgpu/shader/execution/binary/af_division.bin": "82029c55", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "6deae35e", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "221a913c", - "webgpu/shader/execution/binary/af_multiplication.bin": "53c87c5b", - "webgpu/shader/execution/binary/af_remainder.bin": "dadb3ceb", - "webgpu/shader/execution/binary/af_subtraction.bin": "a0a71e32", - "webgpu/shader/execution/binary/f16_addition.bin": "acfee8", - "webgpu/shader/execution/binary/f16_logical.bin": "78e0eec8", - "webgpu/shader/execution/binary/f16_division.bin": "289b2aba", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "bada3cbe", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "bce23630", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "2d6bcf91", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "c3db5537", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "402931c", - "webgpu/shader/execution/binary/f16_multiplication.bin": "ce381066", - "webgpu/shader/execution/binary/f16_remainder.bin": "b2bd9246", - "webgpu/shader/execution/binary/f16_subtraction.bin": "7f7e9abc", - "webgpu/shader/execution/binary/f32_addition.bin": "71302f1a", - "webgpu/shader/execution/binary/f32_logical.bin": "ad34883d", - "webgpu/shader/execution/binary/f32_division.bin": "b35602bb", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "82339c4", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "33c2bd83", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "6b6dd016", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "7733299f", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "d99b4cc3", - "webgpu/shader/execution/binary/f32_multiplication.bin": "ff22107c", - "webgpu/shader/execution/binary/f32_remainder.bin": "44133c4c", - "webgpu/shader/execution/binary/f32_subtraction.bin": "a9a5a319", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "eb21bd8f", - "webgpu/shader/execution/binary/i32_comparison.bin": "83121ec4", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "8d1429d1", - "webgpu/shader/execution/binary/u32_comparison.bin": "3491ba55", - "webgpu/shader/execution/abs.bin": "a5dcabd0", - "webgpu/shader/execution/acos.bin": "adbccc79", - "webgpu/shader/execution/acosh.bin": "7c07ba41", - "webgpu/shader/execution/asin.bin": "8fc62434", - "webgpu/shader/execution/asinh.bin": "a00937c0", - "webgpu/shader/execution/atan.bin": "a237eaa6", - "webgpu/shader/execution/atan2.bin": "b41d74be", - "webgpu/shader/execution/atanh.bin": "2b6934d5", - "webgpu/shader/execution/bitcast.bin": "86e79d18", - "webgpu/shader/execution/ceil.bin": "42a99291", - "webgpu/shader/execution/clamp.bin": "b3fc754f", - "webgpu/shader/execution/cos.bin": "13305519", - "webgpu/shader/execution/cosh.bin": "20965093", - "webgpu/shader/execution/cross.bin": "3cfeb2a1", - "webgpu/shader/execution/degrees.bin": "6d9b3197", - "webgpu/shader/execution/determinant.bin": "d09e35b7", - "webgpu/shader/execution/distance.bin": "790d45ea", - "webgpu/shader/execution/dot.bin": "5f7aa2bc", - "webgpu/shader/execution/exp.bin": "932473", - "webgpu/shader/execution/exp2.bin": "1ada59d6", - "webgpu/shader/execution/faceForward.bin": "dc44d720", - "webgpu/shader/execution/floor.bin": "cbbe2d21", - "webgpu/shader/execution/fma.bin": "b5f22cac", - "webgpu/shader/execution/fract.bin": "b9b72ee2", - "webgpu/shader/execution/frexp.bin": "cc49a118", - "webgpu/shader/execution/inverseSqrt.bin": "ab8d39ed", - "webgpu/shader/execution/ldexp.bin": "cca23df2", - "webgpu/shader/execution/length.bin": "4693ba28", - "webgpu/shader/execution/log.bin": "75d7c534", - "webgpu/shader/execution/log2.bin": "c0c65cb0", - "webgpu/shader/execution/max.bin": "11d13c22", - "webgpu/shader/execution/min.bin": "5c701467", - "webgpu/shader/execution/mix.bin": "af998787", - "webgpu/shader/execution/modf.bin": "3a3b367a", - "webgpu/shader/execution/normalize.bin": "ce31e122", - "webgpu/shader/execution/pack2x16float.bin": "d3198def", - "webgpu/shader/execution/pow.bin": "847bf3ff", - "webgpu/shader/execution/quantizeToF16.bin": "e2f5c3f9", - "webgpu/shader/execution/radians.bin": "8869a385", - "webgpu/shader/execution/reflect.bin": "2be62e10", - "webgpu/shader/execution/refract.bin": "a95cf2a", - "webgpu/shader/execution/round.bin": "cdc5ae01", - "webgpu/shader/execution/saturate.bin": "bb20b07c", - "webgpu/shader/execution/sign.bin": "a59f6a2b", - "webgpu/shader/execution/sin.bin": "b36c7552", - "webgpu/shader/execution/sinh.bin": "9385405", - "webgpu/shader/execution/smoothstep.bin": "5d740ae7", - "webgpu/shader/execution/sqrt.bin": "5e42a89b", - "webgpu/shader/execution/step.bin": "4447b38c", - "webgpu/shader/execution/tan.bin": "b47dfc33", - "webgpu/shader/execution/tanh.bin": "8bd0ae74", - "webgpu/shader/execution/transpose.bin": "afbbaef0", - "webgpu/shader/execution/trunc.bin": "4d15f320", - "webgpu/shader/execution/unpack2x16float.bin": "40ca16cc", - "webgpu/shader/execution/unpack2x16snorm.bin": "39ece93e", - "webgpu/shader/execution/unpack2x16unorm.bin": "dbdc06b8", - "webgpu/shader/execution/unpack4x8snorm.bin": "c83bc6c2", - "webgpu/shader/execution/unpack4x8unorm.bin": "678c5245", - "webgpu/shader/execution/unary/af_arithmetic.bin": "1679466", - "webgpu/shader/execution/unary/af_assignment.bin": "202b8019", - "webgpu/shader/execution/unary/bool_conversion.bin": "6cb7e11a", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "d0409431", - "webgpu/shader/execution/unary/f16_conversion.bin": "884688a", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "8925bac7", - "webgpu/shader/execution/unary/f32_conversion.bin": "798aaba2", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "c21ece3", - "webgpu/shader/execution/unary/i32_complement.bin": "f65f0bec", - "webgpu/shader/execution/unary/i32_conversion.bin": "7ce468a1", - "webgpu/shader/execution/unary/u32_complement.bin": "d6408865", - "webgpu/shader/execution/unary/u32_conversion.bin": "aa09fe0" + "webgpu/shader/execution/binary/af_addition.bin": "b5345cca", + "webgpu/shader/execution/binary/af_logical.bin": "9ec85311", + "webgpu/shader/execution/binary/af_division.bin": "23dd840e", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "ee95539e", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "a2e64d57", + "webgpu/shader/execution/binary/af_multiplication.bin": "94d7a574", + "webgpu/shader/execution/binary/af_remainder.bin": "79f5abc4", + "webgpu/shader/execution/binary/af_subtraction.bin": "62dce5bf", + "webgpu/shader/execution/binary/f16_addition.bin": "ab3b4d9c", + "webgpu/shader/execution/binary/f16_logical.bin": "1edd08ec", + "webgpu/shader/execution/binary/f16_division.bin": "849eed26", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "54afc3f8", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "df5d3ccc", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "3be6032e", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "98a29837", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "c463d7e0", + "webgpu/shader/execution/binary/f16_multiplication.bin": "cdf0775d", + "webgpu/shader/execution/binary/f16_remainder.bin": "e82ef89f", + "webgpu/shader/execution/binary/f16_subtraction.bin": "5125b5b0", + "webgpu/shader/execution/binary/f32_addition.bin": "d3b8004", + "webgpu/shader/execution/binary/f32_logical.bin": "fab7cfc5", + "webgpu/shader/execution/binary/f32_division.bin": "e9fdb0a9", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "d1165469", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "45a79521", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "bf8da7d0", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "82d7fa9", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "57eebe84", + "webgpu/shader/execution/binary/f32_multiplication.bin": "eb0baa56", + "webgpu/shader/execution/binary/f32_remainder.bin": "e6059990", + "webgpu/shader/execution/binary/f32_subtraction.bin": "d57cfa1", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "de93ee2a", + "webgpu/shader/execution/binary/i32_comparison.bin": "aaa1f21b", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "d79a1011", + "webgpu/shader/execution/binary/u32_comparison.bin": "31764c75", + "webgpu/shader/execution/abs.bin": "9278901c", + "webgpu/shader/execution/acos.bin": "9c77ebf9", + "webgpu/shader/execution/acosh.bin": "2baaf7cc", + "webgpu/shader/execution/asin.bin": "8fd7a678", + "webgpu/shader/execution/asinh.bin": "321a7294", + "webgpu/shader/execution/atan.bin": "d3dada7e", + "webgpu/shader/execution/atan2.bin": "2871596c", + "webgpu/shader/execution/atanh.bin": "e7158ef", + "webgpu/shader/execution/bitcast.bin": "aa7d6f0e", + "webgpu/shader/execution/ceil.bin": "3c1d91ad", + "webgpu/shader/execution/clamp.bin": "a53fdbda", + "webgpu/shader/execution/cos.bin": "73b83288", + "webgpu/shader/execution/cosh.bin": "39ca32a4", + "webgpu/shader/execution/cross.bin": "409c3e26", + "webgpu/shader/execution/degrees.bin": "a44bb2b1", + "webgpu/shader/execution/determinant.bin": "41a06528", + "webgpu/shader/execution/distance.bin": "fe3ea84f", + "webgpu/shader/execution/dot.bin": "f70abfec", + "webgpu/shader/execution/exp.bin": "7587e11b", + "webgpu/shader/execution/exp2.bin": "c6f34d2b", + "webgpu/shader/execution/faceForward.bin": "8eb57a7d", + "webgpu/shader/execution/floor.bin": "b9ea647b", + "webgpu/shader/execution/fma.bin": "b9f657b2", + "webgpu/shader/execution/fract.bin": "73a18e4f", + "webgpu/shader/execution/frexp.bin": "857fe9b7", + "webgpu/shader/execution/inverseSqrt.bin": "a595983a", + "webgpu/shader/execution/ldexp.bin": "a04aaeca", + "webgpu/shader/execution/length.bin": "695740fb", + "webgpu/shader/execution/log.bin": "6703ec1a", + "webgpu/shader/execution/log2.bin": "8339559d", + "webgpu/shader/execution/max.bin": "ec000a56", + "webgpu/shader/execution/min.bin": "156cf7cc", + "webgpu/shader/execution/mix.bin": "32064d21", + "webgpu/shader/execution/modf.bin": "a5003ce0", + "webgpu/shader/execution/normalize.bin": "c12bac96", + "webgpu/shader/execution/pack2x16float.bin": "b2cb12ea", + "webgpu/shader/execution/pow.bin": "ee87eccb", + "webgpu/shader/execution/quantizeToF16.bin": "f77ae7e3", + "webgpu/shader/execution/radians.bin": "7ecbe5be", + "webgpu/shader/execution/reflect.bin": "d37034bd", + "webgpu/shader/execution/refract.bin": "6dc9adcf", + "webgpu/shader/execution/round.bin": "d91faa0f", + "webgpu/shader/execution/saturate.bin": "93230980", + "webgpu/shader/execution/sign.bin": "fb1071b8", + "webgpu/shader/execution/sin.bin": "a9ed8361", + "webgpu/shader/execution/sinh.bin": "65ba80fc", + "webgpu/shader/execution/smoothstep.bin": "17e1e103", + "webgpu/shader/execution/sqrt.bin": "4c29a5d4", + "webgpu/shader/execution/step.bin": "aed08458", + "webgpu/shader/execution/tan.bin": "5f671594", + "webgpu/shader/execution/tanh.bin": "991e903a", + "webgpu/shader/execution/transpose.bin": "9d388797", + "webgpu/shader/execution/trunc.bin": "959fe8bc", + "webgpu/shader/execution/unpack2x16float.bin": "26e5b05e", + "webgpu/shader/execution/unpack2x16snorm.bin": "c756d8a3", + "webgpu/shader/execution/unpack2x16unorm.bin": "5257591a", + "webgpu/shader/execution/unpack4x8snorm.bin": "83fbd41a", + "webgpu/shader/execution/unpack4x8unorm.bin": "77d46acb", + "webgpu/shader/execution/unary/af_arithmetic.bin": "2edb2dc5", + "webgpu/shader/execution/unary/af_assignment.bin": "36c04bba", + "webgpu/shader/execution/unary/bool_conversion.bin": "cb53bf65", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "9f459fc4", + "webgpu/shader/execution/unary/f16_conversion.bin": "bf055ca9", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "e15c49e7", + "webgpu/shader/execution/unary/f32_conversion.bin": "92e5069f", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "d322b73d", + "webgpu/shader/execution/unary/i32_complement.bin": "c4e6cbb", + "webgpu/shader/execution/unary/i32_conversion.bin": "d6905a0f", + "webgpu/shader/execution/unary/u32_complement.bin": "d0e4aa97", + "webgpu/shader/execution/unary/u32_conversion.bin": "5a96b263" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/abs.bin b/src/resources/cache/webgpu/shader/execution/abs.bin index 6223beff8744..16111ac66a32 100644 Binary files a/src/resources/cache/webgpu/shader/execution/abs.bin and b/src/resources/cache/webgpu/shader/execution/abs.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/acos.bin b/src/resources/cache/webgpu/shader/execution/acos.bin index 1977729e5c60..3765fcd2bef8 100644 Binary files a/src/resources/cache/webgpu/shader/execution/acos.bin and b/src/resources/cache/webgpu/shader/execution/acos.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/acosh.bin b/src/resources/cache/webgpu/shader/execution/acosh.bin index 4f725dcaf127..ee127796edb4 100644 Binary files a/src/resources/cache/webgpu/shader/execution/acosh.bin and b/src/resources/cache/webgpu/shader/execution/acosh.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/asin.bin b/src/resources/cache/webgpu/shader/execution/asin.bin index ad64140d1e7f..439de48fb3f1 100644 Binary files a/src/resources/cache/webgpu/shader/execution/asin.bin and b/src/resources/cache/webgpu/shader/execution/asin.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/asinh.bin b/src/resources/cache/webgpu/shader/execution/asinh.bin index 45ad2368d194..eda7179e7597 100644 Binary files a/src/resources/cache/webgpu/shader/execution/asinh.bin and b/src/resources/cache/webgpu/shader/execution/asinh.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/atan.bin b/src/resources/cache/webgpu/shader/execution/atan.bin index f36c10fd9f6c..d15ee8b6b7f2 100644 Binary files a/src/resources/cache/webgpu/shader/execution/atan.bin and b/src/resources/cache/webgpu/shader/execution/atan.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/atan2.bin b/src/resources/cache/webgpu/shader/execution/atan2.bin index 723303abdfb5..2966b4ea155c 100644 Binary files a/src/resources/cache/webgpu/shader/execution/atan2.bin and b/src/resources/cache/webgpu/shader/execution/atan2.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/atanh.bin b/src/resources/cache/webgpu/shader/execution/atanh.bin index 471fec788360..d919b4bcb782 100644 Binary files a/src/resources/cache/webgpu/shader/execution/atanh.bin and b/src/resources/cache/webgpu/shader/execution/atanh.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_addition.bin b/src/resources/cache/webgpu/shader/execution/binary/af_addition.bin index 349208085f7f..ebd757c1b6b1 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/af_addition.bin and b/src/resources/cache/webgpu/shader/execution/binary/af_addition.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_division.bin b/src/resources/cache/webgpu/shader/execution/binary/af_division.bin index e40a7de36c5f..af690dcfb53f 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/af_division.bin and b/src/resources/cache/webgpu/shader/execution/binary/af_division.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_logical.bin b/src/resources/cache/webgpu/shader/execution/binary/af_logical.bin index 4ef63b4a7e77..e386de36b7e8 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/af_logical.bin and b/src/resources/cache/webgpu/shader/execution/binary/af_logical.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_matrix_addition.bin b/src/resources/cache/webgpu/shader/execution/binary/af_matrix_addition.bin index a64f636c3808..d387abcd7f20 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/af_matrix_addition.bin and b/src/resources/cache/webgpu/shader/execution/binary/af_matrix_addition.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_matrix_subtraction.bin b/src/resources/cache/webgpu/shader/execution/binary/af_matrix_subtraction.bin index ca0574b737e9..4a1449182109 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/af_matrix_subtraction.bin and b/src/resources/cache/webgpu/shader/execution/binary/af_matrix_subtraction.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/af_multiplication.bin index 2c8ab384831e..552d8b4892c8 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/af_multiplication.bin and b/src/resources/cache/webgpu/shader/execution/binary/af_multiplication.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_remainder.bin b/src/resources/cache/webgpu/shader/execution/binary/af_remainder.bin index a9b404623e38..b9cd253ac7ca 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/af_remainder.bin and b/src/resources/cache/webgpu/shader/execution/binary/af_remainder.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_subtraction.bin b/src/resources/cache/webgpu/shader/execution/binary/af_subtraction.bin index e38720094465..6f0be2978587 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/af_subtraction.bin and b/src/resources/cache/webgpu/shader/execution/binary/af_subtraction.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_addition.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_addition.bin index efc7d2aeed51..30f099139dd4 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_addition.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_addition.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_division.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_division.bin index fe0bae88cc4a..c05b627fa121 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_division.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_division.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_logical.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_logical.bin index f9449065b6c0..558a25ded450 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_logical.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_logical.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_addition.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_addition.bin index 71834238de53..452376760bd3 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_addition.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_addition.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin index 09b86396c9da..6d5f4e1ea1fd 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin index e29b38f5f486..b48be81ebd2a 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_subtraction.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_subtraction.bin index 591a12e3c067..386558c3f768 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_subtraction.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_subtraction.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin index 0b6fa6f8aa8d..cbf224a6b638 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_multiplication.bin index d304084d0972..e9d27019a3b4 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_multiplication.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_multiplication.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_remainder.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_remainder.bin index 870492b379a4..d21370aec98c 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_remainder.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_remainder.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f16_subtraction.bin b/src/resources/cache/webgpu/shader/execution/binary/f16_subtraction.bin index b3d83ab375ce..97080a8ce547 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f16_subtraction.bin and b/src/resources/cache/webgpu/shader/execution/binary/f16_subtraction.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_addition.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_addition.bin index ccc443387641..be7b997bd8eb 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_addition.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_addition.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_division.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_division.bin index cad4b6d5ca82..f80461c21b48 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_division.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_division.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_logical.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_logical.bin index d9b5e494a046..5a6fbe92d5d2 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_logical.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_logical.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_addition.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_addition.bin index e0ca837fffde..b1d7179264c6 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_addition.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_addition.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin index 5cee2264ebfe..9c36c9e8b5c2 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin index b6d84399d89a..76f867b959ec 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_subtraction.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_subtraction.bin index 2faffe9327db..0d0fd2460d72 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_subtraction.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_subtraction.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin index 9ca7f6bbddc0..e139fc971300 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_multiplication.bin index 43ef17cdf19b..1837ce922c56 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_multiplication.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_multiplication.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_remainder.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_remainder.bin index 477bf6cc3fa5..3febfca1d1cb 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_remainder.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_remainder.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/f32_subtraction.bin b/src/resources/cache/webgpu/shader/execution/binary/f32_subtraction.bin index db5975e48288..32b34f690cc6 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/f32_subtraction.bin and b/src/resources/cache/webgpu/shader/execution/binary/f32_subtraction.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/i32_arithmetic.bin b/src/resources/cache/webgpu/shader/execution/binary/i32_arithmetic.bin index a2fcd52c5bd2..55880fdeeded 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/i32_arithmetic.bin and b/src/resources/cache/webgpu/shader/execution/binary/i32_arithmetic.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/i32_comparison.bin b/src/resources/cache/webgpu/shader/execution/binary/i32_comparison.bin index 2acf5c4e7f97..4bc8882dea92 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/i32_comparison.bin and b/src/resources/cache/webgpu/shader/execution/binary/i32_comparison.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/u32_arithmetic.bin b/src/resources/cache/webgpu/shader/execution/binary/u32_arithmetic.bin index a016e61d5eec..56ef2928645a 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/u32_arithmetic.bin and b/src/resources/cache/webgpu/shader/execution/binary/u32_arithmetic.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/binary/u32_comparison.bin b/src/resources/cache/webgpu/shader/execution/binary/u32_comparison.bin index 28c0ca1b9552..5ba639b3cdad 100644 Binary files a/src/resources/cache/webgpu/shader/execution/binary/u32_comparison.bin and b/src/resources/cache/webgpu/shader/execution/binary/u32_comparison.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/bitcast.bin b/src/resources/cache/webgpu/shader/execution/bitcast.bin index 0eb0dcf77736..808ddd88bd19 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/ceil.bin b/src/resources/cache/webgpu/shader/execution/ceil.bin index 5a726de3168a..5ec60b6e150d 100644 Binary files a/src/resources/cache/webgpu/shader/execution/ceil.bin and b/src/resources/cache/webgpu/shader/execution/ceil.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/clamp.bin b/src/resources/cache/webgpu/shader/execution/clamp.bin index aef9dc89c97c..1ceb3f19a10d 100644 Binary files a/src/resources/cache/webgpu/shader/execution/clamp.bin and b/src/resources/cache/webgpu/shader/execution/clamp.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/cos.bin b/src/resources/cache/webgpu/shader/execution/cos.bin index a90a7b369eb1..a7951b447025 100644 Binary files a/src/resources/cache/webgpu/shader/execution/cos.bin and b/src/resources/cache/webgpu/shader/execution/cos.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/cosh.bin b/src/resources/cache/webgpu/shader/execution/cosh.bin index 08c7ebafdeee..693b2d3230e7 100644 Binary files a/src/resources/cache/webgpu/shader/execution/cosh.bin and b/src/resources/cache/webgpu/shader/execution/cosh.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/cross.bin b/src/resources/cache/webgpu/shader/execution/cross.bin index 0a2ee02fa153..64041c731193 100644 Binary files a/src/resources/cache/webgpu/shader/execution/cross.bin and b/src/resources/cache/webgpu/shader/execution/cross.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/degrees.bin b/src/resources/cache/webgpu/shader/execution/degrees.bin index fc44af06f59b..715e6b656f74 100644 Binary files a/src/resources/cache/webgpu/shader/execution/degrees.bin and b/src/resources/cache/webgpu/shader/execution/degrees.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/determinant.bin b/src/resources/cache/webgpu/shader/execution/determinant.bin index b7518c304ffd..cef09cd67bd1 100644 Binary files a/src/resources/cache/webgpu/shader/execution/determinant.bin and b/src/resources/cache/webgpu/shader/execution/determinant.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/distance.bin b/src/resources/cache/webgpu/shader/execution/distance.bin index 27ece1a02778..68e892671e1f 100644 Binary files a/src/resources/cache/webgpu/shader/execution/distance.bin and b/src/resources/cache/webgpu/shader/execution/distance.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/dot.bin b/src/resources/cache/webgpu/shader/execution/dot.bin index 7a6977a83a24..9c243ea4f004 100644 Binary files a/src/resources/cache/webgpu/shader/execution/dot.bin and b/src/resources/cache/webgpu/shader/execution/dot.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/exp.bin b/src/resources/cache/webgpu/shader/execution/exp.bin index 9b92a23ea968..0c4ed5bfea0a 100644 Binary files a/src/resources/cache/webgpu/shader/execution/exp.bin and b/src/resources/cache/webgpu/shader/execution/exp.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/exp2.bin b/src/resources/cache/webgpu/shader/execution/exp2.bin index 0ca78a214eb2..7ffbb062f1b2 100644 Binary files a/src/resources/cache/webgpu/shader/execution/exp2.bin and b/src/resources/cache/webgpu/shader/execution/exp2.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/faceForward.bin b/src/resources/cache/webgpu/shader/execution/faceForward.bin index d3bca445284d..13436dde01b1 100644 Binary files a/src/resources/cache/webgpu/shader/execution/faceForward.bin and b/src/resources/cache/webgpu/shader/execution/faceForward.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/floor.bin b/src/resources/cache/webgpu/shader/execution/floor.bin index fc9ca8cf331d..a4b363825bea 100644 Binary files a/src/resources/cache/webgpu/shader/execution/floor.bin and b/src/resources/cache/webgpu/shader/execution/floor.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/fma.bin b/src/resources/cache/webgpu/shader/execution/fma.bin index 751d8f75703e..db54353a5262 100644 Binary files a/src/resources/cache/webgpu/shader/execution/fma.bin and b/src/resources/cache/webgpu/shader/execution/fma.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/fract.bin b/src/resources/cache/webgpu/shader/execution/fract.bin index aa11d27b2885..566b95144f3c 100644 Binary files a/src/resources/cache/webgpu/shader/execution/fract.bin and b/src/resources/cache/webgpu/shader/execution/fract.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/frexp.bin b/src/resources/cache/webgpu/shader/execution/frexp.bin index 5b534b1520df..2e20d4ce033d 100644 Binary files a/src/resources/cache/webgpu/shader/execution/frexp.bin and b/src/resources/cache/webgpu/shader/execution/frexp.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/inverseSqrt.bin b/src/resources/cache/webgpu/shader/execution/inverseSqrt.bin index cbfa62107600..731c62903cc2 100644 Binary files a/src/resources/cache/webgpu/shader/execution/inverseSqrt.bin and b/src/resources/cache/webgpu/shader/execution/inverseSqrt.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/ldexp.bin b/src/resources/cache/webgpu/shader/execution/ldexp.bin index 4c94aa669a7e..d441928d1cb7 100644 Binary files a/src/resources/cache/webgpu/shader/execution/ldexp.bin and b/src/resources/cache/webgpu/shader/execution/ldexp.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/length.bin b/src/resources/cache/webgpu/shader/execution/length.bin index c9ef2fe9b5de..9ecedd7d997f 100644 Binary files a/src/resources/cache/webgpu/shader/execution/length.bin and b/src/resources/cache/webgpu/shader/execution/length.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/log.bin b/src/resources/cache/webgpu/shader/execution/log.bin index 981182412d3b..5c860bfaa65c 100644 Binary files a/src/resources/cache/webgpu/shader/execution/log.bin and b/src/resources/cache/webgpu/shader/execution/log.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/log2.bin b/src/resources/cache/webgpu/shader/execution/log2.bin index 1d7563a88fbf..7f6d35346b33 100644 Binary files a/src/resources/cache/webgpu/shader/execution/log2.bin and b/src/resources/cache/webgpu/shader/execution/log2.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/max.bin b/src/resources/cache/webgpu/shader/execution/max.bin index 3330d3e34112..63704eb23300 100644 Binary files a/src/resources/cache/webgpu/shader/execution/max.bin and b/src/resources/cache/webgpu/shader/execution/max.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/min.bin b/src/resources/cache/webgpu/shader/execution/min.bin index f90e78691eb8..18b26a3bf8c8 100644 Binary files a/src/resources/cache/webgpu/shader/execution/min.bin and b/src/resources/cache/webgpu/shader/execution/min.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/mix.bin b/src/resources/cache/webgpu/shader/execution/mix.bin index faaf0df313eb..4cf9083aed69 100644 Binary files a/src/resources/cache/webgpu/shader/execution/mix.bin and b/src/resources/cache/webgpu/shader/execution/mix.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/modf.bin b/src/resources/cache/webgpu/shader/execution/modf.bin index fa1483bc7aa0..e5387db46257 100644 Binary files a/src/resources/cache/webgpu/shader/execution/modf.bin and b/src/resources/cache/webgpu/shader/execution/modf.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/normalize.bin b/src/resources/cache/webgpu/shader/execution/normalize.bin index 524639326060..434bbe2dd167 100644 Binary files a/src/resources/cache/webgpu/shader/execution/normalize.bin and b/src/resources/cache/webgpu/shader/execution/normalize.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/pack2x16float.bin b/src/resources/cache/webgpu/shader/execution/pack2x16float.bin index db6b5f242a53..e95227d36e50 100644 Binary files a/src/resources/cache/webgpu/shader/execution/pack2x16float.bin and b/src/resources/cache/webgpu/shader/execution/pack2x16float.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/pow.bin b/src/resources/cache/webgpu/shader/execution/pow.bin index 688f18cad37b..1f23a1f3818a 100644 Binary files a/src/resources/cache/webgpu/shader/execution/pow.bin and b/src/resources/cache/webgpu/shader/execution/pow.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/quantizeToF16.bin b/src/resources/cache/webgpu/shader/execution/quantizeToF16.bin index b18ec2b667e9..9e4308d5cd30 100644 Binary files a/src/resources/cache/webgpu/shader/execution/quantizeToF16.bin and b/src/resources/cache/webgpu/shader/execution/quantizeToF16.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/radians.bin b/src/resources/cache/webgpu/shader/execution/radians.bin index bdf713d1b2bc..63c7cc3ccd5b 100644 Binary files a/src/resources/cache/webgpu/shader/execution/radians.bin and b/src/resources/cache/webgpu/shader/execution/radians.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/reflect.bin b/src/resources/cache/webgpu/shader/execution/reflect.bin index c4f066e11332..6608646fd01e 100644 Binary files a/src/resources/cache/webgpu/shader/execution/reflect.bin and b/src/resources/cache/webgpu/shader/execution/reflect.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/refract.bin b/src/resources/cache/webgpu/shader/execution/refract.bin index ec22e1d6a728..934e44e644bc 100644 Binary files a/src/resources/cache/webgpu/shader/execution/refract.bin and b/src/resources/cache/webgpu/shader/execution/refract.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/round.bin b/src/resources/cache/webgpu/shader/execution/round.bin index cdbdfef8e259..c3b30b68f0a1 100644 Binary files a/src/resources/cache/webgpu/shader/execution/round.bin and b/src/resources/cache/webgpu/shader/execution/round.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/saturate.bin b/src/resources/cache/webgpu/shader/execution/saturate.bin index 676d4878a6f6..39df7169b750 100644 Binary files a/src/resources/cache/webgpu/shader/execution/saturate.bin and b/src/resources/cache/webgpu/shader/execution/saturate.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/sign.bin b/src/resources/cache/webgpu/shader/execution/sign.bin index cddd0c88bac0..2f3b2aa258b2 100644 Binary files a/src/resources/cache/webgpu/shader/execution/sign.bin and b/src/resources/cache/webgpu/shader/execution/sign.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/sin.bin b/src/resources/cache/webgpu/shader/execution/sin.bin index 0b3a32bd67dc..d583876c518c 100644 Binary files a/src/resources/cache/webgpu/shader/execution/sin.bin and b/src/resources/cache/webgpu/shader/execution/sin.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/sinh.bin b/src/resources/cache/webgpu/shader/execution/sinh.bin index 9cd9463a1de9..072c53393f46 100644 Binary files a/src/resources/cache/webgpu/shader/execution/sinh.bin and b/src/resources/cache/webgpu/shader/execution/sinh.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/smoothstep.bin b/src/resources/cache/webgpu/shader/execution/smoothstep.bin index 07a8cff9159a..43aac2b1c46d 100644 Binary files a/src/resources/cache/webgpu/shader/execution/smoothstep.bin and b/src/resources/cache/webgpu/shader/execution/smoothstep.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/sqrt.bin b/src/resources/cache/webgpu/shader/execution/sqrt.bin index 8068f869a6b3..f466d2379f65 100644 Binary files a/src/resources/cache/webgpu/shader/execution/sqrt.bin and b/src/resources/cache/webgpu/shader/execution/sqrt.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/step.bin b/src/resources/cache/webgpu/shader/execution/step.bin index 4bd371f9d8e6..03f251cb65cf 100644 Binary files a/src/resources/cache/webgpu/shader/execution/step.bin and b/src/resources/cache/webgpu/shader/execution/step.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/tan.bin b/src/resources/cache/webgpu/shader/execution/tan.bin index 21ce4e7cb44f..aafb900347d0 100644 Binary files a/src/resources/cache/webgpu/shader/execution/tan.bin and b/src/resources/cache/webgpu/shader/execution/tan.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/tanh.bin b/src/resources/cache/webgpu/shader/execution/tanh.bin index aa15a9409fab..709427217c26 100644 Binary files a/src/resources/cache/webgpu/shader/execution/tanh.bin and b/src/resources/cache/webgpu/shader/execution/tanh.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/transpose.bin b/src/resources/cache/webgpu/shader/execution/transpose.bin index 58acdd9e802d..d1b6bf04ee7e 100644 Binary files a/src/resources/cache/webgpu/shader/execution/transpose.bin and b/src/resources/cache/webgpu/shader/execution/transpose.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/trunc.bin b/src/resources/cache/webgpu/shader/execution/trunc.bin index 065e118daeba..ba81e2ada427 100644 Binary files a/src/resources/cache/webgpu/shader/execution/trunc.bin and b/src/resources/cache/webgpu/shader/execution/trunc.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/af_arithmetic.bin b/src/resources/cache/webgpu/shader/execution/unary/af_arithmetic.bin index 8d79aa10e475..21d3d702ae9f 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/af_arithmetic.bin and b/src/resources/cache/webgpu/shader/execution/unary/af_arithmetic.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/af_assignment.bin b/src/resources/cache/webgpu/shader/execution/unary/af_assignment.bin index 852066cf67a6..a92279b5ce4e 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/af_assignment.bin and b/src/resources/cache/webgpu/shader/execution/unary/af_assignment.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/bool_conversion.bin b/src/resources/cache/webgpu/shader/execution/unary/bool_conversion.bin index 5b4d9ebdcaea..98a90ea45b9a 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/bool_conversion.bin and b/src/resources/cache/webgpu/shader/execution/unary/bool_conversion.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/f16_arithmetic.bin b/src/resources/cache/webgpu/shader/execution/unary/f16_arithmetic.bin index 989a623f40c7..acf8a702cee1 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/f16_arithmetic.bin and b/src/resources/cache/webgpu/shader/execution/unary/f16_arithmetic.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/f16_conversion.bin b/src/resources/cache/webgpu/shader/execution/unary/f16_conversion.bin index 764769fc6217..5b386b070434 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/f16_conversion.bin and b/src/resources/cache/webgpu/shader/execution/unary/f16_conversion.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/f32_arithmetic.bin b/src/resources/cache/webgpu/shader/execution/unary/f32_arithmetic.bin index 83c566986107..ebc60029fa60 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/f32_arithmetic.bin and b/src/resources/cache/webgpu/shader/execution/unary/f32_arithmetic.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/f32_conversion.bin b/src/resources/cache/webgpu/shader/execution/unary/f32_conversion.bin index c71bc25a3ada..bdcc0c72988e 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/f32_conversion.bin and b/src/resources/cache/webgpu/shader/execution/unary/f32_conversion.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/i32_arithmetic.bin b/src/resources/cache/webgpu/shader/execution/unary/i32_arithmetic.bin index c3a962cb3646..4753b020c965 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/i32_arithmetic.bin and b/src/resources/cache/webgpu/shader/execution/unary/i32_arithmetic.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/i32_complement.bin b/src/resources/cache/webgpu/shader/execution/unary/i32_complement.bin index 24eff7d07681..f7b8bd680c37 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/i32_complement.bin and b/src/resources/cache/webgpu/shader/execution/unary/i32_complement.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/i32_conversion.bin b/src/resources/cache/webgpu/shader/execution/unary/i32_conversion.bin index 9f6e54ce3586..74c5d000e3f6 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/i32_conversion.bin and b/src/resources/cache/webgpu/shader/execution/unary/i32_conversion.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/u32_complement.bin b/src/resources/cache/webgpu/shader/execution/unary/u32_complement.bin index 5978b45b590f..526456273e48 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/u32_complement.bin and b/src/resources/cache/webgpu/shader/execution/unary/u32_complement.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unary/u32_conversion.bin b/src/resources/cache/webgpu/shader/execution/unary/u32_conversion.bin index bda5494e1355..609caf94fe68 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unary/u32_conversion.bin and b/src/resources/cache/webgpu/shader/execution/unary/u32_conversion.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unpack2x16float.bin b/src/resources/cache/webgpu/shader/execution/unpack2x16float.bin index e3eec4572642..7f06cb0df6b3 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unpack2x16float.bin and b/src/resources/cache/webgpu/shader/execution/unpack2x16float.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unpack2x16snorm.bin b/src/resources/cache/webgpu/shader/execution/unpack2x16snorm.bin index acd39ca05b9d..08c6af9e9398 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unpack2x16snorm.bin and b/src/resources/cache/webgpu/shader/execution/unpack2x16snorm.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unpack2x16unorm.bin b/src/resources/cache/webgpu/shader/execution/unpack2x16unorm.bin index 90c62a32a19a..1bb97b9c556a 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unpack2x16unorm.bin and b/src/resources/cache/webgpu/shader/execution/unpack2x16unorm.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unpack4x8snorm.bin b/src/resources/cache/webgpu/shader/execution/unpack4x8snorm.bin index 04fb255842dd..1db9856b0562 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unpack4x8snorm.bin and b/src/resources/cache/webgpu/shader/execution/unpack4x8snorm.bin differ diff --git a/src/resources/cache/webgpu/shader/execution/unpack4x8unorm.bin b/src/resources/cache/webgpu/shader/execution/unpack4x8unorm.bin index 30c4a0c0ddc3..8d1f3dc7fb8f 100644 Binary files a/src/resources/cache/webgpu/shader/execution/unpack4x8unorm.bin and b/src/resources/cache/webgpu/shader/execution/unpack4x8unorm.bin differ diff --git a/src/webgpu/shader/execution/expression/binary/af_addition.cache.ts b/src/webgpu/shader/execution/expression/binary/af_addition.cache.ts new file mode 100644 index 000000000000..f5e9d3b481a5 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_addition.cache.ts @@ -0,0 +1,54 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const additionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.additionInterval(e, s))); +}; + +const additionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.additionInterval(s, e))); +}; + +const scalar_cases = { + ['scalar']: () => { + return FP.abstract.generateScalarPairToIntervalCases( + sparseScalarF64Range(), + sparseScalarF64Range(), + 'finite', + FP.abstract.additionInterval + ); + }, +}; + +const vector_scalar_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`vec${dim}_scalar`]: () => { + return FP.abstract.generateVectorScalarToVectorCases( + sparseVectorF64Range(dim), + sparseScalarF64Range(), + 'finite', + additionVectorScalarInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`scalar_vec${dim}`]: () => { + return FP.abstract.generateScalarVectorToVectorCases( + sparseScalarF64Range(), + sparseVectorF64Range(dim), + 'finite', + additionScalarVectorInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_addition', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts index 29fb42e386b0..b34fb4f150dd 100644 --- a/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts @@ -5,66 +5,13 @@ Execution Tests for non-matrix AbstractFloat addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { onlyConstInputSource, run } from '../expression.js'; +import { d } from './af_addition.cache.js'; import { abstractBinary } from './binary.js'; -const additionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.additionInterval(e, s))); -}; - -const additionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.additionInterval(s, e))); -}; - export const g = makeTestGroup(GPUTest); -const scalar_cases = { - ['scalar']: () => { - return FP.abstract.generateScalarPairToIntervalCases( - sparseScalarF64Range(), - sparseScalarF64Range(), - 'finite', - FP.abstract.additionInterval - ); - }, -}; - -const vector_scalar_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`vec${dim}_scalar`]: () => { - return FP.abstract.generateVectorScalarToVectorCases( - sparseVectorF64Range(dim), - sparseScalarF64Range(), - 'finite', - additionVectorScalarInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`scalar_vec${dim}`]: () => { - return FP.abstract.generateScalarVectorToVectorCases( - sparseScalarF64Range(), - sparseVectorF64Range(dim), - 'finite', - additionScalarVectorInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/af_addition', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts new file mode 100644 index 000000000000..648ea5c0b0b8 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts @@ -0,0 +1,90 @@ +import { anyOf } from '../../../../util/compare.js'; +import { abstractFloat, bool, Scalar } from '../../../../util/conversion.js'; +import { flushSubnormalNumberF64, vectorF64Range } from '../../../../util/math.js'; +import { Case } from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +/** + * @returns a test case for the provided left hand & right hand values and truth function. + * Handles quantization and subnormals. + */ +function makeCase( + lhs: number, + rhs: number, + truthFunc: (lhs: Scalar, rhs: Scalar) => boolean +): Case { + // Subnormal float values may be flushed at any time. + // https://www.w3.org/TR/WGSL/#floating-point-evaluation + const af_lhs = abstractFloat(lhs); + const af_rhs = abstractFloat(rhs); + const lhs_options = new Set([af_lhs, abstractFloat(flushSubnormalNumberF64(lhs))]); + const rhs_options = new Set([af_rhs, abstractFloat(flushSubnormalNumberF64(rhs))]); + const expected: Array = []; + lhs_options.forEach(l => { + rhs_options.forEach(r => { + const result = bool(truthFunc(l, r)); + if (!expected.includes(result)) { + expected.push(result); + } + }); + }); + + return { input: [af_lhs, af_rhs], expected: anyOf(...expected) }; +} + +export const d = makeCaseCache('binary/af_logical', { + equals: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) === (rhs.value as number); + }; + + return vectorF64Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + not_equals: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) !== (rhs.value as number); + }; + + return vectorF64Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_than: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) < (rhs.value as number); + }; + + return vectorF64Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_equals: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) <= (rhs.value as number); + }; + + return vectorF64Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_than: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) > (rhs.value as number); + }; + + return vectorF64Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_equals: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) >= (rhs.value as number); + }; + + return vectorF64Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts index 5b8b1637b9db..cc03ee4367f2 100644 --- a/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts @@ -4,107 +4,14 @@ Execution Tests for the AbstractFloat comparison operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { anyOf } from '../../../../util/compare.js'; -import { - abstractFloat, - bool, - Scalar, - TypeAbstractFloat, - TypeBool, -} from '../../../../util/conversion.js'; -import { flushSubnormalNumberF64, vectorF64Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, Case, run } from '../expression.js'; +import { TypeAbstractFloat, TypeBool } from '../../../../util/conversion.js'; +import { allInputSources, run } from '../expression.js'; +import { d } from './af_comparison.cache.js'; import { binary } from './binary.js'; export const g = makeTestGroup(GPUTest); -/** - * @returns a test case for the provided left hand & right hand values and truth function. - * Handles quantization and subnormals. - */ -function makeCase( - lhs: number, - rhs: number, - truthFunc: (lhs: Scalar, rhs: Scalar) => boolean -): Case { - // Subnormal float values may be flushed at any time. - // https://www.w3.org/TR/WGSL/#floating-point-evaluation - const af_lhs = abstractFloat(lhs); - const af_rhs = abstractFloat(rhs); - const lhs_options = new Set([af_lhs, abstractFloat(flushSubnormalNumberF64(lhs))]); - const rhs_options = new Set([af_rhs, abstractFloat(flushSubnormalNumberF64(rhs))]); - const expected: Array = []; - lhs_options.forEach(l => { - rhs_options.forEach(r => { - const result = bool(truthFunc(l, r)); - if (!expected.includes(result)) { - expected.push(result); - } - }); - }); - - return { input: [af_lhs, af_rhs], expected: anyOf(...expected) }; -} - -export const d = makeCaseCache('binary/af_logical', { - equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) === (rhs.value as number); - }; - - return vectorF64Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - not_equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) !== (rhs.value as number); - }; - - return vectorF64Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_than: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) < (rhs.value as number); - }; - - return vectorF64Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) <= (rhs.value as number); - }; - - return vectorF64Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_than: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) > (rhs.value as number); - }; - - return vectorF64Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) >= (rhs.value as number); - }; - - return vectorF64Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, -}); - g.test('equals') .specURL('https://www.w3.org/TR/WGSL/#comparison-expr') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/af_division.cache.ts b/src/webgpu/shader/execution/expression/binary/af_division.cache.ts new file mode 100644 index 000000000000..5af293b3de28 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_division.cache.ts @@ -0,0 +1,54 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const divisionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.divisionInterval(e, s))); +}; + +const divisionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.divisionInterval(s, e))); +}; + +const scalar_cases = { + ['scalar']: () => { + return FP.abstract.generateScalarPairToIntervalCases( + sparseScalarF64Range(), + sparseScalarF64Range(), + 'finite', + FP.abstract.divisionInterval + ); + }, +}; + +const vector_scalar_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`vec${dim}_scalar`]: () => { + return FP.abstract.generateVectorScalarToVectorCases( + sparseVectorF64Range(dim), + sparseScalarF64Range(), + 'finite', + divisionVectorScalarInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`scalar_vec${dim}`]: () => { + return FP.abstract.generateScalarVectorToVectorCases( + sparseScalarF64Range(), + sparseVectorF64Range(dim), + 'finite', + divisionScalarVectorInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_division', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/af_division.spec.ts b/src/webgpu/shader/execution/expression/binary/af_division.spec.ts index 6b2cd819f4ee..1e5492a34d9c 100644 --- a/src/webgpu/shader/execution/expression/binary/af_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_division.spec.ts @@ -5,66 +5,13 @@ Execution Tests for non-matrix AbstractFloat division expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { onlyConstInputSource, run } from '../expression.js'; +import { d } from './af_division.cache.js'; import { abstractBinary } from './binary.js'; -const divisionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.divisionInterval(e, s))); -}; - -const divisionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.divisionInterval(s, e))); -}; - export const g = makeTestGroup(GPUTest); -const scalar_cases = { - ['scalar']: () => { - return FP.abstract.generateScalarPairToIntervalCases( - sparseScalarF64Range(), - sparseScalarF64Range(), - 'finite', - FP.abstract.divisionInterval - ); - }, -}; - -const vector_scalar_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`vec${dim}_scalar`]: () => { - return FP.abstract.generateVectorScalarToVectorCases( - sparseVectorF64Range(dim), - sparseScalarF64Range(), - 'finite', - divisionVectorScalarInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`scalar_vec${dim}`]: () => { - return FP.abstract.generateScalarVectorToVectorCases( - sparseScalarF64Range(), - sparseVectorF64Range(dim), - 'finite', - divisionScalarVectorInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/af_division', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_addition.cache.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.cache.ts new file mode 100644 index 000000000000..75c13d7702b6 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.cache.ts @@ -0,0 +1,21 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF64Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR +const mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).map(rows => ({ + [`mat${cols}x${rows}`]: () => { + return FP.abstract.generateMatrixPairToMatrixCases( + sparseMatrixF64Range(cols, rows), + sparseMatrixF64Range(cols, rows), + 'finite', + FP.abstract.additionMatrixMatrixInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_matrix_addition', mat_cases); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts index 86bddec89467..9f232c1ef6e6 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts @@ -5,33 +5,13 @@ Execution Tests for matrix AbstractFloat addition expressions import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF64Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { onlyConstInputSource, run } from '../expression.js'; +import { d } from './af_matrix_addition.cache.js'; import { abstractBinary } from './binary.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR -const mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).map(rows => ({ - [`mat${cols}x${rows}`]: () => { - return FP.abstract.generateMatrixPairToMatrixCases( - sparseMatrixF64Range(cols, rows), - sparseMatrixF64Range(cols, rows), - 'finite', - FP.abstract.additionMatrixMatrixInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/af_matrix_addition', mat_cases); - g.test('matrix') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.cache.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.cache.ts new file mode 100644 index 000000000000..b030f369e3d3 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.cache.ts @@ -0,0 +1,21 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF64Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR +const mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).map(rows => ({ + [`mat${cols}x${rows}`]: () => { + return FP.abstract.generateMatrixPairToMatrixCases( + sparseMatrixF64Range(cols, rows), + sparseMatrixF64Range(cols, rows), + 'finite', + FP.abstract.subtractionMatrixMatrixInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_matrix_subtraction', mat_cases); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts index 849c11611f80..2697d1fe8cb0 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts @@ -5,33 +5,13 @@ Execution Tests for matrix AbstractFloat subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF64Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { onlyConstInputSource, run } from '../expression.js'; +import { d } from './af_matrix_subtraction.cache.js'; import { abstractBinary } from './binary.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR -const mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).map(rows => ({ - [`mat${cols}x${rows}`]: () => { - return FP.abstract.generateMatrixPairToMatrixCases( - sparseMatrixF64Range(cols, rows), - sparseMatrixF64Range(cols, rows), - 'finite', - FP.abstract.subtractionMatrixMatrixInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/af_matrix_subtraction', mat_cases); - g.test('matrix') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/af_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/af_multiplication.cache.ts new file mode 100644 index 000000000000..e111682cd28a --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_multiplication.cache.ts @@ -0,0 +1,54 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const multiplicationVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.multiplicationInterval(e, s))); +}; + +const multiplicationScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.multiplicationInterval(s, e))); +}; + +const scalar_cases = { + ['scalar']: () => { + return FP.abstract.generateScalarPairToIntervalCases( + sparseScalarF64Range(), + sparseScalarF64Range(), + 'finite', + FP.abstract.multiplicationInterval + ); + }, +}; + +const vector_scalar_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`vec${dim}_scalar`]: () => { + return FP.abstract.generateVectorScalarToVectorCases( + sparseVectorF64Range(dim), + sparseScalarF64Range(), + 'finite', + multiplicationVectorScalarInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`scalar_vec${dim}`]: () => { + return FP.abstract.generateScalarVectorToVectorCases( + sparseScalarF64Range(), + sparseVectorF64Range(dim), + 'finite', + multiplicationScalarVectorInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_multiplication', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts index 61626203b8b7..57beaca49ee8 100644 --- a/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts @@ -5,66 +5,13 @@ Execution Tests for non-matrix AbstractFloat multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { onlyConstInputSource, run } from '../expression.js'; +import { d } from './af_multiplication.cache.js'; import { abstractBinary } from './binary.js'; -const multiplicationVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.multiplicationInterval(e, s))); -}; - -const multiplicationScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.multiplicationInterval(s, e))); -}; - export const g = makeTestGroup(GPUTest); -const scalar_cases = { - ['scalar']: () => { - return FP.abstract.generateScalarPairToIntervalCases( - sparseScalarF64Range(), - sparseScalarF64Range(), - 'finite', - FP.abstract.multiplicationInterval - ); - }, -}; - -const vector_scalar_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`vec${dim}_scalar`]: () => { - return FP.abstract.generateVectorScalarToVectorCases( - sparseVectorF64Range(dim), - sparseScalarF64Range(), - 'finite', - multiplicationVectorScalarInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`scalar_vec${dim}`]: () => { - return FP.abstract.generateScalarVectorToVectorCases( - sparseScalarF64Range(), - sparseVectorF64Range(dim), - 'finite', - multiplicationScalarVectorInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/af_multiplication', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/af_remainder.cache.ts b/src/webgpu/shader/execution/expression/binary/af_remainder.cache.ts new file mode 100644 index 000000000000..eba0a27760b0 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_remainder.cache.ts @@ -0,0 +1,54 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const remainderVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.remainderInterval(e, s))); +}; + +const remainderScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.remainderInterval(s, e))); +}; + +const scalar_cases = { + ['scalar']: () => { + return FP.abstract.generateScalarPairToIntervalCases( + sparseScalarF64Range(), + sparseScalarF64Range(), + 'finite', + FP.abstract.remainderInterval + ); + }, +}; + +const vector_scalar_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`vec${dim}_scalar`]: () => { + return FP.abstract.generateVectorScalarToVectorCases( + sparseVectorF64Range(dim), + sparseScalarF64Range(), + 'finite', + remainderVectorScalarInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`scalar_vec${dim}`]: () => { + return FP.abstract.generateScalarVectorToVectorCases( + sparseScalarF64Range(), + sparseVectorF64Range(dim), + 'finite', + remainderScalarVectorInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_remainder', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts b/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts index 12002e867747..f53cb781b7cb 100644 --- a/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts @@ -5,66 +5,13 @@ Execution Tests for non-matrix abstract float remainder expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { onlyConstInputSource, run } from '../expression.js'; +import { d } from './af_remainder.cache.js'; import { abstractBinary } from './binary.js'; -const remainderVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.remainderInterval(e, s))); -}; - -const remainderScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.remainderInterval(s, e))); -}; - export const g = makeTestGroup(GPUTest); -const scalar_cases = { - ['scalar']: () => { - return FP.abstract.generateScalarPairToIntervalCases( - sparseScalarF64Range(), - sparseScalarF64Range(), - 'finite', - FP.abstract.remainderInterval - ); - }, -}; - -const vector_scalar_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`vec${dim}_scalar`]: () => { - return FP.abstract.generateVectorScalarToVectorCases( - sparseVectorF64Range(dim), - sparseScalarF64Range(), - 'finite', - remainderVectorScalarInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`scalar_vec${dim}`]: () => { - return FP.abstract.generateScalarVectorToVectorCases( - sparseScalarF64Range(), - sparseVectorF64Range(dim), - 'finite', - remainderScalarVectorInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/af_remainder', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/af_subtraction.cache.ts b/src/webgpu/shader/execution/expression/binary/af_subtraction.cache.ts new file mode 100644 index 000000000000..ea5107e143fc --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_subtraction.cache.ts @@ -0,0 +1,54 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const subtractionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.subtractionInterval(e, s))); +}; + +const subtractionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.abstract.toVector(v.map(e => FP.abstract.subtractionInterval(s, e))); +}; + +const scalar_cases = { + ['scalar']: () => { + return FP.abstract.generateScalarPairToIntervalCases( + sparseScalarF64Range(), + sparseScalarF64Range(), + 'finite', + FP.abstract.subtractionInterval + ); + }, +}; + +const vector_scalar_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`vec${dim}_scalar`]: () => { + return FP.abstract.generateVectorScalarToVectorCases( + sparseVectorF64Range(dim), + sparseScalarF64Range(), + 'finite', + subtractionVectorScalarInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .map(dim => ({ + [`scalar_vec${dim}`]: () => { + return FP.abstract.generateScalarVectorToVectorCases( + sparseScalarF64Range(), + sparseVectorF64Range(dim), + 'finite', + subtractionScalarVectorInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_subtraction', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts index 09617a6379a8..b9e304fa134f 100644 --- a/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts @@ -5,66 +5,13 @@ Execution Tests for non-matrix AbstractFloat subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF64Range, sparseVectorF64Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { onlyConstInputSource, run } from '../expression.js'; +import { d } from './af_subtraction.cache.js'; import { abstractBinary } from './binary.js'; -const subtractionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.subtractionInterval(e, s))); -}; - -const subtractionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.abstract.toVector(v.map(e => FP.abstract.subtractionInterval(s, e))); -}; - export const g = makeTestGroup(GPUTest); -const scalar_cases = { - ['scalar']: () => { - return FP.abstract.generateScalarPairToIntervalCases( - sparseScalarF64Range(), - sparseScalarF64Range(), - 'finite', - FP.abstract.subtractionInterval - ); - }, -}; - -const vector_scalar_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`vec${dim}_scalar`]: () => { - return FP.abstract.generateVectorScalarToVectorCases( - sparseVectorF64Range(dim), - sparseScalarF64Range(), - 'finite', - subtractionVectorScalarInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .map(dim => ({ - [`scalar_vec${dim}`]: () => { - return FP.abstract.generateScalarVectorToVectorCases( - sparseScalarF64Range(), - sparseVectorF64Range(dim), - 'finite', - subtractionScalarVectorInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/af_subtraction', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts b/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts index 5457b7ceab54..b47d66a47964 100644 --- a/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts @@ -5,7 +5,8 @@ Execution Tests for the bitwise shift binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { i32, scalarType, ScalarType, TypeU32, u32 } from '../../../../util/conversion.js'; -import { allInputSources, CaseList, run } from '../expression.js'; +import { CaseList } from '../case.js'; +import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; diff --git a/src/webgpu/shader/execution/expression/binary/f16_addition.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_addition.cache.ts new file mode 100644 index 000000000000..f179d48a131b --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_addition.cache.ts @@ -0,0 +1,60 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const additionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.additionInterval(e, s))); +}; + +const additionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.additionInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarPairToIntervalCases( + sparseScalarF16Range(), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f16.additionInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateVectorScalarToVectorCases( + sparseVectorF16Range(dim), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + additionVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarVectorToVectorCases( + sparseScalarF16Range(), + sparseVectorF16Range(dim), + nonConst ? 'unfiltered' : 'finite', + additionScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_addition', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts index 99c08db43681..7f94c7e307a9 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f16 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const additionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.additionInterval(e, s))); -}; - -const additionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.additionInterval(s, e))); -}; +import { d } from './f16_addition.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarPairToIntervalCases( - sparseScalarF16Range(), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f16.additionInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateVectorScalarToVectorCases( - sparseVectorF16Range(dim), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - additionVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarVectorToVectorCases( - sparseScalarF16Range(), - sparseVectorF16Range(dim), - nonConst ? 'unfiltered' : 'finite', - additionScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_addition', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts new file mode 100644 index 000000000000..92d926a412b9 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts @@ -0,0 +1,144 @@ +import { anyOf } from '../../../../util/compare.js'; +import { bool, f16, Scalar } from '../../../../util/conversion.js'; +import { flushSubnormalNumberF16, vectorF16Range } from '../../../../util/math.js'; +import { Case } from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +/** + * @returns a test case for the provided left hand & right hand values and truth function. + * Handles quantization and subnormals. + */ +function makeCase( + lhs: number, + rhs: number, + truthFunc: (lhs: Scalar, rhs: Scalar) => boolean +): Case { + // Subnormal float values may be flushed at any time. + // https://www.w3.org/TR/WGSL/#floating-point-evaluation + const f16_lhs = f16(lhs); + const f16_rhs = f16(rhs); + const lhs_options = new Set([f16_lhs, f16(flushSubnormalNumberF16(lhs))]); + const rhs_options = new Set([f16_rhs, f16(flushSubnormalNumberF16(rhs))]); + const expected: Array = []; + lhs_options.forEach(l => { + rhs_options.forEach(r => { + const result = bool(truthFunc(l, r)); + if (!expected.includes(result)) { + expected.push(result); + } + }); + }); + + return { input: [f16_lhs, f16_rhs], expected: anyOf(...expected) }; +} + +export const d = makeCaseCache('binary/f16_logical', { + equals_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) === (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + equals_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) === (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + not_equals_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) !== (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + not_equals_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) !== (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_than_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) < (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_than_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) < (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_equals_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) <= (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_equals_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) <= (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_than_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) > (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_than_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) > (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_equals_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) >= (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_equals_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) >= (rhs.value as number); + }; + + return vectorF16Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts index ae7e1675c5ac..c84080983de5 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts @@ -4,155 +4,14 @@ Execution Tests for the f16 comparison operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { anyOf } from '../../../../util/compare.js'; -import { bool, f16, Scalar, TypeBool, TypeF16 } from '../../../../util/conversion.js'; -import { flushSubnormalNumberF16, vectorF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, Case, run } from '../expression.js'; +import { TypeBool, TypeF16 } from '../../../../util/conversion.js'; +import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; +import { d } from './f16_comparison.cache.js'; export const g = makeTestGroup(GPUTest); -/** - * @returns a test case for the provided left hand & right hand values and truth function. - * Handles quantization and subnormals. - */ -function makeCase( - lhs: number, - rhs: number, - truthFunc: (lhs: Scalar, rhs: Scalar) => boolean -): Case { - // Subnormal float values may be flushed at any time. - // https://www.w3.org/TR/WGSL/#floating-point-evaluation - const f16_lhs = f16(lhs); - const f16_rhs = f16(rhs); - const lhs_options = new Set([f16_lhs, f16(flushSubnormalNumberF16(lhs))]); - const rhs_options = new Set([f16_rhs, f16(flushSubnormalNumberF16(rhs))]); - const expected: Array = []; - lhs_options.forEach(l => { - rhs_options.forEach(r => { - const result = bool(truthFunc(l, r)); - if (!expected.includes(result)) { - expected.push(result); - } - }); - }); - - return { input: [f16_lhs, f16_rhs], expected: anyOf(...expected) }; -} - -export const d = makeCaseCache('binary/f16_logical', { - equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) === (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) === (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - not_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) !== (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - not_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) !== (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) < (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) < (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) <= (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) <= (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) > (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) > (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) >= (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) >= (rhs.value as number); - }; - - return vectorF16Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, -}); - g.test('equals') .specURL('https://www.w3.org/TR/WGSL/#comparison-expr') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_division.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_division.cache.ts new file mode 100644 index 000000000000..95590ca46789 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_division.cache.ts @@ -0,0 +1,60 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const divisionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.divisionInterval(e, s))); +}; + +const divisionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.divisionInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarPairToIntervalCases( + sparseScalarF16Range(), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f16.divisionInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateVectorScalarToVectorCases( + sparseVectorF16Range(dim), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + divisionVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarVectorToVectorCases( + sparseScalarF16Range(), + sparseVectorF16Range(dim), + nonConst ? 'unfiltered' : 'finite', + divisionScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_division', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts index 725c90828071..7dfc13f81a2f 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f16 division expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const divisionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.divisionInterval(e, s))); -}; - -const divisionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.divisionInterval(s, e))); -}; +import { d } from './f16_division.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarPairToIntervalCases( - sparseScalarF16Range(), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f16.divisionInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateVectorScalarToVectorCases( - sparseVectorF16Range(dim), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - divisionVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarVectorToVectorCases( - sparseScalarF16Range(), - sparseVectorF16Range(dim), - nonConst ? 'unfiltered' : 'finite', - divisionScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_division', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.cache.ts new file mode 100644 index 000000000000..a670b08b0740 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.cache.ts @@ -0,0 +1,23 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_[non_]const +const mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateMatrixPairToMatrixCases( + sparseMatrixF16Range(cols, rows), + sparseMatrixF16Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f16.additionMatrixMatrixInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_matrix_addition', mat_cases); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts index fe64f41503fd..daf768a5365c 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts @@ -5,35 +5,13 @@ Execution Tests for matrix f16 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f16_matrix_addition.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR_[non_]const -const mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateMatrixPairToMatrixCases( - sparseMatrixF16Range(cols, rows), - sparseMatrixF16Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f16.additionMatrixMatrixInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_matrix_addition', mat_cases); - g.test('matrix') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.cache.ts new file mode 100644 index 000000000000..a31813abb3ba --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.cache.ts @@ -0,0 +1,25 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matKxR_matCxK_[non_]const +const mat_mat_cases = ([2, 3, 4] as const) + .flatMap(k => + ([2, 3, 4] as const).flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${k}x${rows}_mat${cols}x${k}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateMatrixPairToMatrixCases( + sparseMatrixF16Range(k, rows), + sparseMatrixF16Range(cols, k), + nonConst ? 'unfiltered' : 'finite', + FP.f16.multiplicationMatrixMatrixInterval + ); + }, + })) + ) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_matrix_matrix_multiplication', mat_mat_cases); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts index 0c8b3e8c51c4..69c56c60c368 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts @@ -5,37 +5,13 @@ Execution Tests for matrix-matrix f16 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f16_matrix_matrix_multiplication.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matKxR_matCxK_[non_]const -const mat_mat_cases = ([2, 3, 4] as const) - .flatMap(k => - ([2, 3, 4] as const).flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${k}x${rows}_mat${cols}x${k}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateMatrixPairToMatrixCases( - sparseMatrixF16Range(k, rows), - sparseMatrixF16Range(cols, k), - nonConst ? 'unfiltered' : 'finite', - FP.f16.multiplicationMatrixMatrixInterval - ); - }, - })) - ) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_matrix_matrix_multiplication', mat_mat_cases); - g.test('matrix_matrix') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.cache.ts new file mode 100644 index 000000000000..f902a1c8bc8d --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.cache.ts @@ -0,0 +1,44 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF16Range, sparseScalarF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_scalar_[non_]const +const mat_scalar_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${cols}x${rows}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateMatrixScalarToMatrixCases( + sparseMatrixF16Range(cols, rows), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f16.multiplicationMatrixScalarInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: scalar_matCxR_[non_]const +const scalar_mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`scalar_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarMatrixToMatrixCases( + sparseScalarF16Range(), + sparseMatrixF16Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f16.multiplicationScalarMatrixInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_matrix_scalar_multiplication', { + ...mat_scalar_cases, + ...scalar_mat_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts index 83f583f9b6d7..338d6d021bb5 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts @@ -5,56 +5,13 @@ Execution Tests for matrix-scalar and scalar-matrix f16 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseScalarF16Range, sparseMatrixF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f16_matrix_scalar_multiplication.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR_scalar_[non_]const -const mat_scalar_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${cols}x${rows}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateMatrixScalarToMatrixCases( - sparseMatrixF16Range(cols, rows), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f16.multiplicationMatrixScalarInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: scalar_matCxR_[non_]const -const scalar_mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`scalar_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarMatrixToMatrixCases( - sparseScalarF16Range(), - sparseMatrixF16Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f16.multiplicationScalarMatrixInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_matrix_scalar_multiplication', { - ...mat_scalar_cases, - ...scalar_mat_cases, -}); - g.test('matrix_scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.cache.ts new file mode 100644 index 000000000000..a6edcf7fe500 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.cache.ts @@ -0,0 +1,23 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_[non_]const +const mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateMatrixPairToMatrixCases( + sparseMatrixF16Range(cols, rows), + sparseMatrixF16Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f16.subtractionMatrixMatrixInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_matrix_subtraction', mat_cases); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts index 5b5f6ba04e3f..442c31ef82e9 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts @@ -5,35 +5,13 @@ Execution Tests for matrix f16 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f16_matrix_subtraction.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR_[non_]const -const mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateMatrixPairToMatrixCases( - sparseMatrixF16Range(cols, rows), - sparseMatrixF16Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f16.subtractionMatrixMatrixInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_matrix_subtraction', mat_cases); - g.test('matrix') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.cache.ts new file mode 100644 index 000000000000..7b822386feb2 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.cache.ts @@ -0,0 +1,44 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF16Range, sparseVectorF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_vecC_[non_]const +const mat_vec_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${cols}x${rows}_vec${cols}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateMatrixVectorToVectorCases( + sparseMatrixF16Range(cols, rows), + sparseVectorF16Range(cols), + nonConst ? 'unfiltered' : 'finite', + FP.f16.multiplicationMatrixVectorInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: vecR_matCxR_[non_]const +const vec_mat_cases = ([2, 3, 4] as const) + .flatMap(rows => + ([2, 3, 4] as const).flatMap(cols => + ([true, false] as const).map(nonConst => ({ + [`vec${rows}_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateVectorMatrixToVectorCases( + sparseVectorF16Range(rows), + sparseMatrixF16Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f16.multiplicationVectorMatrixInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_matrix_vector_multiplication', { + ...mat_vec_cases, + ...vec_mat_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts index 3e916c7fd400..9715fe681e35 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts @@ -5,56 +5,13 @@ Execution Tests for matrix-vector and vector-matrix f16 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeMat, TypeVec } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF16Range, sparseVectorF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f16_matrix_vector_multiplication.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR_vecC_[non_]const -const mat_vec_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${cols}x${rows}_vec${cols}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateMatrixVectorToVectorCases( - sparseMatrixF16Range(cols, rows), - sparseVectorF16Range(cols), - nonConst ? 'unfiltered' : 'finite', - FP.f16.multiplicationMatrixVectorInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: vecR_matCxR_[non_]const -const vec_mat_cases = ([2, 3, 4] as const) - .flatMap(rows => - ([2, 3, 4] as const).flatMap(cols => - ([true, false] as const).map(nonConst => ({ - [`vec${rows}_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateVectorMatrixToVectorCases( - sparseVectorF16Range(rows), - sparseMatrixF16Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f16.multiplicationVectorMatrixInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_matrix_vector_multiplication', { - ...mat_vec_cases, - ...vec_mat_cases, -}); - g.test('matrix_vector') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_multiplication.cache.ts new file mode 100644 index 000000000000..a94f55ccf0e5 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_multiplication.cache.ts @@ -0,0 +1,60 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const multiplicationVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.multiplicationInterval(e, s))); +}; + +const multiplicationScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.multiplicationInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarPairToIntervalCases( + sparseScalarF16Range(), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f16.multiplicationInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateVectorScalarToVectorCases( + sparseVectorF16Range(dim), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + multiplicationVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarVectorToVectorCases( + sparseScalarF16Range(), + sparseVectorF16Range(dim), + nonConst ? 'unfiltered' : 'finite', + multiplicationScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_multiplication', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts index 9af0c2918aa4..005a9a1e64af 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f16 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const multiplicationVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.multiplicationInterval(e, s))); -}; - -const multiplicationScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.multiplicationInterval(s, e))); -}; +import { d } from './f16_multiplication.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarPairToIntervalCases( - sparseScalarF16Range(), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f16.multiplicationInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateVectorScalarToVectorCases( - sparseVectorF16Range(dim), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - multiplicationVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarVectorToVectorCases( - sparseScalarF16Range(), - sparseVectorF16Range(dim), - nonConst ? 'unfiltered' : 'finite', - multiplicationScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_multiplication', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_remainder.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_remainder.cache.ts new file mode 100644 index 000000000000..2c1cdc0c38b4 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_remainder.cache.ts @@ -0,0 +1,60 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const remainderVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.remainderInterval(e, s))); +}; + +const remainderScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.remainderInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarPairToIntervalCases( + sparseScalarF16Range(), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f16.remainderInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateVectorScalarToVectorCases( + sparseVectorF16Range(dim), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + remainderVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarVectorToVectorCases( + sparseScalarF16Range(), + sparseVectorF16Range(dim), + nonConst ? 'unfiltered' : 'finite', + remainderScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_remainder', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts index e7f10daf4eb8..c2f5cd7af11f 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f16 remainder expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const remainderVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.remainderInterval(e, s))); -}; - -const remainderScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.remainderInterval(s, e))); -}; +import { d } from './f16_remainder.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarPairToIntervalCases( - sparseScalarF16Range(), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f16.remainderInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateVectorScalarToVectorCases( - sparseVectorF16Range(dim), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - remainderVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarVectorToVectorCases( - sparseScalarF16Range(), - sparseVectorF16Range(dim), - nonConst ? 'unfiltered' : 'finite', - remainderScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_remainder', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f16_subtraction.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_subtraction.cache.ts new file mode 100644 index 000000000000..a68b58449b33 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f16_subtraction.cache.ts @@ -0,0 +1,60 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const subtractionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.subtractionInterval(e, s))); +}; + +const subtractionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f16.toVector(v.map(e => FP.f16.subtractionInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarPairToIntervalCases( + sparseScalarF16Range(), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f16.subtractionInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateVectorScalarToVectorCases( + sparseVectorF16Range(dim), + sparseScalarF16Range(), + nonConst ? 'unfiltered' : 'finite', + subtractionVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateScalarVectorToVectorCases( + sparseScalarF16Range(), + sparseVectorF16Range(dim), + nonConst ? 'unfiltered' : 'finite', + subtractionScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f16_subtraction', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts index 923c95d40108..a458885ae8ce 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f16 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF16Range, sparseVectorF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const subtractionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.subtractionInterval(e, s))); -}; - -const subtractionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f16.toVector(v.map(e => FP.f16.subtractionInterval(s, e))); -}; +import { d } from './f16_subtraction.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarPairToIntervalCases( - sparseScalarF16Range(), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f16.subtractionInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateVectorScalarToVectorCases( - sparseVectorF16Range(dim), - sparseScalarF16Range(), - nonConst ? 'unfiltered' : 'finite', - subtractionVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateScalarVectorToVectorCases( - sparseScalarF16Range(), - sparseVectorF16Range(dim), - nonConst ? 'unfiltered' : 'finite', - subtractionScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f16_subtraction', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_addition.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_addition.cache.ts new file mode 100644 index 000000000000..9353671fb028 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_addition.cache.ts @@ -0,0 +1,60 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const additionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.additionInterval(e, s))); +}; + +const additionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.additionInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarPairToIntervalCases( + sparseScalarF32Range(), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f32.additionInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateVectorScalarToVectorCases( + sparseVectorF32Range(dim), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + additionVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarVectorToVectorCases( + sparseScalarF32Range(), + sparseVectorF32Range(dim), + nonConst ? 'unfiltered' : 'finite', + additionScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_addition', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts index 772ec272e93f..6bd8b0e7bdb7 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f32 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const additionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.additionInterval(e, s))); -}; - -const additionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.additionInterval(s, e))); -}; +import { d } from './f32_addition.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarPairToIntervalCases( - sparseScalarF32Range(), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f32.additionInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateVectorScalarToVectorCases( - sparseVectorF32Range(dim), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - additionVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarVectorToVectorCases( - sparseScalarF32Range(), - sparseVectorF32Range(dim), - nonConst ? 'unfiltered' : 'finite', - additionScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_addition', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts new file mode 100644 index 000000000000..4c16fc55f0e5 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts @@ -0,0 +1,144 @@ +import { anyOf } from '../../../../util/compare.js'; +import { bool, f32, Scalar } from '../../../../util/conversion.js'; +import { flushSubnormalNumberF32, vectorF32Range } from '../../../../util/math.js'; +import { Case } from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +/** + * @returns a test case for the provided left hand & right hand values and truth function. + * Handles quantization and subnormals. + */ +function makeCase( + lhs: number, + rhs: number, + truthFunc: (lhs: Scalar, rhs: Scalar) => boolean +): Case { + // Subnormal float values may be flushed at any time. + // https://www.w3.org/TR/WGSL/#floating-point-evaluation + const f32_lhs = f32(lhs); + const f32_rhs = f32(rhs); + const lhs_options = new Set([f32_lhs, f32(flushSubnormalNumberF32(lhs))]); + const rhs_options = new Set([f32_rhs, f32(flushSubnormalNumberF32(rhs))]); + const expected: Array = []; + lhs_options.forEach(l => { + rhs_options.forEach(r => { + const result = bool(truthFunc(l, r)); + if (!expected.includes(result)) { + expected.push(result); + } + }); + }); + + return { input: [f32_lhs, f32_rhs], expected: anyOf(...expected) }; +} + +export const d = makeCaseCache('binary/f32_logical', { + equals_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) === (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + equals_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) === (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + not_equals_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) !== (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + not_equals_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) !== (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_than_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) < (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_than_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) < (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_equals_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) <= (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + less_equals_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) <= (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_than_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) > (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_than_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) > (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_equals_non_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) >= (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, + greater_equals_const: () => { + const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + return (lhs.value as number) >= (rhs.value as number); + }; + + return vectorF32Range(2).map(v => { + return makeCase(v[0], v[1], truthFunc); + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts index ef862e7757a0..e5833a0f9f33 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts @@ -4,155 +4,14 @@ Execution Tests for the f32 comparison operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { anyOf } from '../../../../util/compare.js'; -import { bool, f32, Scalar, TypeBool, TypeF32 } from '../../../../util/conversion.js'; -import { flushSubnormalNumberF32, vectorF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, Case, run } from '../expression.js'; +import { TypeBool, TypeF32 } from '../../../../util/conversion.js'; +import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; +import { d } from './f32_comparison.cache.js'; export const g = makeTestGroup(GPUTest); -/** - * @returns a test case for the provided left hand & right hand values and truth function. - * Handles quantization and subnormals. - */ -function makeCase( - lhs: number, - rhs: number, - truthFunc: (lhs: Scalar, rhs: Scalar) => boolean -): Case { - // Subnormal float values may be flushed at any time. - // https://www.w3.org/TR/WGSL/#floating-point-evaluation - const f32_lhs = f32(lhs); - const f32_rhs = f32(rhs); - const lhs_options = new Set([f32_lhs, f32(flushSubnormalNumberF32(lhs))]); - const rhs_options = new Set([f32_rhs, f32(flushSubnormalNumberF32(rhs))]); - const expected: Array = []; - lhs_options.forEach(l => { - rhs_options.forEach(r => { - const result = bool(truthFunc(l, r)); - if (!expected.includes(result)) { - expected.push(result); - } - }); - }); - - return { input: [f32_lhs, f32_rhs], expected: anyOf(...expected) }; -} - -export const d = makeCaseCache('binary/f32_logical', { - equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) === (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) === (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - not_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) !== (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - not_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) !== (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) < (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) < (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) <= (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - less_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) <= (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) > (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) > (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) >= (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, - greater_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { - return (lhs.value as number) >= (rhs.value as number); - }; - - return vectorF32Range(2).map(v => { - return makeCase(v[0], v[1], truthFunc); - }); - }, -}); - g.test('equals') .specURL('https://www.w3.org/TR/WGSL/#comparison-expr') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_division.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_division.cache.ts new file mode 100644 index 000000000000..017f7a451a3f --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_division.cache.ts @@ -0,0 +1,60 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const divisionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.divisionInterval(e, s))); +}; + +const divisionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.divisionInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarPairToIntervalCases( + sparseScalarF32Range(), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f32.divisionInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateVectorScalarToVectorCases( + sparseVectorF32Range(dim), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + divisionVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarVectorToVectorCases( + sparseScalarF32Range(), + sparseVectorF32Range(dim), + nonConst ? 'unfiltered' : 'finite', + divisionScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_division', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts index ecbf7e4fd7dc..0c8dd293f57d 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f32 division expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const divisionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.divisionInterval(e, s))); -}; - -const divisionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.divisionInterval(s, e))); -}; +import { d } from './f32_division.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarPairToIntervalCases( - sparseScalarF32Range(), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f32.divisionInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateVectorScalarToVectorCases( - sparseVectorF32Range(dim), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - divisionVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarVectorToVectorCases( - sparseScalarF32Range(), - sparseVectorF32Range(dim), - nonConst ? 'unfiltered' : 'finite', - divisionScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_division', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.cache.ts new file mode 100644 index 000000000000..0f3ced975d12 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.cache.ts @@ -0,0 +1,23 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_[non_]const +const mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateMatrixPairToMatrixCases( + sparseMatrixF32Range(cols, rows), + sparseMatrixF32Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f32.additionMatrixMatrixInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_matrix_addition', mat_cases); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts index 9f11c3cac19d..4fcb4d08927d 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts @@ -5,35 +5,13 @@ Execution Tests for matrix f32 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f32_matrix_addition.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR_[non_]const -const mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateMatrixPairToMatrixCases( - sparseMatrixF32Range(cols, rows), - sparseMatrixF32Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f32.additionMatrixMatrixInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_matrix_addition', mat_cases); - g.test('matrix') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.cache.ts new file mode 100644 index 000000000000..cde2d74fdfbe --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.cache.ts @@ -0,0 +1,25 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matKxR_matCxK_[non_]const +const mat_mat_cases = ([2, 3, 4] as const) + .flatMap(k => + ([2, 3, 4] as const).flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${k}x${rows}_mat${cols}x${k}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateMatrixPairToMatrixCases( + sparseMatrixF32Range(k, rows), + sparseMatrixF32Range(cols, k), + nonConst ? 'unfiltered' : 'finite', + FP.f32.multiplicationMatrixMatrixInterval + ); + }, + })) + ) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_matrix_matrix_multiplication', mat_mat_cases); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts index 2c48eab1872c..ad34d642d26e 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts @@ -5,37 +5,13 @@ Execution Tests for matrix-matrix f32 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f32_matrix_matrix_multiplication.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matKxR_matCxK_[non_]const -const mat_mat_cases = ([2, 3, 4] as const) - .flatMap(k => - ([2, 3, 4] as const).flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${k}x${rows}_mat${cols}x${k}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateMatrixPairToMatrixCases( - sparseMatrixF32Range(k, rows), - sparseMatrixF32Range(cols, k), - nonConst ? 'unfiltered' : 'finite', - FP.f32.multiplicationMatrixMatrixInterval - ); - }, - })) - ) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_matrix_matrix_multiplication', mat_mat_cases); - g.test('matrix_matrix') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.cache.ts new file mode 100644 index 000000000000..f17dac31e6a9 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.cache.ts @@ -0,0 +1,44 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF32Range, sparseScalarF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_scalar_[non_]const +const mat_scalar_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${cols}x${rows}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateMatrixScalarToMatrixCases( + sparseMatrixF32Range(cols, rows), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f32.multiplicationMatrixScalarInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: scalar_matCxR_[non_]const +const scalar_mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`scalar_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarMatrixToMatrixCases( + sparseScalarF32Range(), + sparseMatrixF32Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f32.multiplicationScalarMatrixInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_matrix_scalar_multiplication', { + ...mat_scalar_cases, + ...scalar_mat_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts index 8f50c7781688..0558862d065d 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts @@ -5,56 +5,13 @@ Execution Tests for matrix-scalar and scalar-matrix f32 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseScalarF32Range, sparseMatrixF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f32_matrix_scalar_multiplication.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR_scalar_[non_]const -const mat_scalar_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${cols}x${rows}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateMatrixScalarToMatrixCases( - sparseMatrixF32Range(cols, rows), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f32.multiplicationMatrixScalarInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: scalar_matCxR_[non_]const -const scalar_mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`scalar_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarMatrixToMatrixCases( - sparseScalarF32Range(), - sparseMatrixF32Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f32.multiplicationScalarMatrixInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_matrix_scalar_multiplication', { - ...mat_scalar_cases, - ...scalar_mat_cases, -}); - g.test('matrix_scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.cache.ts new file mode 100644 index 000000000000..2cd2bc1c6d22 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.cache.ts @@ -0,0 +1,23 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_[non_]const +const mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateMatrixPairToMatrixCases( + sparseMatrixF32Range(cols, rows), + sparseMatrixF32Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f32.subtractionMatrixMatrixInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_matrix_subtraction', mat_cases); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts index 5f101d9b270f..8bcf9a47895c 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts @@ -5,35 +5,13 @@ Execution Tests for matrix f32 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeMat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f32_matrix_subtraction.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR_[non_]const -const mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateMatrixPairToMatrixCases( - sparseMatrixF32Range(cols, rows), - sparseMatrixF32Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f32.subtractionMatrixMatrixInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_matrix_subtraction', mat_cases); - g.test('matrix') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.cache.ts new file mode 100644 index 000000000000..f20b95029e2e --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.cache.ts @@ -0,0 +1,44 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF32Range, sparseVectorF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_vecC_[non_]const +const mat_vec_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`mat${cols}x${rows}_vec${cols}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateMatrixVectorToVectorCases( + sparseMatrixF32Range(cols, rows), + sparseVectorF32Range(cols), + nonConst ? 'unfiltered' : 'finite', + FP.f32.multiplicationMatrixVectorInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: vecR_matCxR_[non_]const +const vec_mat_cases = ([2, 3, 4] as const) + .flatMap(rows => + ([2, 3, 4] as const).flatMap(cols => + ([true, false] as const).map(nonConst => ({ + [`vec${rows}_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateVectorMatrixToVectorCases( + sparseVectorF32Range(rows), + sparseMatrixF32Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f32.multiplicationVectorMatrixInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_matrix_vector_multiplication', { + ...mat_vec_cases, + ...vec_mat_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts index e6cdf16d9240..a04a28cfeff3 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts @@ -5,56 +5,13 @@ Execution Tests for matrix-vector and vector-matrix f32 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeMat, TypeVec } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { sparseMatrixF32Range, sparseVectorF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; +import { d } from './f32_matrix_vector_multiplication.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: matCxR_vecC_[non_]const -const mat_vec_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`mat${cols}x${rows}_vec${cols}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateMatrixVectorToVectorCases( - sparseMatrixF32Range(cols, rows), - sparseVectorF32Range(cols), - nonConst ? 'unfiltered' : 'finite', - FP.f32.multiplicationMatrixVectorInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: vecR_matCxR_[non_]const -const vec_mat_cases = ([2, 3, 4] as const) - .flatMap(rows => - ([2, 3, 4] as const).flatMap(cols => - ([true, false] as const).map(nonConst => ({ - [`vec${rows}_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateVectorMatrixToVectorCases( - sparseVectorF32Range(rows), - sparseMatrixF32Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f32.multiplicationVectorMatrixInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_matrix_vector_multiplication', { - ...mat_vec_cases, - ...vec_mat_cases, -}); - g.test('matrix_vector') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_multiplication.cache.ts new file mode 100644 index 000000000000..6a8c0bd81e9d --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_multiplication.cache.ts @@ -0,0 +1,60 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const multiplicationVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.multiplicationInterval(e, s))); +}; + +const multiplicationScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.multiplicationInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarPairToIntervalCases( + sparseScalarF32Range(), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f32.multiplicationInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateVectorScalarToVectorCases( + sparseVectorF32Range(dim), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + multiplicationVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarVectorToVectorCases( + sparseScalarF32Range(), + sparseVectorF32Range(dim), + nonConst ? 'unfiltered' : 'finite', + multiplicationScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_multiplication', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts index 1c0da8ed19f5..77e670795d2d 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f32 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const multiplicationVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.multiplicationInterval(e, s))); -}; - -const multiplicationScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.multiplicationInterval(s, e))); -}; +import { d } from './f32_multiplication.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarPairToIntervalCases( - sparseScalarF32Range(), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f32.multiplicationInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateVectorScalarToVectorCases( - sparseVectorF32Range(dim), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - multiplicationVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarVectorToVectorCases( - sparseScalarF32Range(), - sparseVectorF32Range(dim), - nonConst ? 'unfiltered' : 'finite', - multiplicationScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_multiplication', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_remainder.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_remainder.cache.ts new file mode 100644 index 000000000000..2cb1a16f9dcc --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_remainder.cache.ts @@ -0,0 +1,64 @@ +export const description = ` +Execution Tests for non-matrix f32 remainder expression +`; + +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const remainderVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.remainderInterval(e, s))); +}; + +const remainderScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.remainderInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarPairToIntervalCases( + sparseScalarF32Range(), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f32.remainderInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateVectorScalarToVectorCases( + sparseVectorF32Range(dim), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + remainderVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarVectorToVectorCases( + sparseScalarF32Range(), + sparseVectorF32Range(dim), + nonConst ? 'unfiltered' : 'finite', + remainderScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_remainder', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts index 3add756dfd66..b61d3ecc633a 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f32 remainder expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const remainderVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.remainderInterval(e, s))); -}; - -const remainderScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.remainderInterval(s, e))); -}; +import { d } from './f32_remainder.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarPairToIntervalCases( - sparseScalarF32Range(), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f32.remainderInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateVectorScalarToVectorCases( - sparseVectorF32Range(dim), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - remainderVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarVectorToVectorCases( - sparseScalarF32Range(), - sparseVectorF32Range(dim), - nonConst ? 'unfiltered' : 'finite', - remainderScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_remainder', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/f32_subtraction.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_subtraction.cache.ts new file mode 100644 index 000000000000..519c0b37833d --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/f32_subtraction.cache.ts @@ -0,0 +1,60 @@ +import { FP, FPVector } from '../../../../util/floating_point.js'; +import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const subtractionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.subtractionInterval(e, s))); +}; + +const subtractionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { + return FP.f32.toVector(v.map(e => FP.f32.subtractionInterval(s, e))); +}; + +const scalar_cases = ([true, false] as const) + .map(nonConst => ({ + [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarPairToIntervalCases( + sparseScalarF32Range(), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + FP.f32.subtractionInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const vector_scalar_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateVectorScalarToVectorCases( + sparseVectorF32Range(dim), + sparseScalarF32Range(), + nonConst ? 'unfiltered' : 'finite', + subtractionVectorScalarInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +const scalar_vector_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateScalarVectorToVectorCases( + sparseScalarF32Range(), + sparseVectorF32Range(dim), + nonConst ? 'unfiltered' : 'finite', + subtractionScalarVectorInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/f32_subtraction', { + ...scalar_cases, + ...vector_scalar_cases, + ...scalar_vector_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts index 2870a14177b7..e873c6ebc3b1 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts @@ -5,72 +5,13 @@ Execution Tests for non-matrix f32 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32, TypeVec } from '../../../../util/conversion.js'; -import { FP, FPVector } from '../../../../util/floating_point.js'; -import { sparseScalarF32Range, sparseVectorF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -const subtractionVectorScalarInterval = (v: readonly number[], s: number): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.subtractionInterval(e, s))); -}; - -const subtractionScalarVectorInterval = (s: number, v: readonly number[]): FPVector => { - return FP.f32.toVector(v.map(e => FP.f32.subtractionInterval(s, e))); -}; +import { d } from './f32_subtraction.cache.js'; export const g = makeTestGroup(GPUTest); -const scalar_cases = ([true, false] as const) - .map(nonConst => ({ - [`scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarPairToIntervalCases( - sparseScalarF32Range(), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - FP.f32.subtractionInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const vector_scalar_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateVectorScalarToVectorCases( - sparseVectorF32Range(dim), - sparseScalarF32Range(), - nonConst ? 'unfiltered' : 'finite', - subtractionVectorScalarInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -const scalar_vector_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`scalar_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateScalarVectorToVectorCases( - sparseScalarF32Range(), - sparseVectorF32Range(dim), - nonConst ? 'unfiltered' : 'finite', - subtractionScalarVectorInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('binary/f32_subtraction', { - ...scalar_cases, - ...vector_scalar_cases, - ...scalar_vector_cases, -}); - g.test('scalar') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.cache.ts b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.cache.ts new file mode 100644 index 000000000000..ca7ca879f52a --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.cache.ts @@ -0,0 +1,306 @@ +import { kValue } from '../../../../util/constants.js'; +import { sparseI32Range, vectorI32Range } from '../../../../util/math.js'; +import { + generateBinaryToI32Cases, + generateI32VectorBinaryToVectorCases, + generateVectorI32BinaryToVectorCases, +} from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +function i32_add(x: number, y: number): number | undefined { + return x + y; +} + +function i32_subtract(x: number, y: number): number | undefined { + return x - y; +} + +function i32_multiply(x: number, y: number): number | undefined { + return Math.imul(x, y); +} + +function i32_divide_non_const(x: number, y: number): number | undefined { + if (y === 0) { + return x; + } + if (x === kValue.i32.negative.min && y === -1) { + return x; + } + return x / y; +} + +function i32_divide_const(x: number, y: number): number | undefined { + if (y === 0) { + return undefined; + } + if (x === kValue.i32.negative.min && y === -1) { + return undefined; + } + return x / y; +} + +function i32_remainder_non_const(x: number, y: number): number | undefined { + if (y === 0) { + return 0; + } + if (x === kValue.i32.negative.min && y === -1) { + return 0; + } + return x % y; +} + +function i32_remainder_const(x: number, y: number): number | undefined { + if (y === 0) { + return undefined; + } + if (x === kValue.i32.negative.min && y === -1) { + return undefined; + } + return x % y; +} + +export const d = makeCaseCache('binary/i32_arithmetic', { + addition: () => { + return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_add); + }, + subtraction: () => { + return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_subtract); + }, + multiplication: () => { + return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_multiply); + }, + division_non_const: () => { + return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_divide_non_const); + }, + division_const: () => { + return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_divide_const); + }, + remainder_non_const: () => { + return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_remainder_non_const); + }, + remainder_const: () => { + return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_remainder_const); + }, + addition_scalar_vector2: () => { + return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(2), i32_add); + }, + addition_scalar_vector3: () => { + return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(3), i32_add); + }, + addition_scalar_vector4: () => { + return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(4), i32_add); + }, + addition_vector2_scalar: () => { + return generateVectorI32BinaryToVectorCases(vectorI32Range(2), sparseI32Range(), i32_add); + }, + addition_vector3_scalar: () => { + return generateVectorI32BinaryToVectorCases(vectorI32Range(3), sparseI32Range(), i32_add); + }, + addition_vector4_scalar: () => { + return generateVectorI32BinaryToVectorCases(vectorI32Range(4), sparseI32Range(), i32_add); + }, + subtraction_scalar_vector2: () => { + return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(2), i32_subtract); + }, + subtraction_scalar_vector3: () => { + return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(3), i32_subtract); + }, + subtraction_scalar_vector4: () => { + return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(4), i32_subtract); + }, + subtraction_vector2_scalar: () => { + return generateVectorI32BinaryToVectorCases(vectorI32Range(2), sparseI32Range(), i32_subtract); + }, + subtraction_vector3_scalar: () => { + return generateVectorI32BinaryToVectorCases(vectorI32Range(3), sparseI32Range(), i32_subtract); + }, + subtraction_vector4_scalar: () => { + return generateVectorI32BinaryToVectorCases(vectorI32Range(4), sparseI32Range(), i32_subtract); + }, + multiplication_scalar_vector2: () => { + return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(2), i32_multiply); + }, + multiplication_scalar_vector3: () => { + return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(3), i32_multiply); + }, + multiplication_scalar_vector4: () => { + return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(4), i32_multiply); + }, + multiplication_vector2_scalar: () => { + return generateVectorI32BinaryToVectorCases(vectorI32Range(2), sparseI32Range(), i32_multiply); + }, + multiplication_vector3_scalar: () => { + return generateVectorI32BinaryToVectorCases(vectorI32Range(3), sparseI32Range(), i32_multiply); + }, + multiplication_vector4_scalar: () => { + return generateVectorI32BinaryToVectorCases(vectorI32Range(4), sparseI32Range(), i32_multiply); + }, + division_scalar_vector2_non_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(2), + i32_divide_non_const + ); + }, + division_scalar_vector3_non_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(3), + i32_divide_non_const + ); + }, + division_scalar_vector4_non_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(4), + i32_divide_non_const + ); + }, + division_vector2_scalar_non_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(2), + sparseI32Range(), + i32_divide_non_const + ); + }, + division_vector3_scalar_non_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(3), + sparseI32Range(), + i32_divide_non_const + ); + }, + division_vector4_scalar_non_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(4), + sparseI32Range(), + i32_divide_non_const + ); + }, + division_scalar_vector2_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(2), + i32_divide_const + ); + }, + division_scalar_vector3_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(3), + i32_divide_const + ); + }, + division_scalar_vector4_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(4), + i32_divide_const + ); + }, + division_vector2_scalar_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(2), + sparseI32Range(), + i32_divide_const + ); + }, + division_vector3_scalar_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(3), + sparseI32Range(), + i32_divide_const + ); + }, + division_vector4_scalar_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(4), + sparseI32Range(), + i32_divide_const + ); + }, + remainder_scalar_vector2_non_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(2), + i32_remainder_non_const + ); + }, + remainder_scalar_vector3_non_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(3), + i32_remainder_non_const + ); + }, + remainder_scalar_vector4_non_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(4), + i32_remainder_non_const + ); + }, + remainder_vector2_scalar_non_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(2), + sparseI32Range(), + i32_remainder_non_const + ); + }, + remainder_vector3_scalar_non_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(3), + sparseI32Range(), + i32_remainder_non_const + ); + }, + remainder_vector4_scalar_non_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(4), + sparseI32Range(), + i32_remainder_non_const + ); + }, + remainder_scalar_vector2_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(2), + i32_remainder_const + ); + }, + remainder_scalar_vector3_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(3), + i32_remainder_const + ); + }, + remainder_scalar_vector4_const: () => { + return generateI32VectorBinaryToVectorCases( + sparseI32Range(), + vectorI32Range(4), + i32_remainder_const + ); + }, + remainder_vector2_scalar_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(2), + sparseI32Range(), + i32_remainder_const + ); + }, + remainder_vector3_scalar_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(3), + sparseI32Range(), + i32_remainder_const + ); + }, + remainder_vector4_scalar_const: () => { + return generateVectorI32BinaryToVectorCases( + vectorI32Range(4), + sparseI32Range(), + i32_remainder_const + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts index e9b7a2407fb2..ef201f66689f 100644 --- a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts @@ -4,320 +4,14 @@ Execution Tests for the i32 arithmetic binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { kValue } from '../../../../util/constants.js'; import { TypeI32, TypeVec } from '../../../../util/conversion.js'; -import { sparseI32Range, vectorI32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { - allInputSources, - generateBinaryToI32Cases, - generateI32VectorBinaryToVectorCases, - generateVectorI32BinaryToVectorCases, - run, -} from '../expression.js'; +import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -function i32_add(x: number, y: number): number | undefined { - return x + y; -} - -function i32_subtract(x: number, y: number): number | undefined { - return x - y; -} - -function i32_multiply(x: number, y: number): number | undefined { - return Math.imul(x, y); -} - -function i32_divide_non_const(x: number, y: number): number | undefined { - if (y === 0) { - return x; - } - if (x === kValue.i32.negative.min && y === -1) { - return x; - } - return x / y; -} - -function i32_divide_const(x: number, y: number): number | undefined { - if (y === 0) { - return undefined; - } - if (x === kValue.i32.negative.min && y === -1) { - return undefined; - } - return x / y; -} - -function i32_remainder_non_const(x: number, y: number): number | undefined { - if (y === 0) { - return 0; - } - if (x === kValue.i32.negative.min && y === -1) { - return 0; - } - return x % y; -} - -function i32_remainder_const(x: number, y: number): number | undefined { - if (y === 0) { - return undefined; - } - if (x === kValue.i32.negative.min && y === -1) { - return undefined; - } - return x % y; -} +import { d } from './i32_arithmetic.cache.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('binary/i32_arithmetic', { - addition: () => { - return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_add); - }, - subtraction: () => { - return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_subtract); - }, - multiplication: () => { - return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_multiply); - }, - division_non_const: () => { - return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_divide_non_const); - }, - division_const: () => { - return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_divide_const); - }, - remainder_non_const: () => { - return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_remainder_non_const); - }, - remainder_const: () => { - return generateBinaryToI32Cases(sparseI32Range(), sparseI32Range(), i32_remainder_const); - }, - addition_scalar_vector2: () => { - return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(2), i32_add); - }, - addition_scalar_vector3: () => { - return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(3), i32_add); - }, - addition_scalar_vector4: () => { - return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(4), i32_add); - }, - addition_vector2_scalar: () => { - return generateVectorI32BinaryToVectorCases(vectorI32Range(2), sparseI32Range(), i32_add); - }, - addition_vector3_scalar: () => { - return generateVectorI32BinaryToVectorCases(vectorI32Range(3), sparseI32Range(), i32_add); - }, - addition_vector4_scalar: () => { - return generateVectorI32BinaryToVectorCases(vectorI32Range(4), sparseI32Range(), i32_add); - }, - subtraction_scalar_vector2: () => { - return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(2), i32_subtract); - }, - subtraction_scalar_vector3: () => { - return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(3), i32_subtract); - }, - subtraction_scalar_vector4: () => { - return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(4), i32_subtract); - }, - subtraction_vector2_scalar: () => { - return generateVectorI32BinaryToVectorCases(vectorI32Range(2), sparseI32Range(), i32_subtract); - }, - subtraction_vector3_scalar: () => { - return generateVectorI32BinaryToVectorCases(vectorI32Range(3), sparseI32Range(), i32_subtract); - }, - subtraction_vector4_scalar: () => { - return generateVectorI32BinaryToVectorCases(vectorI32Range(4), sparseI32Range(), i32_subtract); - }, - multiplication_scalar_vector2: () => { - return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(2), i32_multiply); - }, - multiplication_scalar_vector3: () => { - return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(3), i32_multiply); - }, - multiplication_scalar_vector4: () => { - return generateI32VectorBinaryToVectorCases(sparseI32Range(), vectorI32Range(4), i32_multiply); - }, - multiplication_vector2_scalar: () => { - return generateVectorI32BinaryToVectorCases(vectorI32Range(2), sparseI32Range(), i32_multiply); - }, - multiplication_vector3_scalar: () => { - return generateVectorI32BinaryToVectorCases(vectorI32Range(3), sparseI32Range(), i32_multiply); - }, - multiplication_vector4_scalar: () => { - return generateVectorI32BinaryToVectorCases(vectorI32Range(4), sparseI32Range(), i32_multiply); - }, - division_scalar_vector2_non_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(2), - i32_divide_non_const - ); - }, - division_scalar_vector3_non_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(3), - i32_divide_non_const - ); - }, - division_scalar_vector4_non_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(4), - i32_divide_non_const - ); - }, - division_vector2_scalar_non_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(2), - sparseI32Range(), - i32_divide_non_const - ); - }, - division_vector3_scalar_non_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(3), - sparseI32Range(), - i32_divide_non_const - ); - }, - division_vector4_scalar_non_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(4), - sparseI32Range(), - i32_divide_non_const - ); - }, - division_scalar_vector2_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(2), - i32_divide_const - ); - }, - division_scalar_vector3_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(3), - i32_divide_const - ); - }, - division_scalar_vector4_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(4), - i32_divide_const - ); - }, - division_vector2_scalar_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(2), - sparseI32Range(), - i32_divide_const - ); - }, - division_vector3_scalar_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(3), - sparseI32Range(), - i32_divide_const - ); - }, - division_vector4_scalar_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(4), - sparseI32Range(), - i32_divide_const - ); - }, - remainder_scalar_vector2_non_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(2), - i32_remainder_non_const - ); - }, - remainder_scalar_vector3_non_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(3), - i32_remainder_non_const - ); - }, - remainder_scalar_vector4_non_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(4), - i32_remainder_non_const - ); - }, - remainder_vector2_scalar_non_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(2), - sparseI32Range(), - i32_remainder_non_const - ); - }, - remainder_vector3_scalar_non_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(3), - sparseI32Range(), - i32_remainder_non_const - ); - }, - remainder_vector4_scalar_non_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(4), - sparseI32Range(), - i32_remainder_non_const - ); - }, - remainder_scalar_vector2_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(2), - i32_remainder_const - ); - }, - remainder_scalar_vector3_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(3), - i32_remainder_const - ); - }, - remainder_scalar_vector4_const: () => { - return generateI32VectorBinaryToVectorCases( - sparseI32Range(), - vectorI32Range(4), - i32_remainder_const - ); - }, - remainder_vector2_scalar_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(2), - sparseI32Range(), - i32_remainder_const - ); - }, - remainder_vector3_scalar_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(3), - sparseI32Range(), - i32_remainder_const - ); - }, - remainder_vector4_scalar_const: () => { - return generateVectorI32BinaryToVectorCases( - vectorI32Range(4), - sparseI32Range(), - i32_remainder_const - ); - }, -}); - g.test('addition') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/i32_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/i32_comparison.cache.ts new file mode 100644 index 000000000000..de0a6de477c3 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/i32_comparison.cache.ts @@ -0,0 +1,21 @@ +import { bool, i32 } from '../../../../util/conversion.js'; +import { vectorI32Range } from '../../../../util/math.js'; +import { Case } from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +/** + * @returns a test case for the provided left hand & right hand values and + * expected boolean result. + */ +function makeCase(lhs: number, rhs: number, expected_answer: boolean): Case { + return { input: [i32(lhs), i32(rhs)], expected: bool(expected_answer) }; +} + +export const d = makeCaseCache('binary/i32_comparison', { + equals: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] === v[1])), + not_equals: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] !== v[1])), + less_than: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] < v[1])), + less_equal: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] <= v[1])), + greater_than: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] > v[1])), + greater_equal: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] >= v[1])), +}); diff --git a/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts index dce1a2519ead..c07d09a058ed 100644 --- a/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts @@ -4,32 +4,14 @@ Execution Tests for the i32 comparison expressions import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { i32, bool, TypeBool, TypeI32 } from '../../../../util/conversion.js'; -import { vectorI32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, Case, run } from '../expression.js'; +import { TypeBool, TypeI32 } from '../../../../util/conversion.js'; +import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; +import { d } from './i32_comparison.cache.js'; export const g = makeTestGroup(GPUTest); -/** - * @returns a test case for the provided left hand & right hand values and - * expected boolean result. - */ -function makeCase(lhs: number, rhs: number, expected_answer: boolean): Case { - return { input: [i32(lhs), i32(rhs)], expected: bool(expected_answer) }; -} - -export const d = makeCaseCache('binary/i32_comparison', { - equals: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] === v[1])), - not_equals: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] !== v[1])), - less_than: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] < v[1])), - less_equal: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] <= v[1])), - greater_than: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] > v[1])), - greater_equal: () => vectorI32Range(2).map(v => makeCase(v[0], v[1], v[0] >= v[1])), -}); - g.test('equals') .specURL('https://www.w3.org/TR/WGSL/#comparison-expr') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/u32_arithmetic.cache.ts b/src/webgpu/shader/execution/expression/binary/u32_arithmetic.cache.ts new file mode 100644 index 000000000000..91be35a1eeff --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/u32_arithmetic.cache.ts @@ -0,0 +1,293 @@ +import { sparseU32Range, vectorU32Range } from '../../../../util/math.js'; +import { + generateBinaryToU32Cases, + generateU32VectorBinaryToVectorCases, + generateVectorU32BinaryToVectorCases, +} from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +function u32_add(x: number, y: number): number | undefined { + return x + y; +} + +function u32_subtract(x: number, y: number): number | undefined { + return x - y; +} + +function u32_multiply(x: number, y: number): number | undefined { + return Math.imul(x, y); +} + +function u32_divide_non_const(x: number, y: number): number | undefined { + if (y === 0) { + return x; + } + return x / y; +} + +function u32_divide_const(x: number, y: number): number | undefined { + if (y === 0) { + return undefined; + } + return x / y; +} + +function u32_remainder_non_const(x: number, y: number): number | undefined { + if (y === 0) { + return 0; + } + return x % y; +} + +function u32_remainder_const(x: number, y: number): number | undefined { + if (y === 0) { + return undefined; + } + return x % y; +} + +export const d = makeCaseCache('binary/u32_arithmetic', { + addition: () => { + return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_add); + }, + subtraction: () => { + return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_subtract); + }, + multiplication: () => { + return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_multiply); + }, + division_non_const: () => { + return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_divide_non_const); + }, + division_const: () => { + return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_divide_const); + }, + remainder_non_const: () => { + return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_remainder_non_const); + }, + remainder_const: () => { + return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_remainder_const); + }, + addition_scalar_vector2: () => { + return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(2), u32_add); + }, + addition_scalar_vector3: () => { + return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(3), u32_add); + }, + addition_scalar_vector4: () => { + return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(4), u32_add); + }, + addition_vector2_scalar: () => { + return generateVectorU32BinaryToVectorCases(vectorU32Range(2), sparseU32Range(), u32_add); + }, + addition_vector3_scalar: () => { + return generateVectorU32BinaryToVectorCases(vectorU32Range(3), sparseU32Range(), u32_add); + }, + addition_vector4_scalar: () => { + return generateVectorU32BinaryToVectorCases(vectorU32Range(4), sparseU32Range(), u32_add); + }, + subtraction_scalar_vector2: () => { + return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(2), u32_subtract); + }, + subtraction_scalar_vector3: () => { + return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(3), u32_subtract); + }, + subtraction_scalar_vector4: () => { + return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(4), u32_subtract); + }, + subtraction_vector2_scalar: () => { + return generateVectorU32BinaryToVectorCases(vectorU32Range(2), sparseU32Range(), u32_subtract); + }, + subtraction_vector3_scalar: () => { + return generateVectorU32BinaryToVectorCases(vectorU32Range(3), sparseU32Range(), u32_subtract); + }, + subtraction_vector4_scalar: () => { + return generateVectorU32BinaryToVectorCases(vectorU32Range(4), sparseU32Range(), u32_subtract); + }, + multiplication_scalar_vector2: () => { + return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(2), u32_multiply); + }, + multiplication_scalar_vector3: () => { + return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(3), u32_multiply); + }, + multiplication_scalar_vector4: () => { + return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(4), u32_multiply); + }, + multiplication_vector2_scalar: () => { + return generateVectorU32BinaryToVectorCases(vectorU32Range(2), sparseU32Range(), u32_multiply); + }, + multiplication_vector3_scalar: () => { + return generateVectorU32BinaryToVectorCases(vectorU32Range(3), sparseU32Range(), u32_multiply); + }, + multiplication_vector4_scalar: () => { + return generateVectorU32BinaryToVectorCases(vectorU32Range(4), sparseU32Range(), u32_multiply); + }, + division_scalar_vector2_non_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(2), + u32_divide_non_const + ); + }, + division_scalar_vector3_non_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(3), + u32_divide_non_const + ); + }, + division_scalar_vector4_non_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(4), + u32_divide_non_const + ); + }, + division_vector2_scalar_non_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(2), + sparseU32Range(), + u32_divide_non_const + ); + }, + division_vector3_scalar_non_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(3), + sparseU32Range(), + u32_divide_non_const + ); + }, + division_vector4_scalar_non_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(4), + sparseU32Range(), + u32_divide_non_const + ); + }, + division_scalar_vector2_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(2), + u32_divide_const + ); + }, + division_scalar_vector3_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(3), + u32_divide_const + ); + }, + division_scalar_vector4_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(4), + u32_divide_const + ); + }, + division_vector2_scalar_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(2), + sparseU32Range(), + u32_divide_const + ); + }, + division_vector3_scalar_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(3), + sparseU32Range(), + u32_divide_const + ); + }, + division_vector4_scalar_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(4), + sparseU32Range(), + u32_divide_const + ); + }, + remainder_scalar_vector2_non_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(2), + u32_remainder_non_const + ); + }, + remainder_scalar_vector3_non_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(3), + u32_remainder_non_const + ); + }, + remainder_scalar_vector4_non_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(4), + u32_remainder_non_const + ); + }, + remainder_vector2_scalar_non_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(2), + sparseU32Range(), + u32_remainder_non_const + ); + }, + remainder_vector3_scalar_non_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(3), + sparseU32Range(), + u32_remainder_non_const + ); + }, + remainder_vector4_scalar_non_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(4), + sparseU32Range(), + u32_remainder_non_const + ); + }, + remainder_scalar_vector2_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(2), + u32_remainder_const + ); + }, + remainder_scalar_vector3_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(3), + u32_remainder_const + ); + }, + remainder_scalar_vector4_const: () => { + return generateU32VectorBinaryToVectorCases( + sparseU32Range(), + vectorU32Range(4), + u32_remainder_const + ); + }, + remainder_vector2_scalar_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(2), + sparseU32Range(), + u32_remainder_const + ); + }, + remainder_vector3_scalar_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(3), + sparseU32Range(), + u32_remainder_const + ); + }, + remainder_vector4_scalar_const: () => { + return generateVectorU32BinaryToVectorCases( + vectorU32Range(4), + sparseU32Range(), + u32_remainder_const + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts index 88667e823386..28c25fef8dc9 100644 --- a/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts @@ -5,306 +5,13 @@ Execution Tests for the u32 arithmetic binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeU32, TypeVec } from '../../../../util/conversion.js'; -import { sparseU32Range, vectorU32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { - allInputSources, - generateBinaryToU32Cases, - generateU32VectorBinaryToVectorCases, - generateVectorU32BinaryToVectorCases, - run, -} from '../expression.js'; +import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; - -function u32_add(x: number, y: number): number | undefined { - return x + y; -} - -function u32_subtract(x: number, y: number): number | undefined { - return x - y; -} - -function u32_multiply(x: number, y: number): number | undefined { - return Math.imul(x, y); -} - -function u32_divide_non_const(x: number, y: number): number | undefined { - if (y === 0) { - return x; - } - return x / y; -} - -function u32_divide_const(x: number, y: number): number | undefined { - if (y === 0) { - return undefined; - } - return x / y; -} - -function u32_remainder_non_const(x: number, y: number): number | undefined { - if (y === 0) { - return 0; - } - return x % y; -} - -function u32_remainder_const(x: number, y: number): number | undefined { - if (y === 0) { - return undefined; - } - return x % y; -} +import { d } from './u32_arithmetic.cache.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('binary/u32_arithmetic', { - addition: () => { - return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_add); - }, - subtraction: () => { - return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_subtract); - }, - multiplication: () => { - return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_multiply); - }, - division_non_const: () => { - return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_divide_non_const); - }, - division_const: () => { - return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_divide_const); - }, - remainder_non_const: () => { - return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_remainder_non_const); - }, - remainder_const: () => { - return generateBinaryToU32Cases(sparseU32Range(), sparseU32Range(), u32_remainder_const); - }, - addition_scalar_vector2: () => { - return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(2), u32_add); - }, - addition_scalar_vector3: () => { - return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(3), u32_add); - }, - addition_scalar_vector4: () => { - return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(4), u32_add); - }, - addition_vector2_scalar: () => { - return generateVectorU32BinaryToVectorCases(vectorU32Range(2), sparseU32Range(), u32_add); - }, - addition_vector3_scalar: () => { - return generateVectorU32BinaryToVectorCases(vectorU32Range(3), sparseU32Range(), u32_add); - }, - addition_vector4_scalar: () => { - return generateVectorU32BinaryToVectorCases(vectorU32Range(4), sparseU32Range(), u32_add); - }, - subtraction_scalar_vector2: () => { - return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(2), u32_subtract); - }, - subtraction_scalar_vector3: () => { - return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(3), u32_subtract); - }, - subtraction_scalar_vector4: () => { - return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(4), u32_subtract); - }, - subtraction_vector2_scalar: () => { - return generateVectorU32BinaryToVectorCases(vectorU32Range(2), sparseU32Range(), u32_subtract); - }, - subtraction_vector3_scalar: () => { - return generateVectorU32BinaryToVectorCases(vectorU32Range(3), sparseU32Range(), u32_subtract); - }, - subtraction_vector4_scalar: () => { - return generateVectorU32BinaryToVectorCases(vectorU32Range(4), sparseU32Range(), u32_subtract); - }, - multiplication_scalar_vector2: () => { - return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(2), u32_multiply); - }, - multiplication_scalar_vector3: () => { - return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(3), u32_multiply); - }, - multiplication_scalar_vector4: () => { - return generateU32VectorBinaryToVectorCases(sparseU32Range(), vectorU32Range(4), u32_multiply); - }, - multiplication_vector2_scalar: () => { - return generateVectorU32BinaryToVectorCases(vectorU32Range(2), sparseU32Range(), u32_multiply); - }, - multiplication_vector3_scalar: () => { - return generateVectorU32BinaryToVectorCases(vectorU32Range(3), sparseU32Range(), u32_multiply); - }, - multiplication_vector4_scalar: () => { - return generateVectorU32BinaryToVectorCases(vectorU32Range(4), sparseU32Range(), u32_multiply); - }, - division_scalar_vector2_non_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(2), - u32_divide_non_const - ); - }, - division_scalar_vector3_non_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(3), - u32_divide_non_const - ); - }, - division_scalar_vector4_non_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(4), - u32_divide_non_const - ); - }, - division_vector2_scalar_non_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(2), - sparseU32Range(), - u32_divide_non_const - ); - }, - division_vector3_scalar_non_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(3), - sparseU32Range(), - u32_divide_non_const - ); - }, - division_vector4_scalar_non_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(4), - sparseU32Range(), - u32_divide_non_const - ); - }, - division_scalar_vector2_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(2), - u32_divide_const - ); - }, - division_scalar_vector3_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(3), - u32_divide_const - ); - }, - division_scalar_vector4_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(4), - u32_divide_const - ); - }, - division_vector2_scalar_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(2), - sparseU32Range(), - u32_divide_const - ); - }, - division_vector3_scalar_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(3), - sparseU32Range(), - u32_divide_const - ); - }, - division_vector4_scalar_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(4), - sparseU32Range(), - u32_divide_const - ); - }, - remainder_scalar_vector2_non_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(2), - u32_remainder_non_const - ); - }, - remainder_scalar_vector3_non_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(3), - u32_remainder_non_const - ); - }, - remainder_scalar_vector4_non_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(4), - u32_remainder_non_const - ); - }, - remainder_vector2_scalar_non_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(2), - sparseU32Range(), - u32_remainder_non_const - ); - }, - remainder_vector3_scalar_non_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(3), - sparseU32Range(), - u32_remainder_non_const - ); - }, - remainder_vector4_scalar_non_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(4), - sparseU32Range(), - u32_remainder_non_const - ); - }, - remainder_scalar_vector2_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(2), - u32_remainder_const - ); - }, - remainder_scalar_vector3_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(3), - u32_remainder_const - ); - }, - remainder_scalar_vector4_const: () => { - return generateU32VectorBinaryToVectorCases( - sparseU32Range(), - vectorU32Range(4), - u32_remainder_const - ); - }, - remainder_vector2_scalar_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(2), - sparseU32Range(), - u32_remainder_const - ); - }, - remainder_vector3_scalar_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(3), - sparseU32Range(), - u32_remainder_const - ); - }, - remainder_vector4_scalar_const: () => { - return generateVectorU32BinaryToVectorCases( - vectorU32Range(4), - sparseU32Range(), - u32_remainder_const - ); - }, -}); - g.test('addition') .specURL('https://www.w3.org/TR/WGSL/#arithmetic-expr') .desc( diff --git a/src/webgpu/shader/execution/expression/binary/u32_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/u32_comparison.cache.ts new file mode 100644 index 000000000000..93b05004964d --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/u32_comparison.cache.ts @@ -0,0 +1,21 @@ +import { bool, u32 } from '../../../../util/conversion.js'; +import { vectorU32Range } from '../../../../util/math.js'; +import { Case } from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +/** + * @returns a test case for the provided left hand & right hand values and + * expected boolean result. + */ +function makeCase(lhs: number, rhs: number, expected_answer: boolean): Case { + return { input: [u32(lhs), u32(rhs)], expected: bool(expected_answer) }; +} + +export const d = makeCaseCache('binary/u32_comparison', { + equals: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] === v[1])), + not_equals: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] !== v[1])), + less_than: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] < v[1])), + less_equal: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] <= v[1])), + greater_than: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] > v[1])), + greater_equal: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] >= v[1])), +}); diff --git a/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts index 1f693da5fd76..fbf34b510394 100644 --- a/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts @@ -4,32 +4,14 @@ Execution Tests for the u32 comparison expressions import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { u32, bool, TypeBool, TypeU32 } from '../../../../util/conversion.js'; -import { vectorU32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, Case, run } from '../expression.js'; +import { TypeBool, TypeU32 } from '../../../../util/conversion.js'; +import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; +import { d } from './u32_comparison.cache.js'; export const g = makeTestGroup(GPUTest); -/** - * @returns a test case for the provided left hand & right hand values and - * expected boolean result. - */ -function makeCase(lhs: number, rhs: number, expected_answer: boolean): Case { - return { input: [u32(lhs), u32(rhs)], expected: bool(expected_answer) }; -} - -export const d = makeCaseCache('binary/u32_comparison', { - equals: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] === v[1])), - not_equals: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] !== v[1])), - less_than: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] < v[1])), - less_equal: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] <= v[1])), - greater_than: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] > v[1])), - greater_equal: () => vectorU32Range(2).map(v => makeCase(v[0], v[1], v[0] >= v[1])), -}); - g.test('equals') .specURL('https://www.w3.org/TR/WGSL/#comparison-expr') .desc( diff --git a/src/webgpu/shader/execution/expression/call/builtin/abs.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/abs.cache.ts new file mode 100644 index 000000000000..caed2f469678 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/abs.cache.ts @@ -0,0 +1,17 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract] +const cases = (['f32', 'f16', 'abstract'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + 'unfiltered', + FP[trait].absInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('abs', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts index 8f89b1157c4c..b0736cb22fb6 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts @@ -27,29 +27,13 @@ import { u32Bits, TypeAbstractFloat, } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; +import { d } from './abs.cache.js'; import { abstractBuiltin, builtin } from './builtin.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract] -const cases = (['f32', 'f16', 'abstract'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - 'unfiltered', - FP[trait].absInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('abs', cases); - g.test('abstract_int') .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') .desc(`abstract int tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/acos.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/acos.cache.ts new file mode 100644 index 000000000000..efe44dcc0c1d --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/acos.cache.ts @@ -0,0 +1,20 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [...linearRange(-1, 1, 100), ...FP[trait].scalarRange()], // acos is defined on [-1, 1] + nonConst ? 'unfiltered' : 'finite', + FP[trait].acosInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('acos', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts index 5c865cbd48a4..1d489e1191b8 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts @@ -10,32 +10,13 @@ Returns the arc cosine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, run } from '../../expression.js'; +import { d } from './acos.cache.js'; import { builtin } from './builtin.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [...linearRange(-1, 1, 100), ...FP[trait].scalarRange()], // acos is defined on [-1, 1] - nonConst ? 'unfiltered' : 'finite', - FP[trait].acosInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('acos', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/acosh.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/acosh.cache.ts new file mode 100644 index 000000000000..223f4c96f9b8 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/acosh.cache.ts @@ -0,0 +1,20 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { biasedRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [...biasedRange(1, 2, 100), ...FP[trait].scalarRange()], // x near 1 can be problematic to implement + nonConst ? 'unfiltered' : 'finite', + ...FP[trait].acoshIntervals + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('acosh', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts index e5c5eaa6d590..737bd5677241 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts @@ -13,33 +13,14 @@ Note: The result is not mathematically meaningful when e < 1. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { biasedRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; +import { d } from './acosh.cache.js'; import { builtin } from './builtin.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [...biasedRange(1, 2, 100), ...FP[trait].scalarRange()], // x near 1 can be problematic to implement - nonConst ? 'unfiltered' : 'finite', - ...FP[trait].acoshIntervals - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('acosh', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/asin.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/asin.cache.ts new file mode 100644 index 000000000000..8b71713ac6a3 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/asin.cache.ts @@ -0,0 +1,20 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [...linearRange(-1, 1, 100), ...FP[trait].scalarRange()], // asin is defined on [-1, 1] + nonConst ? 'unfiltered' : 'finite', + FP[trait].asinInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('asin', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts index add7f069d1c9..8a7934936030 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts @@ -9,33 +9,14 @@ Returns the arc sine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; +import { d } from './asin.cache.js'; import { builtin } from './builtin.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [...linearRange(-1, 1, 100), ...FP[trait].scalarRange()], // asin is defined on [-1, 1] - nonConst ? 'unfiltered' : 'finite', - FP[trait].asinInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('asin', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/asinh.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/asinh.cache.ts new file mode 100644 index 000000000000..1a42182b97bc --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/asinh.cache.ts @@ -0,0 +1,17 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16] +const cases = (['f32', 'f16'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + 'unfiltered', + FP[trait].asinhInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('asinh', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts index b25edd5f9e67..b5f362066b85 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts @@ -12,30 +12,14 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; +import { d } from './asinh.cache.js'; import { builtin } from './builtin.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - 'unfiltered', - FP[trait].asinhInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('asinh', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float test`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/atan.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/atan.cache.ts new file mode 100644 index 000000000000..ce2a4e688cfe --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/atan.cache.ts @@ -0,0 +1,21 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +const known_values = [-Math.sqrt(3), -1, -1 / Math.sqrt(3), 0, 1, 1 / Math.sqrt(3), Math.sqrt(3)]; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [...known_values, ...FP[trait].scalarRange()], + nonConst ? 'unfiltered' : 'finite', + FP[trait].atanInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('atan', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts index 2b0a50727640..173fb0a0010e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts @@ -10,34 +10,14 @@ Returns the arc tangent of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; +import { d } from './atan.cache.js'; import { builtin } from './builtin.js'; export const g = makeTestGroup(GPUTest); -const known_values = [-Math.sqrt(3), -1, -1 / Math.sqrt(3), 0, 1, 1 / Math.sqrt(3), Math.sqrt(3)]; - -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [...known_values, ...FP[trait].scalarRange()], - nonConst ? 'unfiltered' : 'finite', - FP[trait].atanInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('atan', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/atan2.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/atan2.cache.ts new file mode 100644 index 000000000000..b8174f647c7b --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/atan2.cache.ts @@ -0,0 +1,27 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(kind => + ([true, false] as const).map(nonConst => ({ + [`${kind}_${nonConst ? 'non_const' : 'const'}`]: () => { + // Using sparse range since there are N^2 cases being generated, and also including extra values + // around 0, where there is a discontinuity that implementations may behave badly at. + const numeric_range = [ + ...FP[kind].sparseScalarRange(), + ...linearRange(FP[kind].constants().negative.max, FP[kind].constants().positive.min, 10), + ]; + return FP[kind].generateScalarPairToIntervalCases( + numeric_range, + numeric_range, + nonConst ? 'unfiltered' : 'finite', + FP[kind].atan2Interval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('atan2', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts index 69eb08791e10..dff1d442f56b 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts @@ -9,40 +9,14 @@ Returns the arc tangent of e1 over e2. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; +import { d } from './atan2.cache.js'; import { builtin } from './builtin.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(kind => - ([true, false] as const).map(nonConst => ({ - [`${kind}_${nonConst ? 'non_const' : 'const'}`]: () => { - // Using sparse range since there are N^2 cases being generated, and also including extra values - // around 0, where there is a discontinuity that implementations may behave badly at. - const numeric_range = [ - ...FP[kind].sparseScalarRange(), - ...linearRange(FP[kind].constants().negative.max, FP[kind].constants().positive.min, 10), - ]; - return FP[kind].generateScalarPairToIntervalCases( - numeric_range, - numeric_range, - nonConst ? 'unfiltered' : 'finite', - FP[kind].atan2Interval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('atan2', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/atanh.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/atanh.cache.ts new file mode 100644 index 000000000000..b33fc4cdb8db --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/atanh.cache.ts @@ -0,0 +1,28 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { biasedRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [ + // discontinuity at x = -1 + ...biasedRange(FP[trait].constants().negative.less_than_one, -0.9, 20), + -1, + // discontinuity at x = 1 + ...biasedRange(FP[trait].constants().positive.less_than_one, 0.9, 20), + 1, + ...FP[trait].scalarRange(), + ], + nonConst ? 'unfiltered' : 'finite', + FP[trait].atanhInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('atanh', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts index e0b88f9efa96..31c14110b662 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts @@ -12,41 +12,14 @@ Note: The result is not mathematically meaningful when abs(e) >= 1. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { biasedRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; +import { d } from './atanh.cache.js'; import { builtin } from './builtin.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [ - // discontinuity at x = -1 - ...biasedRange(FP[trait].constants().negative.less_than_one, -0.9, 20), - -1, - // discontinuity at x = 1 - ...biasedRange(FP[trait].constants().positive.less_than_one, 0.9, 20), - 1, - ...FP[trait].scalarRange(), - ], - nonConst ? 'unfiltered' : 'finite', - FP[trait].atanhInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('atanh', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts new file mode 100644 index 000000000000..60fbc2b03ec8 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts @@ -0,0 +1,797 @@ +import { assert } from '../../../../../../common/util/util.js'; +import { Comparator, alwaysPass, anyOf } from '../../../../../util/compare.js'; +import { kBit, kValue } from '../../../../../util/constants.js'; +import { Scalar, Vector, f16, f32, i32, toVector, u32 } from '../../../../../util/conversion.js'; +import { FP, FPInterval } from '../../../../../util/floating_point.js'; +import { + cartesianProduct, + fullI32Range, + fullU32Range, + isFiniteF16, + isFiniteF32, + isSubnormalNumberF16, + isSubnormalNumberF32, + linearRange, + scalarF16Range, + scalarF32Range, +} from '../../../../../util/math.js'; +import { + reinterpretF16AsU16, + reinterpretF32AsI32, + reinterpretF32AsU32, + reinterpretI32AsF32, + reinterpretI32AsU32, + reinterpretU16AsF16, + reinterpretU32AsF32, + reinterpretU32AsI32, +} from '../../../../../util/reinterpret.js'; +import { makeCaseCache } from '../../case_cache.js'; + +const numNaNs = 11; +const f32InfAndNaNInU32: number[] = [ + // Cover NaNs evenly in integer space. + // The positive NaN with the lowest integer representation is the integer + // for infinity, plus one. + // The positive NaN with the highest integer representation is i32.max (!) + ...linearRange(kBit.f32.positive.infinity + 1, kBit.i32.positive.max, numNaNs), + // The negative NaN with the lowest integer representation is the integer + // for negative infinity, plus one. + // The negative NaN with the highest integer representation is u32.max (!) + ...linearRange(kBit.f32.negative.infinity + 1, kBit.u32.max, numNaNs), + kBit.f32.positive.infinity, + kBit.f32.negative.infinity, +]; +const f32InfAndNaNInF32 = f32InfAndNaNInU32.map(u => reinterpretU32AsF32(u)); +const f32InfAndNaNInI32 = f32InfAndNaNInU32.map(u => reinterpretU32AsI32(u)); + +const f32ZerosInU32 = [0, kBit.f32.negative.zero]; +const f32ZerosInF32 = f32ZerosInU32.map(u => reinterpretU32AsF32(u)); +const f32ZerosInI32 = f32ZerosInU32.map(u => reinterpretU32AsI32(u)); +const f32ZerosInterval: FPInterval = new FPInterval('f32', -0.0, 0.0); + +// f32FiniteRange is a list of finite f32s. fullF32Range() already +// has +0, we only need to add -0. +const f32FiniteRange: number[] = [...scalarF32Range(), kValue.f32.negative.zero]; +const f32RangeWithInfAndNaN: number[] = [...f32FiniteRange, ...f32InfAndNaNInF32]; + +// F16 values, finite, Inf/NaN, and zeros. Represented in float and u16. +const f16FiniteInF16: number[] = [...scalarF16Range(), kValue.f16.negative.zero]; +const f16FiniteInU16: number[] = f16FiniteInF16.map(u => reinterpretF16AsU16(u)); + +const f16InfAndNaNInU16: number[] = [ + // Cover NaNs evenly in integer space. + // The positive NaN with the lowest integer representation is the integer + // for infinity, plus one. + // The positive NaN with the highest integer representation is u16 0x7fff i.e. 32767. + ...linearRange(kBit.f16.positive.infinity + 1, 32767, numNaNs).map(v => Math.ceil(v)), + // The negative NaN with the lowest integer representation is the integer + // for negative infinity, plus one. + // The negative NaN with the highest integer representation is u16 0xffff i.e. 65535 + ...linearRange(kBit.f16.negative.infinity + 1, 65535, numNaNs).map(v => Math.floor(v)), + kBit.f16.positive.infinity, + kBit.f16.negative.infinity, +]; +const f16InfAndNaNInF16 = f16InfAndNaNInU16.map(u => reinterpretU16AsF16(u)); + +const f16ZerosInU16 = [kBit.f16.negative.zero, 0]; + +// f16 interval that match +/-0.0. +const f16ZerosInterval: FPInterval = new FPInterval('f16', -0.0, 0.0); + +/** + * @returns an u32 whose lower and higher 16bits are the two elements of the + * given array of two u16 respectively, in little-endian. + */ +function u16x2ToU32(u16x2: readonly number[]): number { + assert(u16x2.length === 2); + // Create a DataView with 4 bytes buffer. + const buffer = new ArrayBuffer(4); + const view = new DataView(buffer); + // Enforce little-endian. + view.setUint16(0, u16x2[0], true); + view.setUint16(2, u16x2[1], true); + return view.getUint32(0, true); +} + +/** + * @returns an array of two u16, respectively the lower and higher 16bits of + * given u32 in little-endian. + */ +function u32ToU16x2(u32: number): number[] { + // Create a DataView with 4 bytes buffer. + const buffer = new ArrayBuffer(4); + const view = new DataView(buffer); + // Enforce little-endian. + view.setUint32(0, u32, true); + return [view.getUint16(0, true), view.getUint16(2, true)]; +} + +/** + * @returns a vec2 from an array of two u16, each reinterpreted as f16. + */ +function u16x2ToVec2F16(u16x2: number[]): Vector { + assert(u16x2.length === 2); + return toVector(u16x2.map(reinterpretU16AsF16), f16); +} + +/** + * @returns a vec4 from an array of four u16, each reinterpreted as f16. + */ +function u16x4ToVec4F16(u16x4: number[]): Vector { + assert(u16x4.length === 4); + return toVector(u16x4.map(reinterpretU16AsF16), f16); +} + +/** + * @returns true if and only if a given u32 can bitcast to a vec2 with all elements + * being finite f16 values. + */ +function canU32BitcastToFiniteVec2F16(u32: number): boolean { + return u32ToU16x2(u32) + .map(u16 => isFiniteF16(reinterpretU16AsF16(u16))) + .reduce((a, b) => a && b, true); +} + +/** + * @returns an array of N elements with the i-th element being an array of len elements + * [a_i, a_((i+1)%N), ..., a_((i+len-1)%N)], for the input array of N element [a_1, ... a_N] + * and the given len. For example, slidingSlice([1, 2, 3], 2) result in + * [[1, 2], [2, 3], [3, 1]]. + * This helper function is used for generating vector cases from scalar values array. + */ +function slidingSlice(input: number[], len: number) { + const result: number[][] = []; + for (let i = 0; i < input.length; i++) { + const sub: number[] = []; + for (let j = 0; j < len; j++) { + sub.push(input[(i + j) % input.length]); + } + result.push(sub); + } + return result; +} + +// vec2 interesting (zeros, Inf, and NaN) values for testing cases. +// vec2 values that has at least one Inf/NaN f16 element, reinterpreted as u32/i32. +const f16Vec2InfAndNaNInU32 = [ + ...cartesianProduct(f16InfAndNaNInU16, [...f16InfAndNaNInU16, ...f16FiniteInU16]), + ...cartesianProduct(f16FiniteInU16, f16InfAndNaNInU16), +].map(u16x2ToU32); +const f16Vec2InfAndNaNInI32 = f16Vec2InfAndNaNInU32.map(u => reinterpretU32AsI32(u)); +// vec2 values with two f16 0.0 element, reinterpreted as u32/i32. +const f16Vec2ZerosInU32 = cartesianProduct(f16ZerosInU16, f16ZerosInU16).map(u16x2ToU32); +const f16Vec2ZerosInI32 = f16Vec2ZerosInU32.map(u => reinterpretU32AsI32(u)); + +// i32/u32/f32 range for bitcasting to vec2 +// u32 values for bitcasting to vec2 finite, Inf, and NaN. +const u32RangeForF16Vec2FiniteInfNaN: number[] = [ + ...fullU32Range(), + ...f16Vec2ZerosInU32, + ...f16Vec2InfAndNaNInU32, +]; +// u32 values for bitcasting to finite only vec2, used for constant evaluation. +const u32RangeForF16Vec2Finite: number[] = u32RangeForF16Vec2FiniteInfNaN.filter( + canU32BitcastToFiniteVec2F16 +); +// i32 values for bitcasting to vec2 finite, zeros, Inf, and NaN. +const i32RangeForF16Vec2FiniteInfNaN: number[] = [ + ...fullI32Range(), + ...f16Vec2ZerosInI32, + ...f16Vec2InfAndNaNInI32, +]; +// i32 values for bitcasting to finite only vec2, used for constant evaluation. +const i32RangeForF16Vec2Finite: number[] = i32RangeForF16Vec2FiniteInfNaN.filter(u => + canU32BitcastToFiniteVec2F16(reinterpretI32AsU32(u)) +); +// f32 values with finite/Inf/NaN f32, for bitcasting to vec2 finite, zeros, Inf, and NaN. +const f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN: number[] = [ + ...f32RangeWithInfAndNaN, + ...u32RangeForF16Vec2FiniteInfNaN.map(reinterpretU32AsF32), +]; +// Finite f32 values for bitcasting to finite only vec2, used for constant evaluation. +const f32FiniteRangeForF16Vec2Finite: number[] = f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN + .filter(isFiniteF32) + .filter(u => canU32BitcastToFiniteVec2F16(reinterpretF32AsU32(u))); + +// vec2 cases for bitcasting to i32/u32/f32, by combining f16 values into pairs +const f16Vec2FiniteInU16x2 = slidingSlice(f16FiniteInU16, 2); +const f16Vec2FiniteInfNanInU16x2 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 2); +// vec4 cases for bitcasting to vec2, by combining f16 values 4-by-4 +const f16Vec2FiniteInU16x4 = slidingSlice(f16FiniteInU16, 4); +const f16Vec2FiniteInfNanInU16x4 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 4); + +// alwaysPass comparator for i32/u32/f32 cases. For f32/f16 we also use unbound interval, which +// allow per-element unbounded expectation for vector. +const anyF32 = alwaysPass('any f32'); +const anyI32 = alwaysPass('any i32'); +const anyU32 = alwaysPass('any u32'); + +// Unbounded FPInterval +const f32UnboundedInterval = FP.f32.constants().unboundedInterval; +const f16UnboundedInterval = FP.f16.constants().unboundedInterval; + +// i32 and u32 cases for bitcasting to f32. +// i32 cases for bitcasting to f32 finite, zeros, Inf, and NaN. +const i32RangeForF32FiniteInfNaN: number[] = [ + ...fullI32Range(), + ...f32ZerosInI32, + ...f32InfAndNaNInI32, +]; +// i32 cases for bitcasting to f32 finite only. +const i32RangeForF32Finite: number[] = i32RangeForF32FiniteInfNaN.filter(i => + isFiniteF32(reinterpretI32AsF32(i)) +); +// u32 cases for bitcasting to f32 finite, zeros, Inf, and NaN. +const u32RangeForF32FiniteInfNaN: number[] = [ + ...fullU32Range(), + ...f32ZerosInU32, + ...f32InfAndNaNInU32, +]; +// u32 cases for bitcasting to f32 finite only. +const u32RangeForF32Finite: number[] = u32RangeForF32FiniteInfNaN.filter(u => + isFiniteF32(reinterpretU32AsF32(u)) +); + +/** + * @returns a Comparator for checking if a f32 value is a valid + * bitcast conversion from f32. + */ +function bitcastF32ToF32Comparator(f: number): Comparator { + if (!isFiniteF32(f)) return anyF32; + const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])]; + return anyOf(...acceptable.map(f32)); +} + +/** + * @returns a Comparator for checking if a u32 value is a valid + * bitcast conversion from f32. + */ +function bitcastF32ToU32Comparator(f: number): Comparator { + if (!isFiniteF32(f)) return anyU32; + const acceptable: number[] = [ + reinterpretF32AsU32(f), + ...(isSubnormalNumberF32(f) ? f32ZerosInU32 : []), + ]; + return anyOf(...acceptable.map(u32)); +} + +/** + * @returns a Comparator for checking if a i32 value is a valid + * bitcast conversion from f32. + */ +function bitcastF32ToI32Comparator(f: number): Comparator { + if (!isFiniteF32(f)) return anyI32; + const acceptable: number[] = [ + reinterpretF32AsI32(f), + ...(isSubnormalNumberF32(f) ? f32ZerosInI32 : []), + ]; + return anyOf(...acceptable.map(i32)); +} + +/** + * @returns a Comparator for checking if a f32 value is a valid + * bitcast conversion from i32. + */ +function bitcastI32ToF32Comparator(i: number): Comparator { + const f: number = reinterpretI32AsF32(i); + if (!isFiniteF32(f)) return anyI32; + // Positive or negative zero bit pattern map to any zero. + if (f32ZerosInI32.includes(i)) return anyOf(...f32ZerosInF32.map(f32)); + const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])]; + return anyOf(...acceptable.map(f32)); +} + +/** + * @returns a Comparator for checking if a f32 value is a valid + * bitcast conversion from u32. + */ +function bitcastU32ToF32Comparator(u: number): Comparator { + const f: number = reinterpretU32AsF32(u); + if (!isFiniteF32(f)) return anyU32; + // Positive or negative zero bit pattern map to any zero. + if (f32ZerosInU32.includes(u)) return anyOf(...f32ZerosInF32.map(f32)); + const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])]; + return anyOf(...acceptable.map(f32)); +} + +/** + * @returns an array of expected f16 FPInterval for the given bitcasted f16 value, which may be + * subnormal, Inf, or NaN. Test cases that bitcasted to vector of f16 use this function to get + * per-element expectation and build vector expectation using cartesianProduct. + */ +function generateF16ExpectationIntervals(bitcastedF16Value: number): FPInterval[] { + // If the bitcasted f16 value is inf or nan, the result is unbounded + if (!isFiniteF16(bitcastedF16Value)) { + return [f16UnboundedInterval]; + } + // If the casted f16 value is +/-0.0, the result can be one of both. Note that in JS -0.0 === 0.0. + if (bitcastedF16Value === 0.0) { + return [f16ZerosInterval]; + } + const exactInterval = FP.f16.toInterval(bitcastedF16Value); + // If the casted f16 value is subnormal, it also may be flushed to +/-0.0. + return [exactInterval, ...(isSubnormalNumberF16(bitcastedF16Value) ? [f16ZerosInterval] : [])]; +} + +/** + * @returns a Comparator for checking if a f16 value is a valid + * bitcast conversion from f16. + */ +function bitcastF16ToF16Comparator(f: number): Comparator { + if (!isFiniteF16(f)) return anyOf(f16UnboundedInterval); + return anyOf(...generateF16ExpectationIntervals(f)); +} + +/** + * @returns a Comparator for checking if a vec2 is a valid bitcast + * conversion from u32. + */ +function bitcastU32ToVec2F16Comparator(u: number): Comparator { + const bitcastedVec2F16InU16x2 = u32ToU16x2(u).map(reinterpretU16AsF16); + // Generate expection for vec2 f16 result, by generating expected intervals for each elements and + // then do cartesian product. + const expectedIntervalsCombination = cartesianProduct( + ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals) + ); + return anyOf(...expectedIntervalsCombination); +} + +/** + * @returns a Comparator for checking if a vec2 value is a valid + * bitcast conversion from i32. + */ +function bitcastI32ToVec2F16Comparator(i: number): Comparator { + const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretI32AsU32(i)).map(reinterpretU16AsF16); + // Generate expection for vec2 f16 result, by generating expected intervals for each elements and + // then do cartesian product. + const expectedIntervalsCombination = cartesianProduct( + ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals) + ); + return anyOf(...expectedIntervalsCombination); +} + +/** + * @returns a Comparator for checking if a vec2 value is a valid + * bitcast conversion from f32. + */ +function bitcastF32ToVec2F16Comparator(f: number): Comparator { + // If input f32 is not finite, it can be evaluated to any value and thus any result f16 vec2 is + // possible. + if (!isFiniteF32(f)) { + return anyOf([f16UnboundedInterval, f16UnboundedInterval]); + } + const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretF32AsU32(f)).map(reinterpretU16AsF16); + // Generate expection for vec2 f16 result, by generating expected intervals for each elements and + // then do cartesian product. + const expectedIntervalsCombination = cartesianProduct( + ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals) + ); + return anyOf(...expectedIntervalsCombination); +} + +/** + * @returns a Comparator for checking if a vec4 is a valid + * bitcast conversion from vec2. + */ +function bitcastVec2U32ToVec4F16Comparator(u32x2: number[]): Comparator { + assert(u32x2.length === 2); + const bitcastedVec4F16InU16x4 = u32x2.flatMap(u32ToU16x2).map(reinterpretU16AsF16); + // Generate expection for vec4 f16 result, by generating expected intervals for each elements and + // then do cartesian product. + const expectedIntervalsCombination = cartesianProduct( + ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals) + ); + return anyOf(...expectedIntervalsCombination); +} + +/** + * @returns a Comparator for checking if a vec4 is a valid + * bitcast conversion from vec2. + */ +function bitcastVec2I32ToVec4F16Comparator(i32x2: number[]): Comparator { + assert(i32x2.length === 2); + const bitcastedVec4F16InU16x4 = i32x2 + .map(reinterpretI32AsU32) + .flatMap(u32ToU16x2) + .map(reinterpretU16AsF16); + // Generate expection for vec4 f16 result, by generating expected intervals for each elements and + // then do cartesian product. + const expectedIntervalsCombination = cartesianProduct( + ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals) + ); + return anyOf(...expectedIntervalsCombination); +} + +/** + * @returns a Comparator for checking if a vec4 is a valid + * bitcast conversion from vec2. + */ +function bitcastVec2F32ToVec4F16Comparator(f32x2: number[]): Comparator { + assert(f32x2.length === 2); + const bitcastedVec4F16InU16x4 = f32x2 + .map(reinterpretF32AsU32) + .flatMap(u32ToU16x2) + .map(reinterpretU16AsF16); + // Generate expection for vec4 f16 result, by generating expected intervals for each elements and + // then do cartesian product. + const expectedIntervalsCombination = cartesianProduct( + ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals) + ); + return anyOf(...expectedIntervalsCombination); +} + +// Structure that store the expectations of a single 32bit scalar/element bitcasted from two f16. +interface ExpectionFor32BitsScalarFromF16x2 { + // possibleExpectations is Scalar array if the expectation is for i32/u32 and FPInterval array for + // f32. Note that if the expectation for i32/u32 is unbound, possibleExpectations is meaningless. + possibleExpectations: (Scalar | FPInterval)[]; + isUnbounded: boolean; +} + +/** + * @returns the array of possible 16bits, represented in u16, that bitcasted + * from a given finite f16 represented in u16, handling the possible subnormal + * flushing. Used to build up 32bits or larger results. + */ +function possibleBitsInU16FromFiniteF16InU16(f16InU16: number): number[] { + const h = reinterpretU16AsF16(f16InU16); + assert(isFiniteF16(h)); + return [f16InU16, ...(isSubnormalNumberF16(h) ? f16ZerosInU16 : [])]; +} + +/** + * @returns the expectation for a single 32bit scalar bitcasted from given pair of + * f16, result in ExpectionFor32BitsScalarFromF16x2. + */ +function possible32BitScalarIntervalsFromF16x2( + f16x2InU16x2: number[], + type: 'i32' | 'u32' | 'f32' +): ExpectionFor32BitsScalarFromF16x2 { + assert(f16x2InU16x2.length === 2); + let reinterpretFromU32: (x: number) => number; + let expectationsForValue: (x: number) => Scalar[] | FPInterval[]; + let unboundedExpectations: FPInterval[] | Scalar[]; + if (type === 'u32') { + reinterpretFromU32 = (x: number) => x; + expectationsForValue = x => [u32(x)]; + // Scalar expectation can not express "unbounded" for i32 and u32, so use 0 here as a + // placeholder, and the possibleExpectations should be ignored if the result is unbounded. + unboundedExpectations = [u32(0)]; + } else if (type === 'i32') { + reinterpretFromU32 = (x: number) => reinterpretU32AsI32(x); + expectationsForValue = x => [i32(x)]; + // Scalar expectation can not express "unbounded" for i32 and u32, so use 0 here as a + // placeholder, and the possibleExpectations should be ignored if the result is unbounded. + unboundedExpectations = [i32(0)]; + } else { + assert(type === 'f32'); + reinterpretFromU32 = (x: number) => reinterpretU32AsF32(x); + expectationsForValue = x => { + // Handle the possible Inf/NaN/zeros and subnormal cases for f32 result. + if (!isFiniteF32(x)) { + return [f32UnboundedInterval]; + } + // If the casted f16 value is +/-0.0, the result can be one of both. Note that in JS -0.0 === 0.0. + if (x === 0.0) { + return [f32ZerosInterval]; + } + const exactInterval = FP.f32.toInterval(x); + // If the casted f16 value is subnormal, it also may be flushed to +/-0.0. + return [exactInterval, ...(isSubnormalNumberF32(x) ? [f32ZerosInterval] : [])]; + }; + unboundedExpectations = [f32UnboundedInterval]; + } + // Return unbounded expection if f16 Inf/NaN occurs + if ( + !isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[0])) || + !isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[1])) + ) { + return { possibleExpectations: unboundedExpectations, isUnbounded: true }; + } + const possibleU16Bits = f16x2InU16x2.map(possibleBitsInU16FromFiniteF16InU16); + const possibleExpectations = cartesianProduct(...possibleU16Bits).flatMap( + (possibleBitsU16x2: readonly number[]) => { + assert(possibleBitsU16x2.length === 2); + return expectationsForValue(reinterpretFromU32(u16x2ToU32(possibleBitsU16x2))); + } + ); + return { possibleExpectations, isUnbounded: false }; +} + +/** + * @returns a Comparator for checking if a u32 value is a valid + * bitcast conversion from vec2 f16. + */ +function bitcastVec2F16ToU32Comparator(vec2F16InU16x2: number[]): Comparator { + assert(vec2F16InU16x2.length === 2); + const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'u32'); + // Return alwaysPass if result is expected unbounded. + if (expectations.isUnbounded) { + return anyU32; + } + return anyOf(...expectations.possibleExpectations); +} + +/** + * @returns a Comparator for checking if a i32 value is a valid + * bitcast conversion from vec2 f16. + */ +function bitcastVec2F16ToI32Comparator(vec2F16InU16x2: number[]): Comparator { + assert(vec2F16InU16x2.length === 2); + const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'i32'); + // Return alwaysPass if result is expected unbounded. + if (expectations.isUnbounded) { + return anyI32; + } + return anyOf(...expectations.possibleExpectations); +} + +/** + * @returns a Comparator for checking if a i32 value is a valid + * bitcast conversion from vec2 f16. + */ +function bitcastVec2F16ToF32Comparator(vec2F16InU16x2: number[]): Comparator { + assert(vec2F16InU16x2.length === 2); + const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'f32'); + // Return alwaysPass if result is expected unbounded. + if (expectations.isUnbounded) { + return anyF32; + } + return anyOf(...expectations.possibleExpectations); +} + +/** + * @returns a Comparator for checking if a vec2 u32 value is a valid + * bitcast conversion from vec4 f16. + */ +function bitcastVec4F16ToVec2U32Comparator(vec4F16InU16x4: number[]): Comparator { + assert(vec4F16InU16x4.length === 4); + const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e => + possible32BitScalarIntervalsFromF16x2(e, 'u32') + ); + // Return alwaysPass if any element is expected unbounded. Although it may be only one unbounded + // element in the result vector, currently we don't have a way to build a comparator that expect + // only one element of i32/u32 vector unbounded. + if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) { + return alwaysPass('any vec2'); + } + return anyOf( + ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map( + e => new Vector(e as Scalar[]) + ) + ); +} + +/** + * @returns a Comparator for checking if a vec2 i32 value is a valid + * bitcast conversion from vec4 f16. + */ +function bitcastVec4F16ToVec2I32Comparator(vec4F16InU16x4: number[]): Comparator { + assert(vec4F16InU16x4.length === 4); + const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e => + possible32BitScalarIntervalsFromF16x2(e, 'i32') + ); + // Return alwaysPass if any element is expected unbounded. Although it may be only one unbounded + // element in the result vector, currently we don't have a way to build a comparator that expect + // only one element of i32/u32 vector unbounded. + if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) { + return alwaysPass('any vec2'); + } + return anyOf( + ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map( + e => new Vector(e as Scalar[]) + ) + ); +} + +/** + * @returns a Comparator for checking if a vec2 f32 value is a valid + * bitcast conversion from vec4 f16. + */ +function bitcastVec4F16ToVec2F32Comparator(vec4F16InU16x4: number[]): Comparator { + assert(vec4F16InU16x4.length === 4); + const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e => + possible32BitScalarIntervalsFromF16x2(e, 'f32') + ); + return anyOf( + ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(e => [ + e[0] as FPInterval, + e[1] as FPInterval, + ]) + ); +} + +export const d = makeCaseCache('bitcast', { + // Identity Cases + i32_to_i32: () => fullI32Range().map(e => ({ input: i32(e), expected: i32(e) })), + u32_to_u32: () => fullU32Range().map(e => ({ input: u32(e), expected: u32(e) })), + f32_inf_nan_to_f32: () => + f32RangeWithInfAndNaN.map(e => ({ + input: f32(e), + expected: bitcastF32ToF32Comparator(e), + })), + f32_to_f32: () => + f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToF32Comparator(e) })), + f16_inf_nan_to_f16: () => + [...f16FiniteInF16, ...f16InfAndNaNInF16].map(e => ({ + input: f16(e), + expected: bitcastF16ToF16Comparator(e), + })), + f16_to_f16: () => + f16FiniteInF16.map(e => ({ input: f16(e), expected: bitcastF16ToF16Comparator(e) })), + + // i32,u32,f32 to different i32,u32,f32 + i32_to_u32: () => fullI32Range().map(e => ({ input: i32(e), expected: u32(e) })), + i32_to_f32: () => + i32RangeForF32Finite.map(e => ({ + input: i32(e), + expected: bitcastI32ToF32Comparator(e), + })), + i32_to_f32_inf_nan: () => + i32RangeForF32FiniteInfNaN.map(e => ({ + input: i32(e), + expected: bitcastI32ToF32Comparator(e), + })), + u32_to_i32: () => fullU32Range().map(e => ({ input: u32(e), expected: i32(e) })), + u32_to_f32: () => + u32RangeForF32Finite.map(e => ({ + input: u32(e), + expected: bitcastU32ToF32Comparator(e), + })), + u32_to_f32_inf_nan: () => + u32RangeForF32FiniteInfNaN.map(e => ({ + input: u32(e), + expected: bitcastU32ToF32Comparator(e), + })), + f32_inf_nan_to_i32: () => + f32RangeWithInfAndNaN.map(e => ({ + input: f32(e), + expected: bitcastF32ToI32Comparator(e), + })), + f32_to_i32: () => + f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToI32Comparator(e) })), + + f32_inf_nan_to_u32: () => + f32RangeWithInfAndNaN.map(e => ({ + input: f32(e), + expected: bitcastF32ToU32Comparator(e), + })), + f32_to_u32: () => + f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToU32Comparator(e) })), + + // i32,u32,f32 to vec2 + u32_to_vec2_f16_inf_nan: () => + u32RangeForF16Vec2FiniteInfNaN.map(e => ({ + input: u32(e), + expected: bitcastU32ToVec2F16Comparator(e), + })), + u32_to_vec2_f16: () => + u32RangeForF16Vec2Finite.map(e => ({ + input: u32(e), + expected: bitcastU32ToVec2F16Comparator(e), + })), + i32_to_vec2_f16_inf_nan: () => + i32RangeForF16Vec2FiniteInfNaN.map(e => ({ + input: i32(e), + expected: bitcastI32ToVec2F16Comparator(e), + })), + i32_to_vec2_f16: () => + i32RangeForF16Vec2Finite.map(e => ({ + input: i32(e), + expected: bitcastI32ToVec2F16Comparator(e), + })), + f32_inf_nan_to_vec2_f16_inf_nan: () => + f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN.map(e => ({ + input: f32(e), + expected: bitcastF32ToVec2F16Comparator(e), + })), + f32_to_vec2_f16: () => + f32FiniteRangeForF16Vec2Finite.map(e => ({ + input: f32(e), + expected: bitcastF32ToVec2F16Comparator(e), + })), + + // vec2, vec2, vec2 to vec4 + vec2_i32_to_vec4_f16_inf_nan: () => + slidingSlice(i32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({ + input: toVector(e, i32), + expected: bitcastVec2I32ToVec4F16Comparator(e), + })), + vec2_i32_to_vec4_f16: () => + slidingSlice(i32RangeForF16Vec2Finite, 2).map(e => ({ + input: toVector(e, i32), + expected: bitcastVec2I32ToVec4F16Comparator(e), + })), + vec2_u32_to_vec4_f16_inf_nan: () => + slidingSlice(u32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({ + input: toVector(e, u32), + expected: bitcastVec2U32ToVec4F16Comparator(e), + })), + vec2_u32_to_vec4_f16: () => + slidingSlice(u32RangeForF16Vec2Finite, 2).map(e => ({ + input: toVector(e, u32), + expected: bitcastVec2U32ToVec4F16Comparator(e), + })), + vec2_f32_inf_nan_to_vec4_f16_inf_nan: () => + slidingSlice(f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN, 2).map(e => ({ + input: toVector(e, f32), + expected: bitcastVec2F32ToVec4F16Comparator(e), + })), + vec2_f32_to_vec4_f16: () => + slidingSlice(f32FiniteRangeForF16Vec2Finite, 2).map(e => ({ + input: toVector(e, f32), + expected: bitcastVec2F32ToVec4F16Comparator(e), + })), + + // vec2 to i32, u32, f32 + vec2_f16_to_u32: () => + f16Vec2FiniteInU16x2.map(e => ({ + input: u16x2ToVec2F16(e), + expected: bitcastVec2F16ToU32Comparator(e), + })), + vec2_f16_inf_nan_to_u32: () => + f16Vec2FiniteInfNanInU16x2.map(e => ({ + input: u16x2ToVec2F16(e), + expected: bitcastVec2F16ToU32Comparator(e), + })), + vec2_f16_to_i32: () => + f16Vec2FiniteInU16x2.map(e => ({ + input: u16x2ToVec2F16(e), + expected: bitcastVec2F16ToI32Comparator(e), + })), + vec2_f16_inf_nan_to_i32: () => + f16Vec2FiniteInfNanInU16x2.map(e => ({ + input: u16x2ToVec2F16(e), + expected: bitcastVec2F16ToI32Comparator(e), + })), + vec2_f16_to_f32_finite: () => + f16Vec2FiniteInU16x2 + .filter(u16x2 => isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x2)))) + .map(e => ({ + input: u16x2ToVec2F16(e), + expected: bitcastVec2F16ToF32Comparator(e), + })), + vec2_f16_inf_nan_to_f32: () => + f16Vec2FiniteInfNanInU16x2.map(e => ({ + input: u16x2ToVec2F16(e), + expected: bitcastVec2F16ToF32Comparator(e), + })), + + // vec4 to vec2 of i32, u32, f32 + vec4_f16_to_vec2_u32: () => + f16Vec2FiniteInU16x4.map(e => ({ + input: u16x4ToVec4F16(e), + expected: bitcastVec4F16ToVec2U32Comparator(e), + })), + vec4_f16_inf_nan_to_vec2_u32: () => + f16Vec2FiniteInfNanInU16x4.map(e => ({ + input: u16x4ToVec4F16(e), + expected: bitcastVec4F16ToVec2U32Comparator(e), + })), + vec4_f16_to_vec2_i32: () => + f16Vec2FiniteInU16x4.map(e => ({ + input: u16x4ToVec4F16(e), + expected: bitcastVec4F16ToVec2I32Comparator(e), + })), + vec4_f16_inf_nan_to_vec2_i32: () => + f16Vec2FiniteInfNanInU16x4.map(e => ({ + input: u16x4ToVec4F16(e), + expected: bitcastVec4F16ToVec2I32Comparator(e), + })), + vec4_f16_to_vec2_f32_finite: () => + f16Vec2FiniteInU16x4 + .filter( + u16x4 => + isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(0, 2)))) && + isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(2, 4)))) + ) + .map(e => ({ + input: u16x4ToVec4F16(e), + expected: bitcastVec4F16ToVec2F32Comparator(e), + })), + vec4_f16_inf_nan_to_vec2_f32: () => + f16Vec2FiniteInfNanInU16x4.map(e => ({ + input: u16x4ToVec4F16(e), + expected: bitcastVec4F16ToVec2F32Comparator(e), + })), +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts index 13663a28409f..32d32a27ad43 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts @@ -20,823 +20,15 @@ T is i32, u32, f32 import { TestParams } from '../../../../../../common/framework/fixture.js'; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { assert } from '../../../../../../common/util/util.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { Comparator, alwaysPass, anyOf } from '../../../../../util/compare.js'; -import { kBit, kValue } from '../../../../../util/constants.js'; -import { - f32, - i32, - u32, - f16, - TypeF32, - TypeI32, - TypeU32, - TypeF16, - TypeVec, - Vector, - Scalar, - toVector, -} from '../../../../../util/conversion.js'; -import { FPInterval, FP } from '../../../../../util/floating_point.js'; -import { - scalarF32Range, - fullI32Range, - fullU32Range, - scalarF16Range, - linearRange, - isSubnormalNumberF32, - isSubnormalNumberF16, - cartesianProduct, - isFiniteF32, - isFiniteF16, -} from '../../../../../util/math.js'; -import { - reinterpretI32AsF32, - reinterpretI32AsU32, - reinterpretF32AsI32, - reinterpretF32AsU32, - reinterpretU32AsF32, - reinterpretU32AsI32, - reinterpretU16AsF16, - reinterpretF16AsU16, -} from '../../../../../util/reinterpret.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { allInputSources, run, ShaderBuilder } from '../../expression.js'; +import { TypeF16, TypeF32, TypeI32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; +import { ShaderBuilder, allInputSources, run } from '../../expression.js'; +import { d } from './bitcast.cache.js'; import { builtinWithPredeclaration } from './builtin.js'; export const g = makeTestGroup(GPUTest); -const numNaNs = 11; -const f32InfAndNaNInU32: number[] = [ - // Cover NaNs evenly in integer space. - // The positive NaN with the lowest integer representation is the integer - // for infinity, plus one. - // The positive NaN with the highest integer representation is i32.max (!) - ...linearRange(kBit.f32.positive.infinity + 1, kBit.i32.positive.max, numNaNs), - // The negative NaN with the lowest integer representation is the integer - // for negative infinity, plus one. - // The negative NaN with the highest integer representation is u32.max (!) - ...linearRange(kBit.f32.negative.infinity + 1, kBit.u32.max, numNaNs), - kBit.f32.positive.infinity, - kBit.f32.negative.infinity, -]; -const f32InfAndNaNInF32 = f32InfAndNaNInU32.map(u => reinterpretU32AsF32(u)); -const f32InfAndNaNInI32 = f32InfAndNaNInU32.map(u => reinterpretU32AsI32(u)); - -const f32ZerosInU32 = [0, kBit.f32.negative.zero]; -const f32ZerosInF32 = f32ZerosInU32.map(u => reinterpretU32AsF32(u)); -const f32ZerosInI32 = f32ZerosInU32.map(u => reinterpretU32AsI32(u)); -const f32ZerosInterval: FPInterval = new FPInterval('f32', -0.0, 0.0); - -// f32FiniteRange is a list of finite f32s. fullF32Range() already -// has +0, we only need to add -0. -const f32FiniteRange: number[] = [...scalarF32Range(), kValue.f32.negative.zero]; -const f32RangeWithInfAndNaN: number[] = [...f32FiniteRange, ...f32InfAndNaNInF32]; - -// F16 values, finite, Inf/NaN, and zeros. Represented in float and u16. -const f16FiniteInF16: number[] = [...scalarF16Range(), kValue.f16.negative.zero]; -const f16FiniteInU16: number[] = f16FiniteInF16.map(u => reinterpretF16AsU16(u)); - -const f16InfAndNaNInU16: number[] = [ - // Cover NaNs evenly in integer space. - // The positive NaN with the lowest integer representation is the integer - // for infinity, plus one. - // The positive NaN with the highest integer representation is u16 0x7fff i.e. 32767. - ...linearRange(kBit.f16.positive.infinity + 1, 32767, numNaNs).map(v => Math.ceil(v)), - // The negative NaN with the lowest integer representation is the integer - // for negative infinity, plus one. - // The negative NaN with the highest integer representation is u16 0xffff i.e. 65535 - ...linearRange(kBit.f16.negative.infinity + 1, 65535, numNaNs).map(v => Math.floor(v)), - kBit.f16.positive.infinity, - kBit.f16.negative.infinity, -]; -const f16InfAndNaNInF16 = f16InfAndNaNInU16.map(u => reinterpretU16AsF16(u)); - -const f16ZerosInU16 = [kBit.f16.negative.zero, 0]; - -// f16 interval that match +/-0.0. -const f16ZerosInterval: FPInterval = new FPInterval('f16', -0.0, 0.0); - -/** - * @returns an u32 whose lower and higher 16bits are the two elements of the - * given array of two u16 respectively, in little-endian. - */ -function u16x2ToU32(u16x2: readonly number[]): number { - assert(u16x2.length === 2); - // Create a DataView with 4 bytes buffer. - const buffer = new ArrayBuffer(4); - const view = new DataView(buffer); - // Enforce little-endian. - view.setUint16(0, u16x2[0], true); - view.setUint16(2, u16x2[1], true); - return view.getUint32(0, true); -} - -/** - * @returns an array of two u16, respectively the lower and higher 16bits of - * given u32 in little-endian. - */ -function u32ToU16x2(u32: number): number[] { - // Create a DataView with 4 bytes buffer. - const buffer = new ArrayBuffer(4); - const view = new DataView(buffer); - // Enforce little-endian. - view.setUint32(0, u32, true); - return [view.getUint16(0, true), view.getUint16(2, true)]; -} - -/** - * @returns a vec2 from an array of two u16, each reinterpreted as f16. - */ -function u16x2ToVec2F16(u16x2: number[]): Vector { - assert(u16x2.length === 2); - return toVector(u16x2.map(reinterpretU16AsF16), f16); -} - -/** - * @returns a vec4 from an array of four u16, each reinterpreted as f16. - */ -function u16x4ToVec4F16(u16x4: number[]): Vector { - assert(u16x4.length === 4); - return toVector(u16x4.map(reinterpretU16AsF16), f16); -} - -/** - * @returns true if and only if a given u32 can bitcast to a vec2 with all elements - * being finite f16 values. - */ -function canU32BitcastToFiniteVec2F16(u32: number): boolean { - return u32ToU16x2(u32) - .map(u16 => isFiniteF16(reinterpretU16AsF16(u16))) - .reduce((a, b) => a && b, true); -} - -/** - * @returns an array of N elements with the i-th element being an array of len elements - * [a_i, a_((i+1)%N), ..., a_((i+len-1)%N)], for the input array of N element [a_1, ... a_N] - * and the given len. For example, slidingSlice([1, 2, 3], 2) result in - * [[1, 2], [2, 3], [3, 1]]. - * This helper function is used for generating vector cases from scalar values array. - */ -function slidingSlice(input: number[], len: number) { - const result: number[][] = []; - for (let i = 0; i < input.length; i++) { - const sub: number[] = []; - for (let j = 0; j < len; j++) { - sub.push(input[(i + j) % input.length]); - } - result.push(sub); - } - return result; -} - -// vec2 interesting (zeros, Inf, and NaN) values for testing cases. -// vec2 values that has at least one Inf/NaN f16 element, reinterpreted as u32/i32. -const f16Vec2InfAndNaNInU32 = [ - ...cartesianProduct(f16InfAndNaNInU16, [...f16InfAndNaNInU16, ...f16FiniteInU16]), - ...cartesianProduct(f16FiniteInU16, f16InfAndNaNInU16), -].map(u16x2ToU32); -const f16Vec2InfAndNaNInI32 = f16Vec2InfAndNaNInU32.map(u => reinterpretU32AsI32(u)); -// vec2 values with two f16 0.0 element, reinterpreted as u32/i32. -const f16Vec2ZerosInU32 = cartesianProduct(f16ZerosInU16, f16ZerosInU16).map(u16x2ToU32); -const f16Vec2ZerosInI32 = f16Vec2ZerosInU32.map(u => reinterpretU32AsI32(u)); - -// i32/u32/f32 range for bitcasting to vec2 -// u32 values for bitcasting to vec2 finite, Inf, and NaN. -const u32RangeForF16Vec2FiniteInfNaN: number[] = [ - ...fullU32Range(), - ...f16Vec2ZerosInU32, - ...f16Vec2InfAndNaNInU32, -]; -// u32 values for bitcasting to finite only vec2, used for constant evaluation. -const u32RangeForF16Vec2Finite: number[] = u32RangeForF16Vec2FiniteInfNaN.filter( - canU32BitcastToFiniteVec2F16 -); -// i32 values for bitcasting to vec2 finite, zeros, Inf, and NaN. -const i32RangeForF16Vec2FiniteInfNaN: number[] = [ - ...fullI32Range(), - ...f16Vec2ZerosInI32, - ...f16Vec2InfAndNaNInI32, -]; -// i32 values for bitcasting to finite only vec2, used for constant evaluation. -const i32RangeForF16Vec2Finite: number[] = i32RangeForF16Vec2FiniteInfNaN.filter(u => - canU32BitcastToFiniteVec2F16(reinterpretI32AsU32(u)) -); -// f32 values with finite/Inf/NaN f32, for bitcasting to vec2 finite, zeros, Inf, and NaN. -const f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN: number[] = [ - ...f32RangeWithInfAndNaN, - ...u32RangeForF16Vec2FiniteInfNaN.map(reinterpretU32AsF32), -]; -// Finite f32 values for bitcasting to finite only vec2, used for constant evaluation. -const f32FiniteRangeForF16Vec2Finite: number[] = f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN - .filter(isFiniteF32) - .filter(u => canU32BitcastToFiniteVec2F16(reinterpretF32AsU32(u))); - -// vec2 cases for bitcasting to i32/u32/f32, by combining f16 values into pairs -const f16Vec2FiniteInU16x2 = slidingSlice(f16FiniteInU16, 2); -const f16Vec2FiniteInfNanInU16x2 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 2); -// vec4 cases for bitcasting to vec2, by combining f16 values 4-by-4 -const f16Vec2FiniteInU16x4 = slidingSlice(f16FiniteInU16, 4); -const f16Vec2FiniteInfNanInU16x4 = slidingSlice([...f16FiniteInU16, ...f16InfAndNaNInU16], 4); - -// alwaysPass comparator for i32/u32/f32 cases. For f32/f16 we also use unbound interval, which -// allow per-element unbounded expectation for vector. -const anyF32 = alwaysPass('any f32'); -const anyI32 = alwaysPass('any i32'); -const anyU32 = alwaysPass('any u32'); - -// Unbounded FPInterval -const f32UnboundedInterval = FP.f32.constants().unboundedInterval; -const f16UnboundedInterval = FP.f16.constants().unboundedInterval; - -// i32 and u32 cases for bitcasting to f32. -// i32 cases for bitcasting to f32 finite, zeros, Inf, and NaN. -const i32RangeForF32FiniteInfNaN: number[] = [ - ...fullI32Range(), - ...f32ZerosInI32, - ...f32InfAndNaNInI32, -]; -// i32 cases for bitcasting to f32 finite only. -const i32RangeForF32Finite: number[] = i32RangeForF32FiniteInfNaN.filter(i => - isFiniteF32(reinterpretI32AsF32(i)) -); -// u32 cases for bitcasting to f32 finite, zeros, Inf, and NaN. -const u32RangeForF32FiniteInfNaN: number[] = [ - ...fullU32Range(), - ...f32ZerosInU32, - ...f32InfAndNaNInU32, -]; -// u32 cases for bitcasting to f32 finite only. -const u32RangeForF32Finite: number[] = u32RangeForF32FiniteInfNaN.filter(u => - isFiniteF32(reinterpretU32AsF32(u)) -); - -/** - * @returns a Comparator for checking if a f32 value is a valid - * bitcast conversion from f32. - */ -function bitcastF32ToF32Comparator(f: number): Comparator { - if (!isFiniteF32(f)) return anyF32; - const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])]; - return anyOf(...acceptable.map(f32)); -} - -/** - * @returns a Comparator for checking if a u32 value is a valid - * bitcast conversion from f32. - */ -function bitcastF32ToU32Comparator(f: number): Comparator { - if (!isFiniteF32(f)) return anyU32; - const acceptable: number[] = [ - reinterpretF32AsU32(f), - ...(isSubnormalNumberF32(f) ? f32ZerosInU32 : []), - ]; - return anyOf(...acceptable.map(u32)); -} - -/** - * @returns a Comparator for checking if a i32 value is a valid - * bitcast conversion from f32. - */ -function bitcastF32ToI32Comparator(f: number): Comparator { - if (!isFiniteF32(f)) return anyI32; - const acceptable: number[] = [ - reinterpretF32AsI32(f), - ...(isSubnormalNumberF32(f) ? f32ZerosInI32 : []), - ]; - return anyOf(...acceptable.map(i32)); -} - -/** - * @returns a Comparator for checking if a f32 value is a valid - * bitcast conversion from i32. - */ -function bitcastI32ToF32Comparator(i: number): Comparator { - const f: number = reinterpretI32AsF32(i); - if (!isFiniteF32(f)) return anyI32; - // Positive or negative zero bit pattern map to any zero. - if (f32ZerosInI32.includes(i)) return anyOf(...f32ZerosInF32.map(f32)); - const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])]; - return anyOf(...acceptable.map(f32)); -} - -/** - * @returns a Comparator for checking if a f32 value is a valid - * bitcast conversion from u32. - */ -function bitcastU32ToF32Comparator(u: number): Comparator { - const f: number = reinterpretU32AsF32(u); - if (!isFiniteF32(f)) return anyU32; - // Positive or negative zero bit pattern map to any zero. - if (f32ZerosInU32.includes(u)) return anyOf(...f32ZerosInF32.map(f32)); - const acceptable: number[] = [f, ...(isSubnormalNumberF32(f) ? f32ZerosInF32 : [])]; - return anyOf(...acceptable.map(f32)); -} - -/** - * @returns an array of expected f16 FPInterval for the given bitcasted f16 value, which may be - * subnormal, Inf, or NaN. Test cases that bitcasted to vector of f16 use this function to get - * per-element expectation and build vector expectation using cartesianProduct. - */ -function generateF16ExpectationIntervals(bitcastedF16Value: number): FPInterval[] { - // If the bitcasted f16 value is inf or nan, the result is unbounded - if (!isFiniteF16(bitcastedF16Value)) { - return [f16UnboundedInterval]; - } - // If the casted f16 value is +/-0.0, the result can be one of both. Note that in JS -0.0 === 0.0. - if (bitcastedF16Value === 0.0) { - return [f16ZerosInterval]; - } - const exactInterval = FP.f16.toInterval(bitcastedF16Value); - // If the casted f16 value is subnormal, it also may be flushed to +/-0.0. - return [exactInterval, ...(isSubnormalNumberF16(bitcastedF16Value) ? [f16ZerosInterval] : [])]; -} - -/** - * @returns a Comparator for checking if a f16 value is a valid - * bitcast conversion from f16. - */ -function bitcastF16ToF16Comparator(f: number): Comparator { - if (!isFiniteF16(f)) return anyOf(f16UnboundedInterval); - return anyOf(...generateF16ExpectationIntervals(f)); -} - -/** - * @returns a Comparator for checking if a vec2 is a valid bitcast - * conversion from u32. - */ -function bitcastU32ToVec2F16Comparator(u: number): Comparator { - const bitcastedVec2F16InU16x2 = u32ToU16x2(u).map(reinterpretU16AsF16); - // Generate expection for vec2 f16 result, by generating expected intervals for each elements and - // then do cartesian product. - const expectedIntervalsCombination = cartesianProduct( - ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals) - ); - return anyOf(...expectedIntervalsCombination); -} - -/** - * @returns a Comparator for checking if a vec2 value is a valid - * bitcast conversion from i32. - */ -function bitcastI32ToVec2F16Comparator(i: number): Comparator { - const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretI32AsU32(i)).map(reinterpretU16AsF16); - // Generate expection for vec2 f16 result, by generating expected intervals for each elements and - // then do cartesian product. - const expectedIntervalsCombination = cartesianProduct( - ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals) - ); - return anyOf(...expectedIntervalsCombination); -} - -/** - * @returns a Comparator for checking if a vec2 value is a valid - * bitcast conversion from f32. - */ -function bitcastF32ToVec2F16Comparator(f: number): Comparator { - // If input f32 is not finite, it can be evaluated to any value and thus any result f16 vec2 is - // possible. - if (!isFiniteF32(f)) { - return anyOf([f16UnboundedInterval, f16UnboundedInterval]); - } - const bitcastedVec2F16InU16x2 = u32ToU16x2(reinterpretF32AsU32(f)).map(reinterpretU16AsF16); - // Generate expection for vec2 f16 result, by generating expected intervals for each elements and - // then do cartesian product. - const expectedIntervalsCombination = cartesianProduct( - ...bitcastedVec2F16InU16x2.map(generateF16ExpectationIntervals) - ); - return anyOf(...expectedIntervalsCombination); -} - -/** - * @returns a Comparator for checking if a vec4 is a valid - * bitcast conversion from vec2. - */ -function bitcastVec2U32ToVec4F16Comparator(u32x2: number[]): Comparator { - assert(u32x2.length === 2); - const bitcastedVec4F16InU16x4 = u32x2.flatMap(u32ToU16x2).map(reinterpretU16AsF16); - // Generate expection for vec4 f16 result, by generating expected intervals for each elements and - // then do cartesian product. - const expectedIntervalsCombination = cartesianProduct( - ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals) - ); - return anyOf(...expectedIntervalsCombination); -} - -/** - * @returns a Comparator for checking if a vec4 is a valid - * bitcast conversion from vec2. - */ -function bitcastVec2I32ToVec4F16Comparator(i32x2: number[]): Comparator { - assert(i32x2.length === 2); - const bitcastedVec4F16InU16x4 = i32x2 - .map(reinterpretI32AsU32) - .flatMap(u32ToU16x2) - .map(reinterpretU16AsF16); - // Generate expection for vec4 f16 result, by generating expected intervals for each elements and - // then do cartesian product. - const expectedIntervalsCombination = cartesianProduct( - ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals) - ); - return anyOf(...expectedIntervalsCombination); -} - -/** - * @returns a Comparator for checking if a vec4 is a valid - * bitcast conversion from vec2. - */ -function bitcastVec2F32ToVec4F16Comparator(f32x2: number[]): Comparator { - assert(f32x2.length === 2); - const bitcastedVec4F16InU16x4 = f32x2 - .map(reinterpretF32AsU32) - .flatMap(u32ToU16x2) - .map(reinterpretU16AsF16); - // Generate expection for vec4 f16 result, by generating expected intervals for each elements and - // then do cartesian product. - const expectedIntervalsCombination = cartesianProduct( - ...bitcastedVec4F16InU16x4.map(generateF16ExpectationIntervals) - ); - return anyOf(...expectedIntervalsCombination); -} - -// Structure that store the expectations of a single 32bit scalar/element bitcasted from two f16. -interface ExpectionFor32BitsScalarFromF16x2 { - // possibleExpectations is Scalar array if the expectation is for i32/u32 and FPInterval array for - // f32. Note that if the expectation for i32/u32 is unbound, possibleExpectations is meaningless. - possibleExpectations: (Scalar | FPInterval)[]; - isUnbounded: boolean; -} - -/** - * @returns the array of possible 16bits, represented in u16, that bitcasted - * from a given finite f16 represented in u16, handling the possible subnormal - * flushing. Used to build up 32bits or larger results. - */ -function possibleBitsInU16FromFiniteF16InU16(f16InU16: number): number[] { - const h = reinterpretU16AsF16(f16InU16); - assert(isFiniteF16(h)); - return [f16InU16, ...(isSubnormalNumberF16(h) ? f16ZerosInU16 : [])]; -} - -/** - * @returns the expectation for a single 32bit scalar bitcasted from given pair of - * f16, result in ExpectionFor32BitsScalarFromF16x2. - */ -function possible32BitScalarIntervalsFromF16x2( - f16x2InU16x2: number[], - type: 'i32' | 'u32' | 'f32' -): ExpectionFor32BitsScalarFromF16x2 { - assert(f16x2InU16x2.length === 2); - let reinterpretFromU32: (x: number) => number; - let expectationsForValue: (x: number) => Scalar[] | FPInterval[]; - let unboundedExpectations: FPInterval[] | Scalar[]; - if (type === 'u32') { - reinterpretFromU32 = (x: number) => x; - expectationsForValue = x => [u32(x)]; - // Scalar expectation can not express "unbounded" for i32 and u32, so use 0 here as a - // placeholder, and the possibleExpectations should be ignored if the result is unbounded. - unboundedExpectations = [u32(0)]; - } else if (type === 'i32') { - reinterpretFromU32 = (x: number) => reinterpretU32AsI32(x); - expectationsForValue = x => [i32(x)]; - // Scalar expectation can not express "unbounded" for i32 and u32, so use 0 here as a - // placeholder, and the possibleExpectations should be ignored if the result is unbounded. - unboundedExpectations = [i32(0)]; - } else { - assert(type === 'f32'); - reinterpretFromU32 = (x: number) => reinterpretU32AsF32(x); - expectationsForValue = x => { - // Handle the possible Inf/NaN/zeros and subnormal cases for f32 result. - if (!isFiniteF32(x)) { - return [f32UnboundedInterval]; - } - // If the casted f16 value is +/-0.0, the result can be one of both. Note that in JS -0.0 === 0.0. - if (x === 0.0) { - return [f32ZerosInterval]; - } - const exactInterval = FP.f32.toInterval(x); - // If the casted f16 value is subnormal, it also may be flushed to +/-0.0. - return [exactInterval, ...(isSubnormalNumberF32(x) ? [f32ZerosInterval] : [])]; - }; - unboundedExpectations = [f32UnboundedInterval]; - } - // Return unbounded expection if f16 Inf/NaN occurs - if ( - !isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[0])) || - !isFiniteF16(reinterpretU16AsF16(f16x2InU16x2[1])) - ) { - return { possibleExpectations: unboundedExpectations, isUnbounded: true }; - } - const possibleU16Bits = f16x2InU16x2.map(possibleBitsInU16FromFiniteF16InU16); - const possibleExpectations = cartesianProduct(...possibleU16Bits).flatMap( - (possibleBitsU16x2: readonly number[]) => { - assert(possibleBitsU16x2.length === 2); - return expectationsForValue(reinterpretFromU32(u16x2ToU32(possibleBitsU16x2))); - } - ); - return { possibleExpectations, isUnbounded: false }; -} - -/** - * @returns a Comparator for checking if a u32 value is a valid - * bitcast conversion from vec2 f16. - */ -function bitcastVec2F16ToU32Comparator(vec2F16InU16x2: number[]): Comparator { - assert(vec2F16InU16x2.length === 2); - const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'u32'); - // Return alwaysPass if result is expected unbounded. - if (expectations.isUnbounded) { - return anyU32; - } - return anyOf(...expectations.possibleExpectations); -} - -/** - * @returns a Comparator for checking if a i32 value is a valid - * bitcast conversion from vec2 f16. - */ -function bitcastVec2F16ToI32Comparator(vec2F16InU16x2: number[]): Comparator { - assert(vec2F16InU16x2.length === 2); - const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'i32'); - // Return alwaysPass if result is expected unbounded. - if (expectations.isUnbounded) { - return anyI32; - } - return anyOf(...expectations.possibleExpectations); -} - -/** - * @returns a Comparator for checking if a i32 value is a valid - * bitcast conversion from vec2 f16. - */ -function bitcastVec2F16ToF32Comparator(vec2F16InU16x2: number[]): Comparator { - assert(vec2F16InU16x2.length === 2); - const expectations = possible32BitScalarIntervalsFromF16x2(vec2F16InU16x2, 'f32'); - // Return alwaysPass if result is expected unbounded. - if (expectations.isUnbounded) { - return anyF32; - } - return anyOf(...expectations.possibleExpectations); -} - -/** - * @returns a Comparator for checking if a vec2 u32 value is a valid - * bitcast conversion from vec4 f16. - */ -function bitcastVec4F16ToVec2U32Comparator(vec4F16InU16x4: number[]): Comparator { - assert(vec4F16InU16x4.length === 4); - const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e => - possible32BitScalarIntervalsFromF16x2(e, 'u32') - ); - // Return alwaysPass if any element is expected unbounded. Although it may be only one unbounded - // element in the result vector, currently we don't have a way to build a comparator that expect - // only one element of i32/u32 vector unbounded. - if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) { - return alwaysPass('any vec2'); - } - return anyOf( - ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map( - e => new Vector(e as Scalar[]) - ) - ); -} - -/** - * @returns a Comparator for checking if a vec2 i32 value is a valid - * bitcast conversion from vec4 f16. - */ -function bitcastVec4F16ToVec2I32Comparator(vec4F16InU16x4: number[]): Comparator { - assert(vec4F16InU16x4.length === 4); - const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e => - possible32BitScalarIntervalsFromF16x2(e, 'i32') - ); - // Return alwaysPass if any element is expected unbounded. Although it may be only one unbounded - // element in the result vector, currently we don't have a way to build a comparator that expect - // only one element of i32/u32 vector unbounded. - if (expectationsPerElement.map(e => e.isUnbounded).reduce((a, b) => a || b, false)) { - return alwaysPass('any vec2'); - } - return anyOf( - ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map( - e => new Vector(e as Scalar[]) - ) - ); -} - -/** - * @returns a Comparator for checking if a vec2 f32 value is a valid - * bitcast conversion from vec4 f16. - */ -function bitcastVec4F16ToVec2F32Comparator(vec4F16InU16x4: number[]): Comparator { - assert(vec4F16InU16x4.length === 4); - const expectationsPerElement = [vec4F16InU16x4.slice(0, 2), vec4F16InU16x4.slice(2, 4)].map(e => - possible32BitScalarIntervalsFromF16x2(e, 'f32') - ); - return anyOf( - ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map(e => [ - e[0] as FPInterval, - e[1] as FPInterval, - ]) - ); -} - -export const d = makeCaseCache('bitcast', { - // Identity Cases - i32_to_i32: () => fullI32Range().map(e => ({ input: i32(e), expected: i32(e) })), - u32_to_u32: () => fullU32Range().map(e => ({ input: u32(e), expected: u32(e) })), - f32_inf_nan_to_f32: () => - f32RangeWithInfAndNaN.map(e => ({ - input: f32(e), - expected: bitcastF32ToF32Comparator(e), - })), - f32_to_f32: () => - f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToF32Comparator(e) })), - f16_inf_nan_to_f16: () => - [...f16FiniteInF16, ...f16InfAndNaNInF16].map(e => ({ - input: f16(e), - expected: bitcastF16ToF16Comparator(e), - })), - f16_to_f16: () => - f16FiniteInF16.map(e => ({ input: f16(e), expected: bitcastF16ToF16Comparator(e) })), - - // i32,u32,f32 to different i32,u32,f32 - i32_to_u32: () => fullI32Range().map(e => ({ input: i32(e), expected: u32(e) })), - i32_to_f32: () => - i32RangeForF32Finite.map(e => ({ - input: i32(e), - expected: bitcastI32ToF32Comparator(e), - })), - i32_to_f32_inf_nan: () => - i32RangeForF32FiniteInfNaN.map(e => ({ - input: i32(e), - expected: bitcastI32ToF32Comparator(e), - })), - u32_to_i32: () => fullU32Range().map(e => ({ input: u32(e), expected: i32(e) })), - u32_to_f32: () => - u32RangeForF32Finite.map(e => ({ - input: u32(e), - expected: bitcastU32ToF32Comparator(e), - })), - u32_to_f32_inf_nan: () => - u32RangeForF32FiniteInfNaN.map(e => ({ - input: u32(e), - expected: bitcastU32ToF32Comparator(e), - })), - f32_inf_nan_to_i32: () => - f32RangeWithInfAndNaN.map(e => ({ - input: f32(e), - expected: bitcastF32ToI32Comparator(e), - })), - f32_to_i32: () => - f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToI32Comparator(e) })), - - f32_inf_nan_to_u32: () => - f32RangeWithInfAndNaN.map(e => ({ - input: f32(e), - expected: bitcastF32ToU32Comparator(e), - })), - f32_to_u32: () => - f32FiniteRange.map(e => ({ input: f32(e), expected: bitcastF32ToU32Comparator(e) })), - - // i32,u32,f32 to vec2 - u32_to_vec2_f16_inf_nan: () => - u32RangeForF16Vec2FiniteInfNaN.map(e => ({ - input: u32(e), - expected: bitcastU32ToVec2F16Comparator(e), - })), - u32_to_vec2_f16: () => - u32RangeForF16Vec2Finite.map(e => ({ - input: u32(e), - expected: bitcastU32ToVec2F16Comparator(e), - })), - i32_to_vec2_f16_inf_nan: () => - i32RangeForF16Vec2FiniteInfNaN.map(e => ({ - input: i32(e), - expected: bitcastI32ToVec2F16Comparator(e), - })), - i32_to_vec2_f16: () => - i32RangeForF16Vec2Finite.map(e => ({ - input: i32(e), - expected: bitcastI32ToVec2F16Comparator(e), - })), - f32_inf_nan_to_vec2_f16_inf_nan: () => - f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN.map(e => ({ - input: f32(e), - expected: bitcastF32ToVec2F16Comparator(e), - })), - f32_to_vec2_f16: () => - f32FiniteRangeForF16Vec2Finite.map(e => ({ - input: f32(e), - expected: bitcastF32ToVec2F16Comparator(e), - })), - - // vec2, vec2, vec2 to vec4 - vec2_i32_to_vec4_f16_inf_nan: () => - slidingSlice(i32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({ - input: toVector(e, i32), - expected: bitcastVec2I32ToVec4F16Comparator(e), - })), - vec2_i32_to_vec4_f16: () => - slidingSlice(i32RangeForF16Vec2Finite, 2).map(e => ({ - input: toVector(e, i32), - expected: bitcastVec2I32ToVec4F16Comparator(e), - })), - vec2_u32_to_vec4_f16_inf_nan: () => - slidingSlice(u32RangeForF16Vec2FiniteInfNaN, 2).map(e => ({ - input: toVector(e, u32), - expected: bitcastVec2U32ToVec4F16Comparator(e), - })), - vec2_u32_to_vec4_f16: () => - slidingSlice(u32RangeForF16Vec2Finite, 2).map(e => ({ - input: toVector(e, u32), - expected: bitcastVec2U32ToVec4F16Comparator(e), - })), - vec2_f32_inf_nan_to_vec4_f16_inf_nan: () => - slidingSlice(f32RangeWithInfAndNaNForF16Vec2FiniteInfNaN, 2).map(e => ({ - input: toVector(e, f32), - expected: bitcastVec2F32ToVec4F16Comparator(e), - })), - vec2_f32_to_vec4_f16: () => - slidingSlice(f32FiniteRangeForF16Vec2Finite, 2).map(e => ({ - input: toVector(e, f32), - expected: bitcastVec2F32ToVec4F16Comparator(e), - })), - - // vec2 to i32, u32, f32 - vec2_f16_to_u32: () => - f16Vec2FiniteInU16x2.map(e => ({ - input: u16x2ToVec2F16(e), - expected: bitcastVec2F16ToU32Comparator(e), - })), - vec2_f16_inf_nan_to_u32: () => - f16Vec2FiniteInfNanInU16x2.map(e => ({ - input: u16x2ToVec2F16(e), - expected: bitcastVec2F16ToU32Comparator(e), - })), - vec2_f16_to_i32: () => - f16Vec2FiniteInU16x2.map(e => ({ - input: u16x2ToVec2F16(e), - expected: bitcastVec2F16ToI32Comparator(e), - })), - vec2_f16_inf_nan_to_i32: () => - f16Vec2FiniteInfNanInU16x2.map(e => ({ - input: u16x2ToVec2F16(e), - expected: bitcastVec2F16ToI32Comparator(e), - })), - vec2_f16_to_f32_finite: () => - f16Vec2FiniteInU16x2 - .filter(u16x2 => isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x2)))) - .map(e => ({ - input: u16x2ToVec2F16(e), - expected: bitcastVec2F16ToF32Comparator(e), - })), - vec2_f16_inf_nan_to_f32: () => - f16Vec2FiniteInfNanInU16x2.map(e => ({ - input: u16x2ToVec2F16(e), - expected: bitcastVec2F16ToF32Comparator(e), - })), - - // vec4 to vec2 of i32, u32, f32 - vec4_f16_to_vec2_u32: () => - f16Vec2FiniteInU16x4.map(e => ({ - input: u16x4ToVec4F16(e), - expected: bitcastVec4F16ToVec2U32Comparator(e), - })), - vec4_f16_inf_nan_to_vec2_u32: () => - f16Vec2FiniteInfNanInU16x4.map(e => ({ - input: u16x4ToVec4F16(e), - expected: bitcastVec4F16ToVec2U32Comparator(e), - })), - vec4_f16_to_vec2_i32: () => - f16Vec2FiniteInU16x4.map(e => ({ - input: u16x4ToVec4F16(e), - expected: bitcastVec4F16ToVec2I32Comparator(e), - })), - vec4_f16_inf_nan_to_vec2_i32: () => - f16Vec2FiniteInfNanInU16x4.map(e => ({ - input: u16x4ToVec4F16(e), - expected: bitcastVec4F16ToVec2I32Comparator(e), - })), - vec4_f16_to_vec2_f32_finite: () => - f16Vec2FiniteInU16x4 - .filter( - u16x4 => - isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(0, 2)))) && - isFiniteF32(reinterpretU32AsF32(u16x2ToU32(u16x4.slice(2, 4)))) - ) - .map(e => ({ - input: u16x4ToVec4F16(e), - expected: bitcastVec4F16ToVec2F32Comparator(e), - })), - vec4_f16_inf_nan_to_vec2_f32: () => - f16Vec2FiniteInfNanInU16x4.map(e => ({ - input: u16x4ToVec4F16(e), - expected: bitcastVec4F16ToVec2F32Comparator(e), - })), -}); - /** * @returns a ShaderBuilder that generates a call to bitcast, * using appropriate destination type, which optionally can be diff --git a/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts new file mode 100644 index 000000000000..3141d1d1a16f --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/ceil.cache.ts @@ -0,0 +1,25 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +const kSmallMagnitudeTestValues = [0.1, 0.9, 1.0, 1.1, 1.9, -0.1, -0.9, -1.0, -1.1, -1.9]; + +// See https://github.com/gpuweb/cts/issues/2766 for details +const kIssue2766Value = { + f32: 0x8000_0000, + f16: 0x8000, +}; + +// Cases: [f32|f16] +const cases = (['f32', 'f16'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [...kSmallMagnitudeTestValues, kIssue2766Value[trait], ...FP[trait].scalarRange()], + 'unfiltered', + FP[trait].ceilInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('ceil', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts index 8b8f9dbbaa6a..18509efdffb9 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts @@ -10,38 +10,14 @@ Returns the ceiling of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './ceil.cache.js'; export const g = makeTestGroup(GPUTest); -const kSmallMagnitudeTestValues = [0.1, 0.9, 1.0, 1.1, 1.9, -0.1, -0.9, -1.0, -1.1, -1.9]; - -// See https://github.com/gpuweb/cts/issues/2766 for details -const kIssue2766Value = { - f32: 0x8000_0000, - f16: 0x8000, -}; - -// Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [...kSmallMagnitudeTestValues, kIssue2766Value[trait], ...FP[trait].scalarRange()], - 'unfiltered', - FP[trait].ceilInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('ceil', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts new file mode 100644 index 000000000000..b28e75fd40f4 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts @@ -0,0 +1,96 @@ +import { kValue } from '../../../../../util/constants.js'; +import { ScalarType, TypeI32, TypeU32 } from '../../../../../util/conversion.js'; +import { FP } from '../../../../../util/floating_point.js'; +import { Case } from '../../case.js'; +import { makeCaseCache } from '../../case_cache.js'; + +const u32Values = [0, 1, 2, 3, 0x70000000, 0x80000000, kValue.u32.max]; + +const i32Values = [ + kValue.i32.negative.min, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 0x70000000, + kValue.i32.positive.max, +]; + +/** @returns a set of clamp test cases from an ascending list of integer values */ +function generateIntegerTestCases( + test_values: Array, + type: ScalarType, + stage: 'const' | 'non_const' +): Array { + return test_values.flatMap(low => + test_values.flatMap(high => + stage === 'const' && low > high + ? [] + : test_values.map(e => ({ + input: [type.create(e), type.create(low), type.create(high)], + expected: type.create(Math.min(Math.max(e, low), high)), + })) + ) + ); +} + +function generateFloatTestCases( + test_values: readonly number[], + trait: 'f32' | 'f16' | 'abstract', + stage: 'const' | 'non_const' +): Array { + return test_values.flatMap(low => + test_values.flatMap(high => + stage === 'const' && low > high + ? [] + : test_values.flatMap(e => { + const c = FP[trait].makeScalarTripleToIntervalCase( + e, + low, + high, + stage === 'const' ? 'finite' : 'unfiltered', + ...FP[trait].clampIntervals + ); + return c === undefined ? [] : [c]; + }) + ) + ); +} + +// Cases: [f32|f16|abstract]_[non_]const +// abstract_non_const is empty and unused +const fp_cases = (['f32', 'f16', 'abstract'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + if (trait === 'abstract' && nonConst) { + return []; + } + return generateFloatTestCases( + FP[trait].sparseScalarRange(), + trait, + nonConst ? 'non_const' : 'const' + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('clamp', { + u32_non_const: () => { + return generateIntegerTestCases(u32Values, TypeU32, 'non_const'); + }, + u32_const: () => { + return generateIntegerTestCases(u32Values, TypeU32, 'const'); + }, + i32_non_const: () => { + return generateIntegerTestCases(i32Values, TypeI32, 'non_const'); + }, + i32_const: () => { + return generateIntegerTestCases(i32Values, TypeI32, 'const'); + }, + ...fp_cases, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts index a2f8f52843c6..75abf1f11d8d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts @@ -15,114 +15,20 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { kValue } from '../../../../../util/constants.js'; import { - ScalarType, - TypeF32, + TypeAbstractFloat, TypeF16, + TypeF32, TypeI32, TypeU32, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { allInputSources, Case, onlyConstInputSource, run } from '../../expression.js'; +import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './clamp.cache.js'; export const g = makeTestGroup(GPUTest); -const u32Values = [0, 1, 2, 3, 0x70000000, 0x80000000, kValue.u32.max]; - -const i32Values = [ - kValue.i32.negative.min, - -3, - -2, - -1, - 0, - 1, - 2, - 3, - 0x70000000, - kValue.i32.positive.max, -]; - -/** @returns a set of clamp test cases from an ascending list of integer values */ -function generateIntegerTestCases( - test_values: Array, - type: ScalarType, - stage: 'const' | 'non_const' -): Array { - return test_values.flatMap(low => - test_values.flatMap(high => - stage === 'const' && low > high - ? [] - : test_values.map(e => ({ - input: [type.create(e), type.create(low), type.create(high)], - expected: type.create(Math.min(Math.max(e, low), high)), - })) - ) - ); -} - -function generateFloatTestCases( - test_values: readonly number[], - trait: 'f32' | 'f16' | 'abstract', - stage: 'const' | 'non_const' -): Array { - return test_values.flatMap(low => - test_values.flatMap(high => - stage === 'const' && low > high - ? [] - : test_values.flatMap(e => { - const c = FP[trait].makeScalarTripleToIntervalCase( - e, - low, - high, - stage === 'const' ? 'finite' : 'unfiltered', - ...FP[trait].clampIntervals - ); - return c === undefined ? [] : [c]; - }) - ) - ); -} - -// Cases: [f32|f16|abstract]_[non_]const -// abstract_non_const is empty and unused -const fp_cases = (['f32', 'f16', 'abstract'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - if (trait === 'abstract' && nonConst) { - return []; - } - return generateFloatTestCases( - FP[trait].sparseScalarRange(), - trait, - nonConst ? 'non_const' : 'const' - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('clamp', { - u32_non_const: () => { - return generateIntegerTestCases(u32Values, TypeU32, 'non_const'); - }, - u32_const: () => { - return generateIntegerTestCases(u32Values, TypeU32, 'const'); - }, - i32_non_const: () => { - return generateIntegerTestCases(i32Values, TypeI32, 'non_const'); - }, - i32_const: () => { - return generateIntegerTestCases(i32Values, TypeI32, 'const'); - }, - ...fp_cases, -}); - g.test('abstract_int') .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') .desc(`abstract int tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/cos.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/cos.cache.ts new file mode 100644 index 000000000000..1833f12d48e5 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/cos.cache.ts @@ -0,0 +1,22 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16] +const cases = (['f32', 'f16'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [ + // Well-defined accuracy range + ...linearRange(-Math.PI, Math.PI, 100), + ...FP[trait].scalarRange(), + ], + 'unfiltered', + FP[trait].cosInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('cos', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts index bd2eb6089048..5eb9081928d1 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts @@ -9,35 +9,14 @@ Returns the cosine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './cos.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [ - // Well-defined accuracy range - ...linearRange(-Math.PI, Math.PI, 100), - ...FP[trait].scalarRange(), - ], - 'unfiltered', - FP[trait].cosInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('cos', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/cosh.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/cosh.cache.ts new file mode 100644 index 000000000000..e3fb7a0e7b88 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/cosh.cache.ts @@ -0,0 +1,19 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16] +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + nonConst ? 'unfiltered' : 'finite', + FP[trait].coshInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('cosh', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts index 296e3a4b3ef8..aba7212a06e3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts @@ -9,32 +9,14 @@ Returns the hyperbolic cosine of e. Component-wise when T is a vector import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './cosh.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - nonConst ? 'unfiltered' : 'finite', - FP[trait].coshInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('cosh', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/cross.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/cross.cache.ts new file mode 100644 index 000000000000..74752d3afb26 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/cross.cache.ts @@ -0,0 +1,24 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract]_[non_]const +// abstract_non_const is empty and not used +const cases = (['f32', 'f16', 'abstract'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + if (trait === 'abstract' && nonConst) { + return []; + } + return FP[trait].generateVectorPairToVectorCases( + FP[trait].vectorRange(3), + FP[trait].vectorRange(3), + nonConst ? 'unfiltered' : 'finite', + FP[trait].crossInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('cross', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts index 78dc11895181..bf26a42f2f5e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts @@ -9,36 +9,13 @@ Returns the cross product of e1 and e2. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeAbstractFloat, TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './cross.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract]_[non_]const -// abstract_non_const is empty and not used -const cases = (['f32', 'f16', 'abstract'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - if (trait === 'abstract' && nonConst) { - return []; - } - return FP[trait].generateVectorPairToVectorCases( - FP[trait].vectorRange(3), - FP[trait].vectorRange(3), - nonConst ? 'unfiltered' : 'finite', - FP[trait].crossInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('cross', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/degrees.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/degrees.cache.ts new file mode 100644 index 000000000000..2dbe8923c4cc --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/degrees.cache.ts @@ -0,0 +1,23 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract]_[non_]const +// abstract_non_const is empty and not used +const cases = (['f32', 'f16', 'abstract'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + if (trait === 'abstract' && nonConst) { + return []; + } + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + nonConst ? 'unfiltered' : 'finite', + FP[trait].degreesInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('degrees', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts index 4da88c5e3d68..0e52c52c11a3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts @@ -10,35 +10,13 @@ Converts radians to degrees, approximating e1 × 180 ÷ π. Component-wise when import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './degrees.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract]_[non_]const -// abstract_non_const is empty and not used -const cases = (['f32', 'f16', 'abstract'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - if (trait === 'abstract' && nonConst) { - return []; - } - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - nonConst ? 'unfiltered' : 'finite', - FP[trait].degreesInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('degrees', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts new file mode 100644 index 000000000000..379b42b3e63c --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/determinant.cache.ts @@ -0,0 +1,84 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Accuracy for determinant is only defined for e, where e is an integer and +// |e| < quadroot(2**21) [~38], +// due to computational complexity of calculating the general solution for 4x4, +// so custom matrices are used. +// +// Note: For 2x2 and 3x3 the limits are squareroot and cuberoot instead of +// quadroot, but using the tighter 4x4 limits for all cases for simplicity. +const kDeterminantValues = [-38, -10, -5, -1, 0, 1, 5, 10, 38]; + +const kDeterminantMatrixValues = { + 2: kDeterminantValues.map((f, idx) => [ + [idx % 4 === 0 ? f : idx, idx % 4 === 1 ? f : -idx], + [idx % 4 === 2 ? f : -idx, idx % 4 === 3 ? f : idx], + ]), + 3: kDeterminantValues.map((f, idx) => [ + [idx % 9 === 0 ? f : idx, idx % 9 === 1 ? f : -idx, idx % 9 === 2 ? f : idx], + [idx % 9 === 3 ? f : -idx, idx % 9 === 4 ? f : idx, idx % 9 === 5 ? f : -idx], + [idx % 9 === 6 ? f : idx, idx % 9 === 7 ? f : -idx, idx % 9 === 8 ? f : idx], + ]), + 4: kDeterminantValues.map((f, idx) => [ + [ + idx % 16 === 0 ? f : idx, + idx % 16 === 1 ? f : -idx, + idx % 16 === 2 ? f : idx, + idx % 16 === 3 ? f : -idx, + ], + [ + idx % 16 === 4 ? f : -idx, + idx % 16 === 5 ? f : idx, + idx % 16 === 6 ? f : -idx, + idx % 16 === 7 ? f : idx, + ], + [ + idx % 16 === 8 ? f : idx, + idx % 16 === 9 ? f : -idx, + idx % 16 === 10 ? f : idx, + idx % 16 === 11 ? f : -idx, + ], + [ + idx % 16 === 12 ? f : -idx, + idx % 16 === 13 ? f : idx, + idx % 16 === 14 ? f : -idx, + idx % 16 === 15 ? f : idx, + ], + ]), +}; + +// Cases: f32_matDxD_[non_]const +const f32_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`f32_mat${dim}x${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateMatrixToScalarCases( + kDeterminantMatrixValues[dim], + nonConst ? 'unfiltered' : 'finite', + FP.f32.determinantInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: f16_matDxD_[non_]const +const f16_cases = ([2, 3, 4] as const) + .flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`f16_mat${dim}x${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f16.generateMatrixToScalarCases( + kDeterminantMatrixValues[dim], + nonConst ? 'unfiltered' : 'finite', + FP.f16.determinantInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('determinant', { + ...f32_cases, + ...f16_cases, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts index f08f4f0b6b25..c628623f4378 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts @@ -8,97 +8,14 @@ Returns the determinant of e. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16, TypeMat } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32, TypeMat } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './determinant.cache.js'; export const g = makeTestGroup(GPUTest); -// Accuracy for determinant is only defined for e, where e is an integer and -// |e| < quadroot(2**21) [~38], -// due to computational complexity of calculating the general solution for 4x4, -// so custom matrices are used. -// -// Note: For 2x2 and 3x3 the limits are squareroot and cuberoot instead of -// quadroot, but using the tighter 4x4 limits for all cases for simplicity. -const kDeterminantValues = [-38, -10, -5, -1, 0, 1, 5, 10, 38]; - -const kDeterminantMatrixValues = { - 2: kDeterminantValues.map((f, idx) => [ - [idx % 4 === 0 ? f : idx, idx % 4 === 1 ? f : -idx], - [idx % 4 === 2 ? f : -idx, idx % 4 === 3 ? f : idx], - ]), - 3: kDeterminantValues.map((f, idx) => [ - [idx % 9 === 0 ? f : idx, idx % 9 === 1 ? f : -idx, idx % 9 === 2 ? f : idx], - [idx % 9 === 3 ? f : -idx, idx % 9 === 4 ? f : idx, idx % 9 === 5 ? f : -idx], - [idx % 9 === 6 ? f : idx, idx % 9 === 7 ? f : -idx, idx % 9 === 8 ? f : idx], - ]), - 4: kDeterminantValues.map((f, idx) => [ - [ - idx % 16 === 0 ? f : idx, - idx % 16 === 1 ? f : -idx, - idx % 16 === 2 ? f : idx, - idx % 16 === 3 ? f : -idx, - ], - [ - idx % 16 === 4 ? f : -idx, - idx % 16 === 5 ? f : idx, - idx % 16 === 6 ? f : -idx, - idx % 16 === 7 ? f : idx, - ], - [ - idx % 16 === 8 ? f : idx, - idx % 16 === 9 ? f : -idx, - idx % 16 === 10 ? f : idx, - idx % 16 === 11 ? f : -idx, - ], - [ - idx % 16 === 12 ? f : -idx, - idx % 16 === 13 ? f : idx, - idx % 16 === 14 ? f : -idx, - idx % 16 === 15 ? f : idx, - ], - ]), -}; - -// Cases: f32_matDxD_[non_]const -const f32_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`f32_mat${dim}x${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateMatrixToScalarCases( - kDeterminantMatrixValues[dim], - nonConst ? 'unfiltered' : 'finite', - FP.f32.determinantInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: f16_matDxD_[non_]const -const f16_cases = ([2, 3, 4] as const) - .flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`f16_mat${dim}x${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f16.generateMatrixToScalarCases( - kDeterminantMatrixValues[dim], - nonConst ? 'unfiltered' : 'finite', - FP.f16.determinantInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('determinant', { - ...f32_cases, - ...f16_cases, -}); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/distance.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/distance.cache.ts new file mode 100644 index 000000000000..0e1bf291a50f --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/distance.cache.ts @@ -0,0 +1,41 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const scalar_cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarPairToIntervalCases( + FP[trait].scalarRange(), + FP[trait].scalarRange(), + nonConst ? 'unfiltered' : 'finite', + FP[trait].distanceInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: [f32|f16]_vecN_[non_]const +const vec_cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateVectorPairToIntervalCases( + FP[trait].sparseVectorRange(dim), + FP[trait].sparseVectorRange(dim), + nonConst ? 'unfiltered' : 'finite', + FP[trait].distanceInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('distance', { + ...scalar_cases, + ...vec_cases, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts index 69b9606bf0bc..ab63029ca227 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts @@ -10,54 +10,14 @@ Returns the distance between e1 and e2 (e.g. length(e1-e2)). import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './distance.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const scalar_cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarPairToIntervalCases( - FP[trait].scalarRange(), - FP[trait].scalarRange(), - nonConst ? 'unfiltered' : 'finite', - FP[trait].distanceInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: [f32|f16]_vecN_[non_]const -const vec_cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateVectorPairToIntervalCases( - FP[trait].sparseVectorRange(dim), - FP[trait].sparseVectorRange(dim), - nonConst ? 'unfiltered' : 'finite', - FP[trait].distanceInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('distance', { - ...scalar_cases, - ...vec_cases, -}); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/dot.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/dot.cache.ts new file mode 100644 index 000000000000..979e476f85f4 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/dot.cache.ts @@ -0,0 +1,24 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_vecN_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(N => + ([true, false] as const).map(nonConst => ({ + [`${trait}_vec${N}_${nonConst ? 'non_const' : 'const'}`]: () => { + // vec3 and vec4 require calculating all possible permutations, so their runtime is much + // longer per test, so only using sparse vectors for them. + return FP[trait].generateVectorPairToIntervalCases( + N === 2 ? FP[trait].vectorRange(2) : FP[trait].sparseVectorRange(N), + N === 2 ? FP[trait].vectorRange(2) : FP[trait].sparseVectorRange(N), + nonConst ? 'unfiltered' : 'finite', + FP[trait].dotInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('dot', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts index c9c7266b649e..215f3879dc2e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts @@ -8,37 +8,14 @@ Returns the dot product of e1 and e2. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './dot.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_vecN_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(N => - ([true, false] as const).map(nonConst => ({ - [`${trait}_vec${N}_${nonConst ? 'non_const' : 'const'}`]: () => { - // vec3 and vec4 require calculating all possible permutations, so their runtime is much - // longer per test, so only using sparse vectors for them. - return FP[trait].generateVectorPairToIntervalCases( - N === 2 ? FP[trait].vectorRange(2) : FP[trait].sparseVectorRange(N), - N === 2 ? FP[trait].vectorRange(2) : FP[trait].sparseVectorRange(N), - nonConst ? 'unfiltered' : 'finite', - FP[trait].dotInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('dot', cases); - g.test('abstract_int') .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions') .desc(`abstract int tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/exp.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/exp.cache.ts new file mode 100644 index 000000000000..d294ea4f538e --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/exp.cache.ts @@ -0,0 +1,40 @@ +import { kValue } from '../../../../../util/constants.js'; +import { FP } from '../../../../../util/floating_point.js'; +import { biasedRange, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// floor(ln(max f32 value)) = 88, so exp(88) will be within range of a f32, but exp(89) will not +// floor(ln(max f64 value)) = 709, so exp(709) can be handled by the testing framework, but exp(710) will misbehave +const f32_inputs = [ + 0, // Returns 1 by definition + -89, // Returns subnormal value + kValue.f32.negative.min, // Closest to returning 0 as possible + ...biasedRange(kValue.f32.negative.max, -88, 100), + ...biasedRange(kValue.f32.positive.min, 88, 100), + ...linearRange(89, 709, 10), // Overflows f32, but not f64 +]; + +// floor(ln(max f16 value)) = 11, so exp(11) will be within range of a f16, but exp(12) will not +const f16_inputs = [ + 0, // Returns 1 by definition + -12, // Returns subnormal value + kValue.f16.negative.min, // Closest to returning 0 as possible + ...biasedRange(kValue.f16.negative.max, -11, 100), + ...biasedRange(kValue.f16.positive.min, 11, 100), + ...linearRange(12, 709, 10), // Overflows f16, but not f64 +]; + +export const d = makeCaseCache('exp', { + f32_const: () => { + return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.expInterval); + }, + f32_non_const: () => { + return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.expInterval); + }, + f16_const: () => { + return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.expInterval); + }, + f16_non_const: () => { + return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.expInterval); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts index 8b1ced3cab80..ad381df01690 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts @@ -9,53 +9,14 @@ Returns the natural exponentiation of e1 (e.g. e^e1). Component-wise when T is a import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { kValue } from '../../../../../util/constants.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { biasedRange, linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './exp.cache.js'; export const g = makeTestGroup(GPUTest); -// floor(ln(max f32 value)) = 88, so exp(88) will be within range of a f32, but exp(89) will not -// floor(ln(max f64 value)) = 709, so exp(709) can be handled by the testing framework, but exp(710) will misbehave -const f32_inputs = [ - 0, // Returns 1 by definition - -89, // Returns subnormal value - kValue.f32.negative.min, // Closest to returning 0 as possible - ...biasedRange(kValue.f32.negative.max, -88, 100), - ...biasedRange(kValue.f32.positive.min, 88, 100), - ...linearRange(89, 709, 10), // Overflows f32, but not f64 -]; - -// floor(ln(max f16 value)) = 11, so exp(11) will be within range of a f16, but exp(12) will not -const f16_inputs = [ - 0, // Returns 1 by definition - -12, // Returns subnormal value - kValue.f16.negative.min, // Closest to returning 0 as possible - ...biasedRange(kValue.f16.negative.max, -11, 100), - ...biasedRange(kValue.f16.positive.min, 11, 100), - ...linearRange(12, 709, 10), // Overflows f16, but not f64 -]; - -export const d = makeCaseCache('exp', { - f32_const: () => { - return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.expInterval); - }, - f32_non_const: () => { - return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.expInterval); - }, - f16_const: () => { - return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.expInterval); - }, - f16_non_const: () => { - return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.expInterval); - }, -}); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/exp2.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/exp2.cache.ts new file mode 100644 index 000000000000..edb4b613baf2 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/exp2.cache.ts @@ -0,0 +1,40 @@ +import { kValue } from '../../../../../util/constants.js'; +import { FP } from '../../../../../util/floating_point.js'; +import { biasedRange, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// floor(log2(max f32 value)) = 127, so exp2(127) will be within range of a f32, but exp2(128) will not +// floor(ln(max f64 value)) = 1023, so exp2(1023) can be handled by the testing framework, but exp2(1024) will misbehave +const f32_inputs = [ + 0, // Returns 1 by definition + -128, // Returns subnormal value + kValue.f32.negative.min, // Closest to returning 0 as possible + ...biasedRange(kValue.f32.negative.max, -127, 100), + ...biasedRange(kValue.f32.positive.min, 127, 100), + ...linearRange(128, 1023, 10), // Overflows f32, but not f64 +]; + +// floor(log2(max f16 value)) = 15, so exp2(15) will be within range of a f16, but exp2(15) will not +const f16_inputs = [ + 0, // Returns 1 by definition + -16, // Returns subnormal value + kValue.f16.negative.min, // Closest to returning 0 as possible + ...biasedRange(kValue.f16.negative.max, -15, 100), + ...biasedRange(kValue.f16.positive.min, 15, 100), + ...linearRange(16, 1023, 10), // Overflows f16, but not f64 +]; + +export const d = makeCaseCache('exp2', { + f32_const: () => { + return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.exp2Interval); + }, + f32_non_const: () => { + return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.exp2Interval); + }, + f16_const: () => { + return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.exp2Interval); + }, + f16_non_const: () => { + return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.exp2Interval); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts index 67e123cb3012..cf0e3c3cbe81 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts @@ -9,53 +9,14 @@ Returns 2 raised to the power e (e.g. 2^e). Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { kValue } from '../../../../../util/constants.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { biasedRange, linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './exp2.cache.js'; export const g = makeTestGroup(GPUTest); -// floor(log2(max f32 value)) = 127, so exp2(127) will be within range of a f32, but exp2(128) will not -// floor(ln(max f64 value)) = 1023, so exp2(1023) can be handled by the testing framework, but exp2(1024) will misbehave -const f32_inputs = [ - 0, // Returns 1 by definition - -128, // Returns subnormal value - kValue.f32.negative.min, // Closest to returning 0 as possible - ...biasedRange(kValue.f32.negative.max, -127, 100), - ...biasedRange(kValue.f32.positive.min, 127, 100), - ...linearRange(128, 1023, 10), // Overflows f32, but not f64 -]; - -// floor(log2(max f16 value)) = 15, so exp2(15) will be within range of a f16, but exp2(15) will not -const f16_inputs = [ - 0, // Returns 1 by definition - -16, // Returns subnormal value - kValue.f16.negative.min, // Closest to returning 0 as possible - ...biasedRange(kValue.f16.negative.max, -15, 100), - ...biasedRange(kValue.f16.positive.min, 15, 100), - ...linearRange(16, 1023, 10), // Overflows f16, but not f64 -]; - -export const d = makeCaseCache('exp2', { - f32_const: () => { - return FP.f32.generateScalarToIntervalCases(f32_inputs, 'finite', FP.f32.exp2Interval); - }, - f32_non_const: () => { - return FP.f32.generateScalarToIntervalCases(f32_inputs, 'unfiltered', FP.f32.exp2Interval); - }, - f16_const: () => { - return FP.f16.generateScalarToIntervalCases(f16_inputs, 'finite', FP.f16.exp2Interval); - }, - f16_non_const: () => { - return FP.f16.generateScalarToIntervalCases(f16_inputs, 'unfiltered', FP.f16.exp2Interval); - }, -}); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/faceForward.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/faceForward.cache.ts new file mode 100644 index 000000000000..c44c88d276a5 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/faceForward.cache.ts @@ -0,0 +1,98 @@ +import { ROArrayArray } from '../../../../../../common/util/types.js'; +import { anyOf } from '../../../../../util/compare.js'; +import { toVector } from '../../../../../util/conversion.js'; +import { FP, FPKind, FPVector } from '../../../../../util/floating_point.js'; +import { cartesianProduct } from '../../../../../util/math.js'; +import { Case } from '../../case.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { IntervalFilter } from '../../interval_filter.js'; + +// Using a bespoke implementation of make*Case and generate*Cases here +// since faceForwardIntervals is the only builtin with the API signature +// (vec, vec, vec) -> vec +// +// Additionally faceForward has significant complexities around it due to the +// fact that `dot` is calculated in it s operation, but the result of dot isn't +// used to calculate the builtin's result. + +/** + * @returns a Case for `faceForward` + * @param kind what kind of floating point numbers being operated on + * @param x the `x` param for the case + * @param y the `y` param for the case + * @param z the `z` param for the case + * @param check what interval checking to apply + * */ +function makeCase( + kind: FPKind, + x: readonly number[], + y: readonly number[], + z: readonly number[], + check: IntervalFilter +): Case | undefined { + const fp = FP[kind]; + x = x.map(fp.quantize); + y = y.map(fp.quantize); + z = z.map(fp.quantize); + + const results = FP[kind].faceForwardIntervals(x, y, z); + if (check === 'finite' && results.some(r => r === undefined)) { + return undefined; + } + + // Stripping the undefined results, since undefined is used to signal that an OOB + // could occur within the calculation that isn't reflected in the result + // intervals. + const define_results = results.filter((r): r is FPVector => r !== undefined); + + return { + input: [ + toVector(x, fp.scalarBuilder), + toVector(y, fp.scalarBuilder), + toVector(z, fp.scalarBuilder), + ], + expected: anyOf(...define_results), + }; +} + +/** + * @returns an array of Cases for `faceForward` + * @param kind what kind of floating point numbers being operated on + * @param xs array of inputs to try for the `x` param + * @param ys array of inputs to try for the `y` param + * @param zs array of inputs to try for the `z` param + * @param check what interval checking to apply + */ +function generateCases( + kind: FPKind, + xs: ROArrayArray, + ys: ROArrayArray, + zs: ROArrayArray, + check: IntervalFilter +): Case[] { + // Cannot use `cartesianProduct` here due to heterogeneous param types + return cartesianProduct(xs, ys, zs) + .map(e => makeCase(kind, e[0], e[1], e[2], check)) + .filter((c): c is Case => c !== undefined); +} + +// Cases: [f32|f16]_vecN_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return generateCases( + trait, + FP[trait].sparseVectorRange(dim), + FP[trait].sparseVectorRange(dim), + FP[trait].sparseVectorRange(dim), + nonConst ? 'unfiltered' : 'finite' + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('faceForward', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts index 767dbf35a894..c21541ab8839 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts @@ -7,109 +7,15 @@ Returns e1 if dot(e2,e3) is negative, and -e1 otherwise. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { ROArrayArray } from '../../../../../../common/util/types.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { anyOf } from '../../../../../util/compare.js'; -import { toVector, TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js'; -import { FP, FPKind, FPVector } from '../../../../../util/floating_point.js'; -import { cartesianProduct } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { allInputSources, Case, IntervalFilter, run } from '../../expression.js'; +import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './faceForward.cache.js'; export const g = makeTestGroup(GPUTest); -// Using a bespoke implementation of make*Case and generate*Cases here -// since faceForwardIntervals is the only builtin with the API signature -// (vec, vec, vec) -> vec -// -// Additionally faceForward has significant complexities around it due to the -// fact that `dot` is calculated in it s operation, but the result of dot isn't -// used to calculate the builtin's result. - -/** - * @returns a Case for `faceForward` - * @param kind what kind of floating point numbers being operated on - * @param x the `x` param for the case - * @param y the `y` param for the case - * @param z the `z` param for the case - * @param check what interval checking to apply - * */ -function makeCase( - kind: FPKind, - x: readonly number[], - y: readonly number[], - z: readonly number[], - check: IntervalFilter -): Case | undefined { - const fp = FP[kind]; - x = x.map(fp.quantize); - y = y.map(fp.quantize); - z = z.map(fp.quantize); - - const results = FP[kind].faceForwardIntervals(x, y, z); - if (check === 'finite' && results.some(r => r === undefined)) { - return undefined; - } - - // Stripping the undefined results, since undefined is used to signal that an OOB - // could occur within the calculation that isn't reflected in the result - // intervals. - const define_results = results.filter((r): r is FPVector => r !== undefined); - - return { - input: [ - toVector(x, fp.scalarBuilder), - toVector(y, fp.scalarBuilder), - toVector(z, fp.scalarBuilder), - ], - expected: anyOf(...define_results), - }; -} - -/** - * @returns an array of Cases for `faceForward` - * @param kind what kind of floating point numbers being operated on - * @param xs array of inputs to try for the `x` param - * @param ys array of inputs to try for the `y` param - * @param zs array of inputs to try for the `z` param - * @param check what interval checking to apply - */ -function generateCases( - kind: FPKind, - xs: ROArrayArray, - ys: ROArrayArray, - zs: ROArrayArray, - check: IntervalFilter -): Case[] { - // Cannot use `cartesianProduct` here due to heterogeneous param types - return cartesianProduct(xs, ys, zs) - .map(e => makeCase(kind, e[0], e[1], e[2], check)) - .filter((c): c is Case => c !== undefined); -} - -// Cases: [f32|f16]_vecN_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return generateCases( - trait, - FP[trait].sparseVectorRange(dim), - FP[trait].sparseVectorRange(dim), - FP[trait].sparseVectorRange(dim), - nonConst ? 'unfiltered' : 'finite' - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('faceForward', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/floor.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/floor.cache.ts new file mode 100644 index 000000000000..0af966cfd257 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/floor.cache.ts @@ -0,0 +1,26 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +const kSmallMagnitudeTestValues = [0.1, 0.9, 1.0, 1.1, 1.9, -0.1, -0.9, -1.0, -1.1, -1.9]; + +// See https://github.com/gpuweb/cts/issues/2766 for details +const kIssue2766Value = { + abstract: 0x8000_0000_0000_0000, + f32: 0x8000_0000, + f16: 0x8000, +}; + +// Cases: [f32|f16|abstract] +const cases = (['f32', 'f16', 'abstract'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [...kSmallMagnitudeTestValues, kIssue2766Value[trait], ...FP[trait].scalarRange()], + 'unfiltered', + FP[trait].floorInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('floor', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts index d756042a9e86..08a29ed3e5f4 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts @@ -9,39 +9,14 @@ Returns the floor of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16, TypeAbstractFloat } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './floor.cache.js'; export const g = makeTestGroup(GPUTest); -const kSmallMagnitudeTestValues = [0.1, 0.9, 1.0, 1.1, 1.9, -0.1, -0.9, -1.0, -1.1, -1.9]; - -// See https://github.com/gpuweb/cts/issues/2766 for details -const kIssue2766Value = { - abstract: 0x8000_0000_0000_0000, - f32: 0x8000_0000, - f16: 0x8000, -}; - -// Cases: [f32|f16|abstract] -const cases = (['f32', 'f16', 'abstract'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [...kSmallMagnitudeTestValues, kIssue2766Value[trait], ...FP[trait].scalarRange()], - 'unfiltered', - FP[trait].floorInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('floor', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/fma.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/fma.cache.ts new file mode 100644 index 000000000000..0efa57f39c73 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/fma.cache.ts @@ -0,0 +1,25 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract]_[non_]const +// abstract_non_const is empty and not used +const cases = (['f32', 'f16', 'abstract'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + if (trait === 'abstract' && nonConst) { + return []; + } + return FP[trait].generateScalarTripleToIntervalCases( + FP[trait].sparseScalarRange(), + FP[trait].sparseScalarRange(), + FP[trait].sparseScalarRange(), + nonConst ? 'unfiltered' : 'finite', + FP[trait].fmaInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('fma', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts index 6a275afad6f5..39360cb5bc51 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts @@ -9,38 +9,14 @@ Returns e1 * e2 + e3. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16, TypeAbstractFloat } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './fma.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract]_[non_]const -// abstract_non_const is empty and not used -const cases = (['f32', 'f16', 'abstract'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - if (trait === 'abstract' && nonConst) { - return []; - } - return FP[trait].generateScalarTripleToIntervalCases( - FP[trait].sparseScalarRange(), - FP[trait].sparseScalarRange(), - FP[trait].sparseScalarRange(), - nonConst ? 'unfiltered' : 'finite', - FP[trait].fmaInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('fma', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/fract.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/fract.cache.ts new file mode 100644 index 000000000000..56fa3f661f1f --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/fract.cache.ts @@ -0,0 +1,45 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +const kCommonValues = [ + 0.5, // 0.5 -> 0.5 + 0.9, // ~0.9 -> ~0.9 + 1, // 1 -> 0 + 2, // 2 -> 0 + 1.11, // ~1.11 -> ~0.11 + -0.1, // ~-0.1 -> ~0.9 + -0.5, // -0.5 -> 0.5 + -0.9, // ~-0.9 -> ~0.1 + -1, // -1 -> 0 + -2, // -2 -> 0 + -1.11, // ~-1.11 -> ~0.89 +]; + +const kTraitSpecificValues = { + f32: [ + 10.0001, // ~10.0001 -> ~0.0001 + -10.0001, // -10.0001 -> ~0.9999 + 0x8000_0000, // https://github.com/gpuweb/cts/issues/2766 + ], + f16: [ + 10.0078125, // 10.0078125 -> 0.0078125 + -10.0078125, // -10.0078125 -> 0.9921875 + 658.5, // 658.5 -> 0.5 + 0x8000, // https://github.com/gpuweb/cts/issues/2766 + ], +}; + +// Cases: [f32|f16] +const cases = (['f32', 'f16'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [...kCommonValues, ...kTraitSpecificValues[trait], ...FP[trait].scalarRange()], + 'unfiltered', + FP[trait].fractInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('fract', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts index 9f412129733a..df9e1845273e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts @@ -10,58 +10,14 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './fract.cache.js'; export const g = makeTestGroup(GPUTest); -const kCommonValues = [ - 0.5, // 0.5 -> 0.5 - 0.9, // ~0.9 -> ~0.9 - 1, // 1 -> 0 - 2, // 2 -> 0 - 1.11, // ~1.11 -> ~0.11 - -0.1, // ~-0.1 -> ~0.9 - -0.5, // -0.5 -> 0.5 - -0.9, // ~-0.9 -> ~0.1 - -1, // -1 -> 0 - -2, // -2 -> 0 - -1.11, // ~-1.11 -> ~0.89 -]; - -const kTraitSpecificValues = { - f32: [ - 10.0001, // ~10.0001 -> ~0.0001 - -10.0001, // -10.0001 -> ~0.9999 - 0x8000_0000, // https://github.com/gpuweb/cts/issues/2766 - ], - f16: [ - 10.0078125, // 10.0078125 -> 0.0078125 - -10.0078125, // -10.0078125 -> 0.9921875 - 658.5, // 658.5 -> 0.5 - 0x8000, // https://github.com/gpuweb/cts/issues/2766 - ], -}; - -// Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [...kCommonValues, ...kTraitSpecificValues[trait], ...FP[trait].scalarRange()], - 'unfiltered', - FP[trait].fractInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('fract', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts new file mode 100644 index 000000000000..7c107eb729a7 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts @@ -0,0 +1,95 @@ +import { skipUndefined } from '../../../../../util/compare.js'; +import { Scalar, Vector, i32, toVector } from '../../../../../util/conversion.js'; +import { FP } from '../../../../../util/floating_point.js'; +import { frexp } from '../../../../../util/math.js'; +import { Case } from '../../case.js'; +import { makeCaseCache } from '../../case_cache.js'; + +/* @returns a fract Case for a given scalar or vector input */ +function makeCaseFract(v: number | readonly number[], trait: 'f32' | 'f16'): Case { + const fp = FP[trait]; + let toInput: (n: readonly number[]) => Scalar | Vector; + let toOutput: (n: readonly number[]) => Scalar | Vector; + if (v instanceof Array) { + // Input is vector + toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder); + toOutput = (n: readonly number[]) => toVector(n, fp.scalarBuilder); + } else { + // Input is scalar, also wrap it in an array. + v = [v]; + toInput = (n: readonly number[]) => fp.scalarBuilder(n[0]); + toOutput = (n: readonly number[]) => fp.scalarBuilder(n[0]); + } + + v = v.map(fp.quantize); + if (v.some(e => e !== 0 && fp.isSubnormal(e))) { + return { input: toInput(v), expected: skipUndefined(undefined) }; + } + + const fs = v.map(e => { + return frexp(e, trait).fract; + }); + + return { input: toInput(v), expected: toOutput(fs) }; +} + +/* @returns an exp Case for a given scalar or vector input */ +function makeCaseExp(v: number | readonly number[], trait: 'f32' | 'f16'): Case { + const fp = FP[trait]; + let toInput: (n: readonly number[]) => Scalar | Vector; + let toOutput: (n: readonly number[]) => Scalar | Vector; + if (v instanceof Array) { + // Input is vector + toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder); + toOutput = (n: readonly number[]) => toVector(n, i32); + } else { + // Input is scalar, also wrap it in an array. + v = [v]; + toInput = (n: readonly number[]) => fp.scalarBuilder(n[0]); + toOutput = (n: readonly number[]) => i32(n[0]); + } + + v = v.map(fp.quantize); + if (v.some(e => e !== 0 && fp.isSubnormal(e))) { + return { input: toInput(v), expected: skipUndefined(undefined) }; + } + + const fs = v.map(e => { + return frexp(e, trait).exp; + }); + + return { input: toInput(v), expected: toOutput(fs) }; +} + +// Cases: [f32|f16]_vecN_[exp|whole] +const vec_cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(dim => + (['exp', 'fract'] as const).map(portion => ({ + [`${trait}_vec${dim}_${portion}`]: () => { + return FP[trait] + .vectorRange(dim) + .map(v => (portion === 'exp' ? makeCaseExp(v, trait) : makeCaseFract(v, trait))); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: [f32|f16]_[exp|whole] +const scalar_cases = (['f32', 'f16'] as const) + .flatMap(trait => + (['exp', 'fract'] as const).map(portion => ({ + [`${trait}_${portion}`]: () => { + return FP[trait] + .scalarRange() + .map(v => (portion === 'exp' ? makeCaseExp(v, trait) : makeCaseFract(v, trait))); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('frexp', { + ...scalar_cases, + ...vec_cases, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts index 32565ef23667..663e345f499a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts @@ -15,27 +15,10 @@ The magnitude of the significand is in the range of [0.5, 1.0) or 0. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { skipUndefined } from '../../../../../util/compare.js'; -import { - i32, - Scalar, - toVector, - TypeF32, - TypeF16, - TypeI32, - TypeVec, - Vector, -} from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { frexp } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { - allInputSources, - basicExpressionBuilder, - Case, - run, - ShaderBuilder, -} from '../../expression.js'; +import { TypeF16, TypeF32, TypeI32, TypeVec } from '../../../../../util/conversion.js'; +import { ShaderBuilder, allInputSources, basicExpressionBuilder, run } from '../../expression.js'; + +import { d } from './frexp.cache.js'; export const g = makeTestGroup(GPUTest); @@ -48,96 +31,6 @@ function fractBuilder(): ShaderBuilder { function expBuilder(): ShaderBuilder { return basicExpressionBuilder(value => `frexp(${value}).exp`); } - -/* @returns a fract Case for a given scalar or vector input */ -function makeCaseFract(v: number | readonly number[], trait: 'f32' | 'f16'): Case { - const fp = FP[trait]; - let toInput: (n: readonly number[]) => Scalar | Vector; - let toOutput: (n: readonly number[]) => Scalar | Vector; - if (v instanceof Array) { - // Input is vector - toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder); - toOutput = (n: readonly number[]) => toVector(n, fp.scalarBuilder); - } else { - // Input is scalar, also wrap it in an array. - v = [v]; - toInput = (n: readonly number[]) => fp.scalarBuilder(n[0]); - toOutput = (n: readonly number[]) => fp.scalarBuilder(n[0]); - } - - v = v.map(fp.quantize); - if (v.some(e => e !== 0 && fp.isSubnormal(e))) { - return { input: toInput(v), expected: skipUndefined(undefined) }; - } - - const fs = v.map(e => { - return frexp(e, trait).fract; - }); - - return { input: toInput(v), expected: toOutput(fs) }; -} - -/* @returns an exp Case for a given scalar or vector input */ -function makeCaseExp(v: number | readonly number[], trait: 'f32' | 'f16'): Case { - const fp = FP[trait]; - let toInput: (n: readonly number[]) => Scalar | Vector; - let toOutput: (n: readonly number[]) => Scalar | Vector; - if (v instanceof Array) { - // Input is vector - toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder); - toOutput = (n: readonly number[]) => toVector(n, i32); - } else { - // Input is scalar, also wrap it in an array. - v = [v]; - toInput = (n: readonly number[]) => fp.scalarBuilder(n[0]); - toOutput = (n: readonly number[]) => i32(n[0]); - } - - v = v.map(fp.quantize); - if (v.some(e => e !== 0 && fp.isSubnormal(e))) { - return { input: toInput(v), expected: skipUndefined(undefined) }; - } - - const fs = v.map(e => { - return frexp(e, trait).exp; - }); - - return { input: toInput(v), expected: toOutput(fs) }; -} - -// Cases: [f32|f16]_vecN_[exp|whole] -const vec_cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(dim => - (['exp', 'fract'] as const).map(portion => ({ - [`${trait}_vec${dim}_${portion}`]: () => { - return FP[trait] - .vectorRange(dim) - .map(v => (portion === 'exp' ? makeCaseExp(v, trait) : makeCaseFract(v, trait))); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: [f32|f16]_[exp|whole] -const scalar_cases = (['f32', 'f16'] as const) - .flatMap(trait => - (['exp', 'fract'] as const).map(portion => ({ - [`${trait}_${portion}`]: () => { - return FP[trait] - .scalarRange() - .map(v => (portion === 'exp' ? makeCaseExp(v, trait) : makeCaseFract(v, trait))); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('frexp', { - ...scalar_cases, - ...vec_cases, -}); - g.test('f32_fract') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc( diff --git a/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.cache.ts new file mode 100644 index 000000000000..3ad58d5fc12c --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.cache.ts @@ -0,0 +1,31 @@ +import { kValue } from '../../../../../util/constants.js'; +import { FP } from '../../../../../util/floating_point.js'; +import { biasedRange, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +export const d = makeCaseCache('inverseSqrt', { + f32: () => { + return FP.f32.generateScalarToIntervalCases( + [ + // 0 < x <= 1 linearly spread + ...linearRange(kValue.f32.positive.min, 1, 100), + // 1 <= x < 2^32, biased towards 1 + ...biasedRange(1, 2 ** 32, 1000), + ], + 'unfiltered', + FP.f32.inverseSqrtInterval + ); + }, + f16: () => { + return FP.f16.generateScalarToIntervalCases( + [ + // 0 < x <= 1 linearly spread + ...linearRange(kValue.f16.positive.min, 1, 100), + // 1 <= x < 2^15, biased towards 1 + ...biasedRange(1, 2 ** 15, 1000), + ], + 'unfiltered', + FP.f16.inverseSqrtInterval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts index 3e83816387d8..fb5248ec8ad6 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts @@ -9,44 +9,14 @@ Returns the reciprocal of sqrt(e). Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { kValue } from '../../../../../util/constants.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { biasedRange, linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './inversesqrt.cache.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('inverseSqrt', { - f32: () => { - return FP.f32.generateScalarToIntervalCases( - [ - // 0 < x <= 1 linearly spread - ...linearRange(kValue.f32.positive.min, 1, 100), - // 1 <= x < 2^32, biased towards 1 - ...biasedRange(1, 2 ** 32, 1000), - ], - 'unfiltered', - FP.f32.inverseSqrtInterval - ); - }, - f16: () => { - return FP.f16.generateScalarToIntervalCases( - [ - // 0 < x <= 1 linearly spread - ...linearRange(kValue.f16.positive.min, 1, 100), - // 1 <= x < 2^15, biased towards 1 - ...biasedRange(1, 2 ** 15, 1000), - ], - 'unfiltered', - FP.f16.inverseSqrtInterval - ); - }, -}); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/ldexp.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/ldexp.cache.ts new file mode 100644 index 000000000000..edee0a7f3d89 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/ldexp.cache.ts @@ -0,0 +1,61 @@ +import { assert } from '../../../../../../common/util/util.js'; +import { anyOf } from '../../../../../util/compare.js'; +import { i32 } from '../../../../../util/conversion.js'; +import { FP } from '../../../../../util/floating_point.js'; +import { biasedRange, quantizeToI32, sparseI32Range } from '../../../../../util/math.js'; +import { Case } from '../../case.js'; +import { makeCaseCache } from '../../case_cache.js'; + +const bias = { + f32: 127, + f16: 15, +} as const; + +// ldexpInterval's return interval doesn't cover the flush-to-zero cases when e2 + bias <= 0, thus +// special examination is required. +// See the comment block on ldexpInterval for more details +// e2 is an integer (i32) while e1 is float. +const makeCase = (trait: 'f32' | 'f16', e1: number, e2: number): Case => { + const FPTrait = FP[trait]; + e1 = FPTrait.quantize(e1); + // e2 should be in i32 range for the convinience. + assert(-2147483648 <= e2 && e2 <= 2147483647, 'e2 should be in i32 range'); + e2 = quantizeToI32(e2); + + const expected = FPTrait.ldexpInterval(e1, e2); + + // Result may be zero if e2 + bias <= 0 + if (e2 + bias[trait] <= 0) { + return { + input: [FPTrait.scalarBuilder(e1), i32(e2)], + expected: anyOf(expected, FPTrait.constants().zeroInterval), + }; + } + + return { input: [FPTrait.scalarBuilder(e1), i32(e2)], expected }; +}; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + if (nonConst) { + return FP[trait] + .sparseScalarRange() + .flatMap(e1 => sparseI32Range().map(e2 => makeCase(trait, e1, e2))); + } + // const + return FP[trait] + .sparseScalarRange() + .flatMap(e1 => + biasedRange(-bias[trait] - 10, bias[trait] + 1, 10).flatMap(e2 => + FP[trait].isFinite(e1 * 2 ** quantizeToI32(e2)) ? makeCase(trait, e1, e2) : [] + ) + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('ldexp', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts index 2eec0c8ead5d..e2eba8232d43 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts @@ -13,73 +13,15 @@ Returns e1 * 2^e2. Component-wise when T is a vector. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { assert } from '../../../../../../common/util/util.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { anyOf } from '../../../../../util/compare.js'; -import { i32, TypeF32, TypeF16, TypeI32 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { biasedRange, quantizeToI32, sparseI32Range } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { allInputSources, Case, run } from '../../expression.js'; +import { TypeF16, TypeF32, TypeI32 } from '../../../../../util/conversion.js'; +import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './ldexp.cache.js'; export const g = makeTestGroup(GPUTest); -const bias = { - f32: 127, - f16: 15, -} as const; - -// ldexpInterval's return interval doesn't cover the flush-to-zero cases when e2 + bias <= 0, thus -// special examination is required. -// See the comment block on ldexpInterval for more details -// e2 is an integer (i32) while e1 is float. -const makeCase = (trait: 'f32' | 'f16', e1: number, e2: number): Case => { - const FPTrait = FP[trait]; - e1 = FPTrait.quantize(e1); - // e2 should be in i32 range for the convinience. - assert(-2147483648 <= e2 && e2 <= 2147483647, 'e2 should be in i32 range'); - e2 = quantizeToI32(e2); - - const expected = FPTrait.ldexpInterval(e1, e2); - - // Result may be zero if e2 + bias <= 0 - if (e2 + bias[trait] <= 0) { - return { - input: [FPTrait.scalarBuilder(e1), i32(e2)], - expected: anyOf(expected, FPTrait.constants().zeroInterval), - }; - } - - return { input: [FPTrait.scalarBuilder(e1), i32(e2)], expected }; -}; - -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - if (nonConst) { - return FP[trait] - .sparseScalarRange() - .flatMap(e1 => sparseI32Range().map(e2 => makeCase(trait, e1, e2))); - } - // const - return FP[trait] - .sparseScalarRange() - .flatMap(e1 => - biasedRange(-bias[trait] - 10, bias[trait] + 1, 10).flatMap(e2 => - FP[trait].isFinite(e1 * 2 ** quantizeToI32(e2)) ? makeCase(trait, e1, e2) : [] - ) - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('ldexp', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc( diff --git a/src/webgpu/shader/execution/expression/call/builtin/length.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/length.cache.ts new file mode 100644 index 000000000000..f194352ff7f5 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/length.cache.ts @@ -0,0 +1,37 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const scalar_cases = (['f32', 'f16'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + 'unfiltered', + FP[trait].lengthInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: [f32|f16]_vecN_[non_]const +const vec_cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateVectorToIntervalCases( + FP[trait].vectorRange(dim), + nonConst ? 'unfiltered' : 'finite', + FP[trait].lengthInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('length', { + ...scalar_cases, + ...vec_cases, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts index 82028dfdf98f..e91d5dcdafe5 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts @@ -9,50 +9,14 @@ Returns the length of e (e.g. abs(e) if T is a scalar, or sqrt(e[0]^2 + e[1]^2 + import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './length.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const scalar_cases = (['f32', 'f16'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - 'unfiltered', - FP[trait].lengthInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: [f32|f16]_vecN_[non_]const -const vec_cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateVectorToIntervalCases( - FP[trait].vectorRange(dim), - nonConst ? 'unfiltered' : 'finite', - FP[trait].lengthInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('length', { - ...scalar_cases, - ...vec_cases, -}); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/log.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/log.cache.ts new file mode 100644 index 000000000000..11d900f7dc53 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/log.cache.ts @@ -0,0 +1,26 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { biasedRange, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// log's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] } +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [ + ...linearRange(FP[trait].constants().positive.min, 0.5, 20), + ...linearRange(0.5, 2.0, 20), + ...biasedRange(2.0, 2 ** 32, 1000), + ...FP[trait].scalarRange(), + ], + nonConst ? 'unfiltered' : 'finite', + FP[trait].logInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('log', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts index 19e367611f49..387705320ff9 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts @@ -9,39 +9,14 @@ Returns the natural logarithm of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { biasedRange, linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './log.cache.js'; export const g = makeTestGroup(GPUTest); -// log's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] } -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [ - ...linearRange(FP[trait].constants().positive.min, 0.5, 20), - ...linearRange(0.5, 2.0, 20), - ...biasedRange(2.0, 2 ** 32, 1000), - ...FP[trait].scalarRange(), - ], - nonConst ? 'unfiltered' : 'finite', - FP[trait].logInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('log', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/log2.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/log2.cache.ts new file mode 100644 index 000000000000..310ae2b85cae --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/log2.cache.ts @@ -0,0 +1,26 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { biasedRange, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// log2's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] } +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [ + ...linearRange(FP[trait].constants().positive.min, 0.5, 20), + ...linearRange(0.5, 2.0, 20), + ...biasedRange(2.0, 2 ** 32, 1000), + ...FP[trait].scalarRange(), + ], + nonConst ? 'unfiltered' : 'finite', + FP[trait].log2Interval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('log2', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts index 2850a6410033..86a38a60246a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts @@ -9,39 +9,14 @@ Returns the base-2 logarithm of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { biasedRange, linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './log2.cache.js'; export const g = makeTestGroup(GPUTest); -// log2's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] } -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [ - ...linearRange(FP[trait].constants().positive.min, 0.5, 20), - ...linearRange(0.5, 2.0, 20), - ...biasedRange(2.0, 2 ** 32, 1000), - ...FP[trait].scalarRange(), - ], - nonConst ? 'unfiltered' : 'finite', - FP[trait].log2Interval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('log2', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/max.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/max.cache.ts new file mode 100644 index 000000000000..7566d9342fa5 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/max.cache.ts @@ -0,0 +1,18 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract] +const cases = (['f32', 'f16', 'abstract'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarPairToIntervalCases( + FP[trait].scalarRange(), + FP[trait].scalarRange(), + 'unfiltered', + FP[trait].maxInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('max', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts index 7a7d199f4eb8..a596c3f12486 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts @@ -19,19 +19,19 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { - i32, - TypeF32, + TypeAbstractFloat, TypeF16, + TypeF32, TypeI32, TypeU32, + i32, u32, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { allInputSources, Case, onlyConstInputSource, run } from '../../expression.js'; +import { Case } from '../../case.js'; +import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './max.cache.js'; /** Generate set of max test cases from list of interesting values */ function generateTestCases( @@ -49,22 +49,6 @@ function generateTestCases( export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract] -const cases = (['f32', 'f16', 'abstract'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarPairToIntervalCases( - FP[trait].scalarRange(), - FP[trait].scalarRange(), - 'unfiltered', - FP[trait].maxInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('max', cases); - g.test('abstract_int') .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') .desc(`abstract int tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/min.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/min.cache.ts new file mode 100644 index 000000000000..83c986a392ed --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/min.cache.ts @@ -0,0 +1,18 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract] +const cases = (['f32', 'f16', 'abstract'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarPairToIntervalCases( + FP[trait].scalarRange(), + FP[trait].scalarRange(), + 'unfiltered', + FP[trait].minInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('min', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts index 68fb5be8d2d6..a9ada22da702 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts @@ -18,19 +18,19 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { - i32, - TypeF32, + TypeAbstractFloat, TypeF16, + TypeF32, TypeI32, TypeU32, + i32, u32, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { allInputSources, Case, onlyConstInputSource, run } from '../../expression.js'; +import { Case } from '../../case.js'; +import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './min.cache.js'; export const g = makeTestGroup(GPUTest); @@ -48,22 +48,6 @@ function generateTestCases( return cases; } -// Cases: [f32|f16|abstract] -const cases = (['f32', 'f16', 'abstract'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarPairToIntervalCases( - FP[trait].scalarRange(), - FP[trait].scalarRange(), - 'unfiltered', - FP[trait].minInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('min', cases); - g.test('abstract_int') .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') .desc(`abstract int tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/mix.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/mix.cache.ts new file mode 100644 index 000000000000..12176f87d4cf --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/mix.cache.ts @@ -0,0 +1,43 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const scalar_cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarTripleToIntervalCases( + FP[trait].sparseScalarRange(), + FP[trait].sparseScalarRange(), + FP[trait].sparseScalarRange(), + nonConst ? 'unfiltered' : 'finite', + ...FP[trait].mixIntervals + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: [f32|f16]_vecN_scalar_[non_]const +const vec_scalar_cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`${trait}_vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateVectorPairScalarToVectorComponentWiseCase( + FP[trait].sparseVectorRange(dim), + FP[trait].sparseVectorRange(dim), + FP[trait].sparseScalarRange(), + nonConst ? 'unfiltered' : 'finite', + ...FP[trait].mixIntervals + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('mix', { + ...scalar_cases, + ...vec_scalar_cases, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts index 80b28508ecef..c4d443698746 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts @@ -16,56 +16,14 @@ Same as mix(e1,e2,T2(e3)). import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeVec, TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './mix.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const scalar_cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarTripleToIntervalCases( - FP[trait].sparseScalarRange(), - FP[trait].sparseScalarRange(), - FP[trait].sparseScalarRange(), - nonConst ? 'unfiltered' : 'finite', - ...FP[trait].mixIntervals - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: [f32|f16]_vecN_scalar_[non_]const -const vec_scalar_cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`${trait}_vec${dim}_scalar_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateVectorPairScalarToVectorComponentWiseCase( - FP[trait].sparseVectorRange(dim), - FP[trait].sparseVectorRange(dim), - FP[trait].sparseScalarRange(), - nonConst ? 'unfiltered' : 'finite', - ...FP[trait].mixIntervals - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('mix', { - ...scalar_cases, - ...vec_scalar_cases, -}); - g.test('abstract_float_matching') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract_float test with matching third param`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/modf.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/modf.cache.ts new file mode 100644 index 000000000000..1a76de56bbae --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/modf.cache.ts @@ -0,0 +1,75 @@ +import { toVector } from '../../../../../util/conversion.js'; +import { FP, FPKind } from '../../../../../util/floating_point.js'; +import { Case } from '../../case.js'; +import { makeCaseCache } from '../../case_cache.js'; + +/** @returns a fract Case for a scalar vector input */ +function makeScalarCaseFract(kind: FPKind, n: number): Case { + const fp = FP[kind]; + n = fp.quantize(n); + const result = fp.modfInterval(n).fract; + + return { input: fp.scalarBuilder(n), expected: result }; +} + +/** @returns a whole Case for a scalar vector input */ +function makeScalarCaseWhole(kind: FPKind, n: number): Case { + const fp = FP[kind]; + n = fp.quantize(n); + const result = fp.modfInterval(n).whole; + + return { input: fp.scalarBuilder(n), expected: result }; +} + +/** @returns a fract Case for a given vector input */ +function makeVectorCaseFract(kind: FPKind, v: readonly number[]): Case { + const fp = FP[kind]; + v = v.map(fp.quantize); + const fs = v.map(e => { + return fp.modfInterval(e).fract; + }); + + return { input: toVector(v, fp.scalarBuilder), expected: fs }; +} + +/** @returns a whole Case for a given vector input */ +function makeVectorCaseWhole(kind: FPKind, v: readonly number[]): Case { + const fp = FP[kind]; + v = v.map(fp.quantize); + const ws = v.map(e => { + return fp.modfInterval(e).whole; + }); + + return { input: toVector(v, fp.scalarBuilder), expected: ws }; +} + +// Cases: [f32|f16|abstract]_[fract|whole] +const scalar_cases = (['f32', 'f16', 'abstract'] as const) + .flatMap(kind => + (['whole', 'fract'] as const).map(portion => ({ + [`${kind}_${portion}`]: () => { + const makeCase = portion === 'whole' ? makeScalarCaseWhole : makeScalarCaseFract; + return FP[kind].scalarRange().map(makeCase.bind(null, kind)); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: [f32|f16|abstract]_vecN_[fract|whole] +const vec_cases = (['f32', 'f16', 'abstract'] as const) + .flatMap(kind => + ([2, 3, 4] as const).flatMap(n => + (['whole', 'fract'] as const).map(portion => ({ + [`${kind}_vec${n}_${portion}`]: () => { + const makeCase = portion === 'whole' ? makeVectorCaseWhole : makeVectorCaseFract; + return FP[kind].vectorRange(n).map(makeCase.bind(null, kind)); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('modf', { + ...scalar_cases, + ...vec_cases, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts index d6b82957b202..3e12d4f8696d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts @@ -18,25 +18,18 @@ Returns the result_struct for the given type. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - toVector, - TypeAbstractFloat, - TypeF16, - TypeF32, - TypeVec, -} from '../../../../../util/conversion.js'; -import { FP, FPKind } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeAbstractFloat, TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; import { abstractFloatShaderBuilder, allInputSources, basicExpressionBuilder, - Case, onlyConstInputSource, run, ShaderBuilder, } from '../../expression.js'; +import { d } from './modf.cache.js'; + export const g = makeTestGroup(GPUTest); /** @returns an ShaderBuilder that evaluates modf and returns .whole from the result structure */ @@ -59,77 +52,6 @@ function abstractFractBuilder(): ShaderBuilder { return abstractFloatShaderBuilder(value => `modf(${value}).fract`); } -/** @returns a fract Case for a scalar vector input */ -function makeScalarCaseFract(kind: FPKind, n: number): Case { - const fp = FP[kind]; - n = fp.quantize(n); - const result = fp.modfInterval(n).fract; - - return { input: fp.scalarBuilder(n), expected: result }; -} - -/** @returns a whole Case for a scalar vector input */ -function makeScalarCaseWhole(kind: FPKind, n: number): Case { - const fp = FP[kind]; - n = fp.quantize(n); - const result = fp.modfInterval(n).whole; - - return { input: fp.scalarBuilder(n), expected: result }; -} - -/** @returns a fract Case for a given vector input */ -function makeVectorCaseFract(kind: FPKind, v: readonly number[]): Case { - const fp = FP[kind]; - v = v.map(fp.quantize); - const fs = v.map(e => { - return fp.modfInterval(e).fract; - }); - - return { input: toVector(v, fp.scalarBuilder), expected: fs }; -} - -/** @returns a whole Case for a given vector input */ -function makeVectorCaseWhole(kind: FPKind, v: readonly number[]): Case { - const fp = FP[kind]; - v = v.map(fp.quantize); - const ws = v.map(e => { - return fp.modfInterval(e).whole; - }); - - return { input: toVector(v, fp.scalarBuilder), expected: ws }; -} - -// Cases: [f32|f16|abstract]_[fract|whole] -const scalar_cases = (['f32', 'f16', 'abstract'] as const) - .flatMap(kind => - (['whole', 'fract'] as const).map(portion => ({ - [`${kind}_${portion}`]: () => { - const makeCase = portion === 'whole' ? makeScalarCaseWhole : makeScalarCaseFract; - return FP[kind].scalarRange().map(makeCase.bind(null, kind)); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: [f32|f16|abstract]_vecN_[fract|whole] -const vec_cases = (['f32', 'f16', 'abstract'] as const) - .flatMap(kind => - ([2, 3, 4] as const).flatMap(n => - (['whole', 'fract'] as const).map(portion => ({ - [`${kind}_vec${n}_${portion}`]: () => { - const makeCase = portion === 'whole' ? makeVectorCaseWhole : makeVectorCaseFract; - return FP[kind].vectorRange(n).map(makeCase.bind(null, kind)); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('modf', { - ...scalar_cases, - ...vec_cases, -}); - g.test('f32_fract') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc( diff --git a/src/webgpu/shader/execution/expression/call/builtin/normalize.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/normalize.cache.ts new file mode 100644 index 000000000000..41c87b2fb93e --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/normalize.cache.ts @@ -0,0 +1,21 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_vecN_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateVectorToVectorCases( + FP[trait].vectorRange(dim), + nonConst ? 'unfiltered' : 'finite', + FP[trait].normalizeInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('normalize', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts index b9d1414f928d..4820f5884d69 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts @@ -8,34 +8,14 @@ Returns a unit vector in the same direction as e. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './normalize.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_vecN_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateVectorToVectorCases( - FP[trait].vectorRange(dim), - nonConst ? 'unfiltered' : 'finite', - FP[trait].normalizeInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('normalize', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.cache.ts new file mode 100644 index 000000000000..9cd7824cd978 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.cache.ts @@ -0,0 +1,55 @@ +import { anyOf, skipUndefined } from '../../../../../util/compare.js'; +import { f32, pack2x16float, u32, vec2 } from '../../../../../util/conversion.js'; +import { cartesianProduct, quantizeToF32, scalarF32Range } from '../../../../../util/math.js'; +import { Case } from '../../case.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// pack2x16float has somewhat unusual behaviour, specifically around how it is +// supposed to behave when values go OOB and when they are considered to have +// gone OOB, so has its own bespoke implementation. + +/** + * @returns a Case for `pack2x16float` + * @param param0 first param for the case + * @param param1 second param for the case + * @param filter_undefined should inputs that cause an undefined expectation be + * filtered out, needed for const-eval + */ +function makeCase(param0: number, param1: number, filter_undefined: boolean): Case | undefined { + param0 = quantizeToF32(param0); + param1 = quantizeToF32(param1); + + const results = pack2x16float(param0, param1); + if (filter_undefined && results.some(r => r === undefined)) { + return undefined; + } + + return { + input: [vec2(f32(param0), f32(param1))], + expected: anyOf( + ...results.map(r => (r === undefined ? skipUndefined(undefined) : skipUndefined(u32(r)))) + ), + }; +} + +/** + * @returns an array of Cases for `pack2x16float` + * @param param0s array of inputs to try for the first param + * @param param1s array of inputs to try for the second param + * @param filter_undefined should inputs that cause an undefined expectation be + * filtered out, needed for const-eval + */ +function generateCases(param0s: number[], param1s: number[], filter_undefined: boolean): Case[] { + return cartesianProduct(param0s, param1s) + .map(e => makeCase(e[0], e[1], filter_undefined)) + .filter((c): c is Case => c !== undefined); +} + +export const d = makeCaseCache('pack2x16float', { + f32_const: () => { + return generateCases(scalarF32Range(), scalarF32Range(), true); + }, + f32_non_const: () => { + return generateCases(scalarF32Range(), scalarF32Range(), false); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts index a0716038082f..efa4041259eb 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts @@ -6,74 +6,14 @@ which is then placed in bits 16 × i through 16 × i + 15 of the result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { anyOf, skipUndefined } from '../../../../../util/compare.js'; -import { - f32, - pack2x16float, - TypeF32, - TypeU32, - TypeVec, - u32, - vec2, -} from '../../../../../util/conversion.js'; -import { cartesianProduct, scalarF32Range, quantizeToF32 } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { allInputSources, Case, run } from '../../expression.js'; +import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; +import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './pack2x16float.cache.js'; export const g = makeTestGroup(GPUTest); -// pack2x16float has somewhat unusual behaviour, specifically around how it is -// supposed to behave when values go OOB and when they are considered to have -// gone OOB, so has its own bespoke implementation. - -/** - * @returns a Case for `pack2x16float` - * @param param0 first param for the case - * @param param1 second param for the case - * @param filter_undefined should inputs that cause an undefined expectation be - * filtered out, needed for const-eval - */ -function makeCase(param0: number, param1: number, filter_undefined: boolean): Case | undefined { - param0 = quantizeToF32(param0); - param1 = quantizeToF32(param1); - - const results = pack2x16float(param0, param1); - if (filter_undefined && results.some(r => r === undefined)) { - return undefined; - } - - return { - input: [vec2(f32(param0), f32(param1))], - expected: anyOf( - ...results.map(r => (r === undefined ? skipUndefined(undefined) : skipUndefined(u32(r)))) - ), - }; -} - -/** - * @returns an array of Cases for `pack2x16float` - * @param param0s array of inputs to try for the first param - * @param param1s array of inputs to try for the second param - * @param filter_undefined should inputs that cause an undefined expectation be - * filtered out, needed for const-eval - */ -function generateCases(param0s: number[], param1s: number[], filter_undefined: boolean): Case[] { - return cartesianProduct(param0s, param1s) - .map(e => makeCase(e[0], e[1], filter_undefined)) - .filter((c): c is Case => c !== undefined); -} - -export const d = makeCaseCache('pack2x16float', { - f32_const: () => { - return generateCases(scalarF32Range(), scalarF32Range(), true); - }, - f32_non_const: () => { - return generateCases(scalarF32Range(), scalarF32Range(), false); - }, -}); - g.test('pack') .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions') .desc( diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts index 54bb21f6c645..6479fe7ce070 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts @@ -18,7 +18,8 @@ import { vec2, } from '../../../../../util/conversion.js'; import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; -import { allInputSources, Case, run } from '../../expression.js'; +import { Case } from '../../case.js'; +import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts index a875a9c7e13a..303c555c1daf 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts @@ -18,7 +18,8 @@ import { vec2, } from '../../../../../util/conversion.js'; import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; -import { allInputSources, Case, run } from '../../expression.js'; +import { Case } from '../../case.js'; +import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts index de0463e9fc4c..b1c2607c940f 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts @@ -19,7 +19,8 @@ import { vec4, } from '../../../../../util/conversion.js'; import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; -import { allInputSources, Case, run } from '../../expression.js'; +import { Case } from '../../case.js'; +import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts index b670e92fbbbf..fa67e8f4ae3d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts @@ -19,7 +19,8 @@ import { vec4, } from '../../../../../util/conversion.js'; import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; -import { allInputSources, Case, run } from '../../expression.js'; +import { Case } from '../../case.js'; +import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; diff --git a/src/webgpu/shader/execution/expression/call/builtin/pow.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/pow.cache.ts new file mode 100644 index 000000000000..ff2803747599 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/pow.cache.ts @@ -0,0 +1,20 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarPairToIntervalCases( + FP[trait].scalarRange(), + FP[trait].scalarRange(), + nonConst ? 'unfiltered' : 'finite', + FP[trait].powInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('pow', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts index 6e682168de78..deea077e7a31 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts @@ -9,33 +9,14 @@ Returns e1 raised to the power e2. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './pow.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarPairToIntervalCases( - FP[trait].scalarRange(), - FP[trait].scalarRange(), - nonConst ? 'unfiltered' : 'finite', - FP[trait].powInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('pow', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.cache.ts new file mode 100644 index 000000000000..91aa845d29f7 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.cache.ts @@ -0,0 +1,41 @@ +import { kValue } from '../../../../../util/constants.js'; +import { FP } from '../../../../../util/floating_point.js'; +import { scalarF16Range, scalarF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +export const d = makeCaseCache('quantizeToF16', { + f32_const: () => { + return FP.f32.generateScalarToIntervalCases( + [ + kValue.f16.negative.min, + kValue.f16.negative.max, + kValue.f16.negative.subnormal.min, + kValue.f16.negative.subnormal.max, + kValue.f16.positive.subnormal.min, + kValue.f16.positive.subnormal.max, + kValue.f16.positive.min, + kValue.f16.positive.max, + ...scalarF16Range(), + ], + 'finite', + FP.f32.quantizeToF16Interval + ); + }, + f32_non_const: () => { + return FP.f32.generateScalarToIntervalCases( + [ + kValue.f16.negative.min, + kValue.f16.negative.max, + kValue.f16.negative.subnormal.min, + kValue.f16.negative.subnormal.max, + kValue.f16.positive.subnormal.min, + kValue.f16.positive.subnormal.max, + kValue.f16.positive.min, + kValue.f16.positive.max, + ...scalarF32Range(), + ], + 'unfiltered', + FP.f32.quantizeToF16Interval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts index d48dfddb09c5..ee34ddd17479 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts @@ -10,54 +10,14 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { kValue } from '../../../../../util/constants.js'; import { TypeF32 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { scalarF16Range, scalarF32Range } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './quantizeToF16.cache.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('quantizeToF16', { - f32_const: () => { - return FP.f32.generateScalarToIntervalCases( - [ - kValue.f16.negative.min, - kValue.f16.negative.max, - kValue.f16.negative.subnormal.min, - kValue.f16.negative.subnormal.max, - kValue.f16.positive.subnormal.min, - kValue.f16.positive.subnormal.max, - kValue.f16.positive.min, - kValue.f16.positive.max, - ...scalarF16Range(), - ], - 'finite', - FP.f32.quantizeToF16Interval - ); - }, - f32_non_const: () => { - return FP.f32.generateScalarToIntervalCases( - [ - kValue.f16.negative.min, - kValue.f16.negative.max, - kValue.f16.negative.subnormal.min, - kValue.f16.negative.subnormal.max, - kValue.f16.positive.subnormal.min, - kValue.f16.positive.subnormal.max, - kValue.f16.positive.min, - kValue.f16.positive.max, - ...scalarF32Range(), - ], - 'unfiltered', - FP.f32.quantizeToF16Interval - ); - }, -}); - g.test('f32') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`f32 tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/radians.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/radians.cache.ts new file mode 100644 index 000000000000..e92b4970c499 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/radians.cache.ts @@ -0,0 +1,17 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract] +const cases = (['f32', 'f16', 'abstract'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + 'unfiltered', + FP[trait].radiansInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('radians', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts index 0404ff3d6df4..f83f89b9154e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts @@ -11,29 +11,13 @@ Component-wise when T is a vector import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './radians.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract] -const cases = (['f32', 'f16', 'abstract'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - 'unfiltered', - FP[trait].radiansInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('radians', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/reflect.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/reflect.cache.ts new file mode 100644 index 000000000000..5cfb33e55915 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/reflect.cache.ts @@ -0,0 +1,22 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_vecN_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateVectorPairToVectorCases( + FP[trait].sparseVectorRange(dim), + FP[trait].sparseVectorRange(dim), + nonConst ? 'unfiltered' : 'finite', + FP[trait].reflectInterval + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('reflect', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts index 86e04271f665..0908bf04be5c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts @@ -9,35 +9,14 @@ direction e1-2*dot(e2,e1)*e2. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './reflect.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_vecN_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateVectorPairToVectorCases( - FP[trait].sparseVectorRange(dim), - FP[trait].sparseVectorRange(dim), - nonConst ? 'unfiltered' : 'finite', - FP[trait].reflectInterval - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('reflect', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/refract.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/refract.cache.ts new file mode 100644 index 000000000000..b426045e9b15 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/refract.cache.ts @@ -0,0 +1,89 @@ +import { ROArrayArray } from '../../../../../../common/util/types.js'; +import { toVector } from '../../../../../util/conversion.js'; +import { FP, FPKind } from '../../../../../util/floating_point.js'; +import { Case } from '../../case.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { IntervalFilter } from '../../interval_filter.js'; + +// Using a bespoke implementation of make*Case and generate*Cases here +// since refract is the only builtin with the API signature +// (vec, vec, scalar) -> vec + +/** + * @returns a Case for `refract` + * @param kind what type of floating point numbers to operate on + * @param i the `i` param for the case + * @param s the `s` param for the case + * @param r the `r` param for the case + * @param check what interval checking to apply + * */ +function makeCase( + kind: FPKind, + i: readonly number[], + s: readonly number[], + r: number, + check: IntervalFilter +): Case | undefined { + const fp = FP[kind]; + i = i.map(fp.quantize); + s = s.map(fp.quantize); + r = fp.quantize(r); + + const vectors = fp.refractInterval(i, s, r); + if (check === 'finite' && vectors.some(e => !e.isFinite())) { + return undefined; + } + + return { + input: [toVector(i, fp.scalarBuilder), toVector(s, fp.scalarBuilder), fp.scalarBuilder(r)], + expected: fp.refractInterval(i, s, r), + }; +} + +/** + * @returns an array of Cases for `refract` + * @param kind what type of floating point numbers to operate on + * @param param_is array of inputs to try for the `i` param + * @param param_ss array of inputs to try for the `s` param + * @param param_rs array of inputs to try for the `r` param + * @param check what interval checking to apply + */ +function generateCases( + kind: FPKind, + param_is: ROArrayArray, + param_ss: ROArrayArray, + param_rs: readonly number[], + check: IntervalFilter +): Case[] { + // Cannot use `cartesianProduct` here due to heterogeneous param types + return param_is + .flatMap(i => { + return param_ss.flatMap(s => { + return param_rs.map(r => { + return makeCase(kind, i, s, r, check); + }); + }); + }) + .filter((c): c is Case => c !== undefined); +} + +// Cases: [f32|f16]_vecN_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(dim => + ([true, false] as const).map(nonConst => ({ + [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { + return generateCases( + trait, + FP[trait].sparseVectorRange(dim), + FP[trait].sparseVectorRange(dim), + FP[trait].sparseScalarRange(), + nonConst ? 'unfiltered' : 'finite' + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('refract', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts index 30253f9ce09c..67eb8ac94658 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts @@ -11,100 +11,15 @@ vector e3*e1- (e3* dot(e2,e1) + sqrt(k)) *e2. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { ROArrayArray } from '../../../../../../common/util/types.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { toVector, TypeF32, TypeF16, TypeVec } from '../../../../../util/conversion.js'; -import { FP, FPKind } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { allInputSources, Case, IntervalFilter, run } from '../../expression.js'; +import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './refract.cache.js'; export const g = makeTestGroup(GPUTest); -// Using a bespoke implementation of make*Case and generate*Cases here -// since refract is the only builtin with the API signature -// (vec, vec, scalar) -> vec - -/** - * @returns a Case for `refract` - * @param kind what type of floating point numbers to operate on - * @param i the `i` param for the case - * @param s the `s` param for the case - * @param r the `r` param for the case - * @param check what interval checking to apply - * */ -function makeCase( - kind: FPKind, - i: readonly number[], - s: readonly number[], - r: number, - check: IntervalFilter -): Case | undefined { - const fp = FP[kind]; - i = i.map(fp.quantize); - s = s.map(fp.quantize); - r = fp.quantize(r); - - const vectors = fp.refractInterval(i, s, r); - if (check === 'finite' && vectors.some(e => !e.isFinite())) { - return undefined; - } - - return { - input: [toVector(i, fp.scalarBuilder), toVector(s, fp.scalarBuilder), fp.scalarBuilder(r)], - expected: fp.refractInterval(i, s, r), - }; -} - -/** - * @returns an array of Cases for `refract` - * @param kind what type of floating point numbers to operate on - * @param param_is array of inputs to try for the `i` param - * @param param_ss array of inputs to try for the `s` param - * @param param_rs array of inputs to try for the `r` param - * @param check what interval checking to apply - */ -function generateCases( - kind: FPKind, - param_is: ROArrayArray, - param_ss: ROArrayArray, - param_rs: readonly number[], - check: IntervalFilter -): Case[] { - // Cannot use `cartesianProduct` here due to heterogeneous param types - return param_is - .flatMap(i => { - return param_ss.flatMap(s => { - return param_rs.map(r => { - return makeCase(kind, i, s, r, check); - }); - }); - }) - .filter((c): c is Case => c !== undefined); -} - -// Cases: [f32|f16]_vecN_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(dim => - ([true, false] as const).map(nonConst => ({ - [`${trait}_vec${dim}_${nonConst ? 'non_const' : 'const'}`]: () => { - return generateCases( - trait, - FP[trait].sparseVectorRange(dim), - FP[trait].sparseVectorRange(dim), - FP[trait].sparseScalarRange(), - nonConst ? 'unfiltered' : 'finite' - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('refract', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/round.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/round.cache.ts new file mode 100644 index 000000000000..e5383b2075ed --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/round.cache.ts @@ -0,0 +1,24 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// See https://github.com/gpuweb/cts/issues/2766 for details +const kIssue2766Value = { + abstract: 0x8000_0000_0000_0000, + f32: 0x8000_0000, + f16: 0x8000, +}; + +// Cases: [f32|f16|abstract] +const cases = (['f32', 'f16', 'abstract'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [kIssue2766Value[trait], ...FP[trait].scalarRange()], + 'unfiltered', + FP[trait].roundInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('round', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts index 307490b835df..de2d6acfd0e1 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts @@ -12,37 +12,14 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16, TypeAbstractFloat } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './round.cache.js'; export const g = makeTestGroup(GPUTest); -// See https://github.com/gpuweb/cts/issues/2766 for details -const kIssue2766Value = { - abstract: 0x8000_0000_0000_0000, - f32: 0x8000_0000, - f16: 0x8000, -}; - -// Cases: [f32|f16|abstract] -const cases = (['f32', 'f16', 'abstract'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [kIssue2766Value[trait], ...FP[trait].scalarRange()], - 'unfiltered', - FP[trait].roundInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('round', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/saturate.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/saturate.cache.ts new file mode 100644 index 000000000000..4a4ffeee30f7 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/saturate.cache.ts @@ -0,0 +1,18 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract] +const cases = (['f32', 'f16', 'abstract'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [...linearRange(0.0, 1.0, 20), ...FP[trait].scalarRange()], + 'unfiltered', + FP[trait].saturateInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('saturate', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts index 429d5a322dae..bf19a3c6cb23 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts @@ -10,30 +10,13 @@ Returns clamp(e, 0.0, 1.0). Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './saturate.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract] -const cases = (['f32', 'f16', 'abstract'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [...linearRange(0.0, 1.0, 20), ...FP[trait].scalarRange()], - 'unfiltered', - FP[trait].saturateInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('saturate', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts index c64f989f4218..e69ab1886e03 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts @@ -33,7 +33,8 @@ import { abstractFloat, TypeAbstractFloat, } from '../../../../../util/conversion.js'; -import { run, CaseList, allInputSources } from '../../expression.js'; +import { CaseList } from '../../case.js'; +import { run, allInputSources } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; diff --git a/src/webgpu/shader/execution/expression/call/builtin/sign.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/sign.cache.ts new file mode 100644 index 000000000000..8bac506ddc3a --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/sign.cache.ts @@ -0,0 +1,26 @@ +import { i32 } from '../../../../../util/conversion.js'; +import { FP } from '../../../../../util/floating_point.js'; +import { fullI32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract] +const fp_cases = (['f32', 'f16', 'abstract'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + 'unfiltered', + FP[trait].signInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('sign', { + ...fp_cases, + i32: () => + fullI32Range().map(i => { + const signFunc = (i: number): number => (i < 0 ? -1 : i > 0 ? 1 : 0); + return { input: [i32(i)], expected: i32(signFunc(i)) }; + }), +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts index 91d62148bf49..a17bb7b205ac 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts @@ -9,44 +9,14 @@ Returns the sign of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - i32, - TypeF32, - TypeF16, - TypeI32, - TypeAbstractFloat, -} from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { fullI32Range } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeAbstractFloat, TypeF16, TypeF32, TypeI32 } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './sign.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract] -const fp_cases = (['f32', 'f16', 'abstract'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - 'unfiltered', - FP[trait].signInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('sign', { - ...fp_cases, - i32: () => - fullI32Range().map(i => { - const signFunc = (i: number): number => (i < 0 ? -1 : i > 0 ? 1 : 0); - return { input: [i32(i)], expected: i32(signFunc(i)) }; - }), -}); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#sign-builtin') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/sin.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/sin.cache.ts new file mode 100644 index 000000000000..6c107d62dd11 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/sin.cache.ts @@ -0,0 +1,22 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16] +const cases = (['f32', 'f16'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [ + // Well-defined accuracy range + ...linearRange(-Math.PI, Math.PI, 100), + ...FP[trait].scalarRange(), + ], + 'unfiltered', + FP[trait].sinInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('sin', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts index 6507d3aeddc1..e9420fc0880d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts @@ -9,35 +9,14 @@ Returns the sine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './sin.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [ - // Well-defined accuracy range - ...linearRange(-Math.PI, Math.PI, 100), - ...FP[trait].scalarRange(), - ], - 'unfiltered', - FP[trait].sinInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('sin', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/sinh.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/sinh.cache.ts new file mode 100644 index 000000000000..4a4d776292f3 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/sinh.cache.ts @@ -0,0 +1,19 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + nonConst ? 'unfiltered' : 'finite', + FP[trait].sinhInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('sinh', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts index 18268211d366..0070befa47fc 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts @@ -9,32 +9,14 @@ Returns the hyperbolic sine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './sinh.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - nonConst ? 'unfiltered' : 'finite', - FP[trait].sinhInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('sinh', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/smoothstep.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.cache.ts new file mode 100644 index 000000000000..1c918aacc056 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.cache.ts @@ -0,0 +1,21 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarTripleToIntervalCases( + FP[trait].sparseScalarRange(), + FP[trait].sparseScalarRange(), + FP[trait].sparseScalarRange(), + nonConst ? 'unfiltered' : 'finite', + FP[trait].smoothStepInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('smoothstep', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts index fb6e166dacc4..a32c112b8a94 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts @@ -11,34 +11,14 @@ For scalar T, the result is t * t * (3.0 - 2.0 * t), where t = clamp((x - low) / import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './smoothstep.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarTripleToIntervalCases( - FP[trait].sparseScalarRange(), - FP[trait].sparseScalarRange(), - FP[trait].sparseScalarRange(), - nonConst ? 'unfiltered' : 'finite', - FP[trait].smoothStepInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('smoothstep', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/sqrt.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/sqrt.cache.ts new file mode 100644 index 000000000000..f350eaff3576 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/sqrt.cache.ts @@ -0,0 +1,19 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16]_[non_]const +const cases = (['f32', 'f16'] as const) + .flatMap(trait => + ([true, false] as const).map(nonConst => ({ + [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + nonConst ? 'unfiltered' : 'finite', + FP[trait].sqrtInterval + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('sqrt', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts index 4954ddf9ab51..cfd379fea660 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts @@ -9,32 +9,14 @@ Returns the square root of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './sqrt.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16]_[non_]const -const cases = (['f32', 'f16'] as const) - .flatMap(trait => - ([true, false] as const).map(nonConst => ({ - [`${trait}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - nonConst ? 'unfiltered' : 'finite', - FP[trait].sqrtInterval - ); - }, - })) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('sqrt', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/step.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/step.cache.ts new file mode 100644 index 000000000000..c741f8fd2603 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/step.cache.ts @@ -0,0 +1,41 @@ +import { anyOf } from '../../../../../util/compare.js'; +import { FP } from '../../../../../util/floating_point.js'; +import { Case } from '../../case.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// stepInterval's return value can't always be interpreted as a single acceptance +// interval, valid result may be 0.0 or 1.0 or both of them, but will never be a +// value in interval (0.0, 1.0). +// See the comment block on stepInterval for more details +const makeCase = (trait: 'f32' | 'f16', edge: number, x: number): Case => { + const FPTrait = FP[trait]; + edge = FPTrait.quantize(edge); + x = FPTrait.quantize(x); + const expected = FPTrait.stepInterval(edge, x); + + // [0, 0], [1, 1], or [-∞, +∞] cases + if (expected.isPoint() || !expected.isFinite()) { + return { input: [FPTrait.scalarBuilder(edge), FPTrait.scalarBuilder(x)], expected }; + } + + // [0, 1] case, valid result is either 0.0 or 1.0. + const zeroInterval = FPTrait.toInterval(0); + const oneInterval = FPTrait.toInterval(1); + return { + input: [FPTrait.scalarBuilder(edge), FPTrait.scalarBuilder(x)], + expected: anyOf(zeroInterval, oneInterval), + }; +}; + +// Cases: [f32|f16] +const cases = (['f32', 'f16'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait] + .scalarRange() + .flatMap(edge => FP[trait].scalarRange().map(x => makeCase(trait, edge, x))); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('step', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts index 2e8f7ce8a070..c55a48812e9c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts @@ -9,53 +9,14 @@ Returns 1.0 if edge ≤ x, and 0.0 otherwise. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { anyOf } from '../../../../../util/compare.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; -import { allInputSources, Case, run } from '../../expression.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './step.cache.js'; export const g = makeTestGroup(GPUTest); -// stepInterval's return value can't always be interpreted as a single acceptance -// interval, valid result may be 0.0 or 1.0 or both of them, but will never be a -// value in interval (0.0, 1.0). -// See the comment block on stepInterval for more details -const makeCase = (trait: 'f32' | 'f16', edge: number, x: number): Case => { - const FPTrait = FP[trait]; - edge = FPTrait.quantize(edge); - x = FPTrait.quantize(x); - const expected = FPTrait.stepInterval(edge, x); - - // [0, 0], [1, 1], or [-∞, +∞] cases - if (expected.isPoint() || !expected.isFinite()) { - return { input: [FPTrait.scalarBuilder(edge), FPTrait.scalarBuilder(x)], expected }; - } - - // [0, 1] case, valid result is either 0.0 or 1.0. - const zeroInterval = FPTrait.toInterval(0); - const oneInterval = FPTrait.toInterval(1); - return { - input: [FPTrait.scalarBuilder(edge), FPTrait.scalarBuilder(x)], - expected: anyOf(zeroInterval, oneInterval), - }; -}; - -// Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait] - .scalarRange() - .flatMap(edge => FP[trait].scalarRange().map(x => makeCase(trait, edge, x))); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('step', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/tan.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/tan.cache.ts new file mode 100644 index 000000000000..cf850a5d78f0 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/tan.cache.ts @@ -0,0 +1,22 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16] +const cases = (['f32', 'f16'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + [ + // Well-defined accuracy range + ...linearRange(-Math.PI, Math.PI, 100), + ...FP[trait].scalarRange(), + ], + 'unfiltered', + FP[trait].tanInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('tan', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts index b54f0b11052b..f86b3c959a16 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts @@ -9,35 +9,14 @@ Returns the tangent of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { linearRange } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './tan.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - [ - // Well-defined accuracy range - ...linearRange(-Math.PI, Math.PI, 100), - ...FP[trait].scalarRange(), - ], - 'unfiltered', - FP[trait].tanInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('tan', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/tanh.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/tanh.cache.ts new file mode 100644 index 000000000000..46724e6b7033 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/tanh.cache.ts @@ -0,0 +1,17 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16] +const cases = (['f32', 'f16'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + 'unfiltered', + FP[trait].tanhInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('tanh', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts index 426181e79b05..496c7bcf1b11 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts @@ -9,30 +9,14 @@ Returns the hyperbolic tangent of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; +import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './tanh.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16] -const cases = (['f32', 'f16'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - 'unfiltered', - FP[trait].tanhInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('tanh', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/transpose.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/transpose.cache.ts new file mode 100644 index 000000000000..cb89e2d1944d --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/transpose.cache.ts @@ -0,0 +1,27 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract]_matCxR_[non_]const +// abstract_matCxR_non_const is empty and not used +const cases = (['f32', 'f16', 'abstract'] as const) + .flatMap(trait => + ([2, 3, 4] as const).flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`${trait}_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + if (trait === 'abstract' && nonConst) { + return []; + } + return FP[trait].generateMatrixToMatrixCases( + FP[trait].sparseMatrixRange(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP[trait].transposeInterval + ); + }, + })) + ) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('transpose', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts index 502983517a76..82250cb985cf 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts @@ -9,39 +9,13 @@ Returns the transpose of e. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeAbstractFloat, TypeF16, TypeF32, TypeMat } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './transpose.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract]_matCxR_[non_]const -// abstract_matCxR_non_const is empty and not used -const cases = (['f32', 'f16', 'abstract'] as const) - .flatMap(trait => - ([2, 3, 4] as const).flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`${trait}_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - if (trait === 'abstract' && nonConst) { - return []; - } - return FP[trait].generateMatrixToMatrixCases( - FP[trait].sparseMatrixRange(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP[trait].transposeInterval - ); - }, - })) - ) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('transpose', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/trunc.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/trunc.cache.ts new file mode 100644 index 000000000000..061c95b07fed --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/trunc.cache.ts @@ -0,0 +1,17 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { makeCaseCache } from '../../case_cache.js'; + +// Cases: [f32|f16|abstract] +const cases = (['f32', 'f16', 'abstract'] as const) + .map(trait => ({ + [`${trait}`]: () => { + return FP[trait].generateScalarToIntervalCases( + FP[trait].scalarRange(), + 'unfiltered', + FP[trait].truncInterval + ); + }, + })) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('trunc', cases); diff --git a/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts index ce885450b85f..7dcc9395c3e3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts @@ -11,29 +11,13 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractBuiltin, builtin } from './builtin.js'; +import { d } from './trunc.cache.js'; export const g = makeTestGroup(GPUTest); -// Cases: [f32|f16|abstract] -const cases = (['f32', 'f16', 'abstract'] as const) - .map(trait => ({ - [`${trait}`]: () => { - return FP[trait].generateScalarToIntervalCases( - FP[trait].scalarRange(), - 'unfiltered', - FP[trait].truncInterval - ); - }, - })) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('trunc', cases); - g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.cache.ts new file mode 100644 index 000000000000..79a7a568d22f --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.cache.ts @@ -0,0 +1,20 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +export const d = makeCaseCache('unpack2x16float', { + u32_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'finite', + FP.f32.unpack2x16floatInterval + ); + }, + u32_non_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'unfiltered', + FP.f32.unpack2x16floatInterval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts index 4a0bf075e91f..da1819f8ef6e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts @@ -8,32 +8,13 @@ interpretation of bits 16×i through 16×i+15 of e as an IEEE-754 binary16 value import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { fullU32Range } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './unpack2x16float.cache.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unpack2x16float', { - u32_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'finite', - FP.f32.unpack2x16floatInterval - ); - }, - u32_non_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'unfiltered', - FP.f32.unpack2x16floatInterval - ); - }, -}); - g.test('unpack') .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') .desc( diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.cache.ts new file mode 100644 index 000000000000..89dfb475d390 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.cache.ts @@ -0,0 +1,20 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +export const d = makeCaseCache('unpack2x16snorm', { + u32_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'finite', + FP.f32.unpack2x16snormInterval + ); + }, + u32_non_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'unfiltered', + FP.f32.unpack2x16snormInterval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts index 195cfd9a0111..a88702bcce70 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts @@ -8,32 +8,13 @@ of bits 16×i through 16×i+15 of e as a twos-complement signed integer. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { fullU32Range } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './unpack2x16snorm.cache.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unpack2x16snorm', { - u32_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'finite', - FP.f32.unpack2x16snormInterval - ); - }, - u32_non_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'unfiltered', - FP.f32.unpack2x16snormInterval - ); - }, -}); - g.test('unpack') .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') .desc( diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.cache.ts new file mode 100644 index 000000000000..7f0fe84af600 --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.cache.ts @@ -0,0 +1,20 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +export const d = makeCaseCache('unpack2x16unorm', { + u32_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'finite', + FP.f32.unpack2x16unormInterval + ); + }, + u32_non_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'unfiltered', + FP.f32.unpack2x16unormInterval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts index 16b4e6397c6d..325e0a9735b5 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts @@ -8,32 +8,13 @@ Component i of the result is v ÷ 65535, where v is the interpretation of bits import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { fullU32Range } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './unpack2x16unorm.cache.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unpack2x16unorm', { - u32_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'finite', - FP.f32.unpack2x16unormInterval - ); - }, - u32_non_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'unfiltered', - FP.f32.unpack2x16unormInterval - ); - }, -}); - g.test('unpack') .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') .desc( diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.cache.ts new file mode 100644 index 000000000000..3a4790f1882c --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.cache.ts @@ -0,0 +1,20 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +export const d = makeCaseCache('unpack4x8snorm', { + u32_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'finite', + FP.f32.unpack4x8snormInterval + ); + }, + u32_non_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'unfiltered', + FP.f32.unpack4x8snormInterval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts index 7ea8d5191895..3b48cca4539f 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts @@ -8,32 +8,13 @@ bits 8×i through 8×i+7 of e as a twos-complement signed integer. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { fullU32Range } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './unpack4x8snorm.cache.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unpack4x8snorm', { - u32_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'finite', - FP.f32.unpack4x8snormInterval - ); - }, - u32_non_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'unfiltered', - FP.f32.unpack4x8snormInterval - ); - }, -}); - g.test('unpack') .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') .desc( diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.cache.ts new file mode 100644 index 000000000000..21390f74b19f --- /dev/null +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.cache.ts @@ -0,0 +1,20 @@ +import { FP } from '../../../../../util/floating_point.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; + +export const d = makeCaseCache('unpack4x8unorm', { + u32_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'finite', + FP.f32.unpack4x8unormInterval + ); + }, + u32_non_const: () => { + return FP.f32.generateU32ToIntervalCases( + fullU32Range(), + 'unfiltered', + FP.f32.unpack4x8unormInterval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts index bf54d23c12e1..3e57f2592ee5 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts @@ -8,32 +8,13 @@ through 8×i+7 of e as an unsigned integer. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; -import { FP } from '../../../../../util/floating_point.js'; -import { fullU32Range } from '../../../../../util/math.js'; -import { makeCaseCache } from '../../case_cache.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; +import { d } from './unpack4x8unorm.cache.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unpack4x8unorm', { - u32_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'finite', - FP.f32.unpack4x8unormInterval - ); - }, - u32_non_const: () => { - return FP.f32.generateU32ToIntervalCases( - fullU32Range(), - 'unfiltered', - FP.f32.unpack4x8unormInterval - ); - }, -}); - g.test('unpack') .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') .desc( diff --git a/src/webgpu/shader/execution/expression/case.ts b/src/webgpu/shader/execution/expression/case.ts new file mode 100644 index 000000000000..65fd85d77bbc --- /dev/null +++ b/src/webgpu/shader/execution/expression/case.ts @@ -0,0 +1,247 @@ +import { ROArrayArray } from '../../../../common/util/types.js'; +import { ScalarBuilder, Value, Vector, i32, u32 } from '../../../util/conversion.js'; +import { + QuantizeFunc, + cartesianProduct, + quantizeToI32, + quantizeToU32, +} from '../../../util/math.js'; + +import { Expectation } from './expectation.js'; + +/** Case is a single expression test case. */ +export type Case = { + // The input value(s) + input: Value | ReadonlyArray; + // The expected result, or function to check the result + expected: Expectation; +}; + +/** CaseList is a list of Cases */ +export type CaseList = Array; + +/** + * A function that performs a binary operation on x and y, and returns the expected + * result. + */ +export interface BinaryOp { + (x: number, y: number): number | undefined; +} + +/** + * @returns a Case for the input params with op applied + * @param scalar scalar param + * @param vector vector param (2, 3, or 4 elements) + * @param op the op to apply to scalar and vector + * @param quantize function to quantize all values in vectors and scalars + * @param scalarize function to convert numbers to Scalars + */ +function makeScalarVectorBinaryToVectorCase( + scalar: number, + vector: readonly number[], + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder +): Case | undefined { + scalar = quantize(scalar); + vector = vector.map(quantize); + const result = vector.map(v => op(scalar, v)); + if (result.includes(undefined)) { + return undefined; + } + return { + input: [scalarize(scalar), new Vector(vector.map(scalarize))], + expected: new Vector((result as readonly number[]).map(scalarize)), + }; +} + +/** + * @returns array of Case for the input params with op applied + * @param scalars array of scalar params + * @param vectors array of vector params (2, 3, or 4 elements) + * @param op the op to apply to each pair of scalar and vector + * @param quantize function to quantize all values in vectors and scalars + * @param scalarize function to convert numbers to Scalars + */ +function generateScalarVectorBinaryToVectorCases( + scalars: readonly number[], + vectors: ROArrayArray, + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder +): Case[] { + const cases = new Array(); + scalars.forEach(s => { + vectors.forEach(v => { + const c = makeScalarVectorBinaryToVectorCase(s, v, op, quantize, scalarize); + if (c !== undefined) { + cases.push(c); + } + }); + }); + return cases; +} + +/** + * @returns a Case for the input params with op applied + * @param vector vector param (2, 3, or 4 elements) + * @param scalar scalar param + * @param op the op to apply to vector and scalar + * @param quantize function to quantize all values in vectors and scalars + * @param scalarize function to convert numbers to Scalars + */ +function makeVectorScalarBinaryToVectorCase( + vector: readonly number[], + scalar: number, + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder +): Case | undefined { + vector = vector.map(quantize); + scalar = quantize(scalar); + const result = vector.map(v => op(v, scalar)); + if (result.includes(undefined)) { + return undefined; + } + return { + input: [new Vector(vector.map(scalarize)), scalarize(scalar)], + expected: new Vector((result as readonly number[]).map(scalarize)), + }; +} + +/** + * @returns array of Case for the input params with op applied + * @param vectors array of vector params (2, 3, or 4 elements) + * @param scalars array of scalar params + * @param op the op to apply to each pair of vector and scalar + * @param quantize function to quantize all values in vectors and scalars + * @param scalarize function to convert numbers to Scalars + */ +function generateVectorScalarBinaryToVectorCases( + vectors: ROArrayArray, + scalars: readonly number[], + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder +): Case[] { + const cases = new Array(); + scalars.forEach(s => { + vectors.forEach(v => { + const c = makeVectorScalarBinaryToVectorCase(v, s, op, quantize, scalarize); + if (c !== undefined) { + cases.push(c); + } + }); + }); + return cases; +} + +/** + * @returns array of Case for the input params with op applied + * @param scalars array of scalar params + * @param vectors array of vector params (2, 3, or 4 elements) + * @param op he op to apply to each pair of scalar and vector + */ +export function generateU32VectorBinaryToVectorCases( + scalars: readonly number[], + vectors: ROArrayArray, + op: BinaryOp +): Case[] { + return generateScalarVectorBinaryToVectorCases(scalars, vectors, op, quantizeToU32, u32); +} + +/** + * @returns array of Case for the input params with op applied + * @param vectors array of vector params (2, 3, or 4 elements) + * @param scalars array of scalar params + * @param op he op to apply to each pair of vector and scalar + */ +export function generateVectorU32BinaryToVectorCases( + vectors: ROArrayArray, + scalars: readonly number[], + op: BinaryOp +): Case[] { + return generateVectorScalarBinaryToVectorCases(vectors, scalars, op, quantizeToU32, u32); +} + +/** + * @returns array of Case for the input params with op applied + * @param scalars array of scalar params + * @param vectors array of vector params (2, 3, or 4 elements) + * @param op he op to apply to each pair of scalar and vector + */ +export function generateI32VectorBinaryToVectorCases( + scalars: readonly number[], + vectors: ROArrayArray, + op: BinaryOp +): Case[] { + return generateScalarVectorBinaryToVectorCases(scalars, vectors, op, quantizeToI32, i32); +} + +/** + * @returns array of Case for the input params with op applied + * @param vectors array of vector params (2, 3, or 4 elements) + * @param scalars array of scalar params + * @param op he op to apply to each pair of vector and scalar + */ +export function generateVectorI32BinaryToVectorCases( + vectors: ROArrayArray, + scalars: readonly number[], + op: BinaryOp +): Case[] { + return generateVectorScalarBinaryToVectorCases(vectors, scalars, op, quantizeToI32, i32); +} + +/** + * @returns array of Case for the input params with op applied + * @param param0s array of inputs to try for the first param + * @param param1s array of inputs to try for the second param + * @param op callback called on each pair of inputs to produce each case + * @param quantize function to quantize all values + * @param scalarize function to convert numbers to Scalars + */ +function generateScalarBinaryToScalarCases( + param0s: readonly number[], + param1s: readonly number[], + op: BinaryOp, + quantize: QuantizeFunc, + scalarize: ScalarBuilder +): Case[] { + param0s = param0s.map(quantize); + param1s = param1s.map(quantize); + return cartesianProduct(param0s, param1s).reduce((cases, e) => { + const expected = op(e[0], e[1]); + if (expected !== undefined) { + cases.push({ input: [scalarize(e[0]), scalarize(e[1])], expected: scalarize(expected) }); + } + return cases; + }, new Array()); +} + +/** + * @returns an array of Cases for operations over a range of inputs + * @param param0s array of inputs to try for the first param + * @param param1s array of inputs to try for the second param + * @param op callback called on each pair of inputs to produce each case + */ +export function generateBinaryToI32Cases( + param0s: readonly number[], + param1s: readonly number[], + op: BinaryOp +) { + return generateScalarBinaryToScalarCases(param0s, param1s, op, quantizeToI32, i32); +} + +/** + * @returns an array of Cases for operations over a range of inputs + * @param param0s array of inputs to try for the first param + * @param param1s array of inputs to try for the second param + * @param op callback called on each pair of inputs to produce each case + */ +export function generateBinaryToU32Cases( + param0s: readonly number[], + param1s: readonly number[], + op: BinaryOp +) { + return generateScalarBinaryToScalarCases(param0s, param1s, op, quantizeToU32, u32); +} diff --git a/src/webgpu/shader/execution/expression/case_cache.ts b/src/webgpu/shader/execution/expression/case_cache.ts index f9bfd3c00b21..1580cf3298ca 100644 --- a/src/webgpu/shader/execution/expression/case_cache.ts +++ b/src/webgpu/shader/execution/expression/case_cache.ts @@ -3,21 +3,22 @@ import { unreachable } from '../../../../common/util/util.js'; import BinaryStream from '../../../util/binary_stream.js'; import { deserializeComparator, serializeComparator } from '../../../util/compare.js'; import { + Matrix, Scalar, + Value, Vector, - serializeValue, deserializeValue, - Matrix, - Value, + serializeValue, } from '../../../util/conversion.js'; import { - deserializeFPInterval, FPInterval, + deserializeFPInterval, serializeFPInterval, } from '../../../util/floating_point.js'; import { flatten2DArray, unflatten2DArray } from '../../../util/math.js'; -import { Case, CaseList, Expectation, isComparator } from './expression.js'; +import { Case, CaseList } from './case.js'; +import { Expectation, isComparator } from './expectation.js'; enum SerializedExpectationKind { Value, @@ -164,7 +165,7 @@ export class CaseCache implements Cacheable> { * serialize() implements the Cacheable.serialize interface. * @returns the serialized data. */ - async serialize(data: Record): Promise { + serialize(data: Record): Uint8Array { const maxSize = 32 << 20; // 32MB - max size for a file const stream = new BinaryStream(new ArrayBuffer(maxSize)); stream.writeU32(Object.keys(data).length); @@ -172,17 +173,15 @@ export class CaseCache implements Cacheable> { stream.writeString(name); stream.writeArray(data[name], serializeCase); } - const compressed = this.compress('gzip', stream.buffer()); - return compressed; + return stream.buffer(); } /** * deserialize() implements the Cacheable.deserialize interface. * @returns the deserialize data. */ - async deserialize(array: Uint8Array): Promise> { - const decompressed = await this.decompress('gzip', array); - const s = new BinaryStream(decompressed); + deserialize(array: Uint8Array): Record { + const s = new BinaryStream(array.buffer); const casesByName: Record = {}; const numRecords = s.readU32(); for (let i = 0; i < numRecords; i++) { @@ -193,26 +192,6 @@ export class CaseCache implements Cacheable> { return casesByName; } - /** - * Compresses a Uint8Array using using the given CompressionFormat - */ - private async compress(format: CompressionFormat, data: Uint8Array): Promise { - const stream = new Blob([data]).stream(); - const compressedStream = stream.pipeThrough(new CompressionStream(format)); - const blob = await new Response(compressedStream).blob(); - return new Uint8Array(await blob.arrayBuffer()); - } - - /** - * Decompresses a Uint8Array using using gzip - */ - private async decompress(format: CompressionFormat, data: Uint8Array): Promise { - const stream = new Blob([data]).stream(); - const decompressedStream = stream.pipeThrough(new DecompressionStream(format)); - const blob = await new Response(decompressedStream).blob(); - return await blob.arrayBuffer(); - } - public readonly path: string; private readonly builders: Record; } diff --git a/src/webgpu/shader/execution/expression/expectation.ts b/src/webgpu/shader/execution/expression/expectation.ts new file mode 100644 index 000000000000..8078673f2569 --- /dev/null +++ b/src/webgpu/shader/execution/expression/expectation.ts @@ -0,0 +1,31 @@ +import { ROArrayArray } from '../../../../common/util/types.js'; +import { Comparator, compare } from '../../../util/compare.js'; +import { Matrix, Scalar, Value, Vector } from '../../../util/conversion.js'; +import { FPInterval } from '../../../util/floating_point.js'; + +export type Expectation = + | Value + | FPInterval + | readonly FPInterval[] + | ROArrayArray + | Comparator; + +/** @returns if this Expectation actually a Comparator */ +export function isComparator(e: Expectation): e is Comparator { + return !( + e instanceof FPInterval || + e instanceof Scalar || + e instanceof Vector || + e instanceof Matrix || + e instanceof Array + ); +} + +/** @returns the input if it is already a Comparator, otherwise wraps it in a 'value' comparator */ +export function toComparator(input: Expectation): Comparator { + if (isComparator(input)) { + return input; + } + + return { compare: got => compare(got, input as Value), kind: 'value' }; +} diff --git a/src/webgpu/shader/execution/expression/expression.ts b/src/webgpu/shader/execution/expression/expression.ts index f85516f29bdd..d59456c5596a 100644 --- a/src/webgpu/shader/execution/expression/expression.ts +++ b/src/webgpu/shader/execution/expression/expression.ts @@ -1,70 +1,23 @@ import { globalTestConfig } from '../../../../common/framework/test_config.js'; -import { ROArrayArray } from '../../../../common/util/types.js'; import { assert, objectEquals, unreachable } from '../../../../common/util/util.js'; import { GPUTest } from '../../../gpu_test.js'; -import { compare, Comparator, ComparatorImpl } from '../../../util/compare.js'; +import { Comparator, ComparatorImpl } from '../../../util/compare.js'; import { kValue } from '../../../util/constants.js'; import { - ScalarType, + MatrixType, Scalar, + ScalarType, Type, - TypeVec, TypeU32, + TypeVec, Value, Vector, VectorType, - u32, - i32, - Matrix, - MatrixType, - ScalarBuilder, scalarTypeOf, } from '../../../util/conversion.js'; -import { FPInterval } from '../../../util/floating_point.js'; -import { - cartesianProduct, - QuantizeFunc, - quantizeToI32, - quantizeToU32, -} from '../../../util/math.js'; - -export type Expectation = - | Value - | FPInterval - | readonly FPInterval[] - | ROArrayArray - | Comparator; - -/** @returns if this Expectation actually a Comparator */ -export function isComparator(e: Expectation): e is Comparator { - return !( - e instanceof FPInterval || - e instanceof Scalar || - e instanceof Vector || - e instanceof Matrix || - e instanceof Array - ); -} -/** @returns the input if it is already a Comparator, otherwise wraps it in a 'value' comparator */ -export function toComparator(input: Expectation): Comparator { - if (isComparator(input)) { - return input; - } - - return { compare: got => compare(got, input as Value), kind: 'value' }; -} - -/** Case is a single expression test case. */ -export type Case = { - // The input value(s) - input: Value | ReadonlyArray; - // The expected result, or function to check the result - expected: Expectation; -}; - -/** CaseList is a list of Cases */ -export type CaseList = Array; +import { Case, CaseList } from './case.js'; +import { toComparator } from './expectation.js'; /** The input value source */ export type InputSource = @@ -1199,238 +1152,3 @@ function packScalarsToVector( resultType: packedResultType, }; } - -/** - * Indicates bounds that acceptance intervals need to be within to avoid inputs - * being filtered out. This is used for const-eval tests, since going OOB will - * cause a validation error not an execution error. - */ -export type IntervalFilter = - | 'finite' // Expected to be finite in the interval numeric space - | 'unfiltered'; // No expectations - -/** - * A function that performs a binary operation on x and y, and returns the expected - * result. - */ -export interface BinaryOp { - (x: number, y: number): number | undefined; -} - -/** - * @returns array of Case for the input params with op applied - * @param param0s array of inputs to try for the first param - * @param param1s array of inputs to try for the second param - * @param op callback called on each pair of inputs to produce each case - * @param quantize function to quantize all values - * @param scalarize function to convert numbers to Scalars - */ -function generateScalarBinaryToScalarCases( - param0s: readonly number[], - param1s: readonly number[], - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder -): Case[] { - param0s = param0s.map(quantize); - param1s = param1s.map(quantize); - return cartesianProduct(param0s, param1s).reduce((cases, e) => { - const expected = op(e[0], e[1]); - if (expected !== undefined) { - cases.push({ input: [scalarize(e[0]), scalarize(e[1])], expected: scalarize(expected) }); - } - return cases; - }, new Array()); -} - -/** - * @returns an array of Cases for operations over a range of inputs - * @param param0s array of inputs to try for the first param - * @param param1s array of inputs to try for the second param - * @param op callback called on each pair of inputs to produce each case - */ -export function generateBinaryToI32Cases( - param0s: readonly number[], - param1s: readonly number[], - op: BinaryOp -) { - return generateScalarBinaryToScalarCases(param0s, param1s, op, quantizeToI32, i32); -} - -/** - * @returns an array of Cases for operations over a range of inputs - * @param param0s array of inputs to try for the first param - * @param param1s array of inputs to try for the second param - * @param op callback called on each pair of inputs to produce each case - */ -export function generateBinaryToU32Cases( - param0s: readonly number[], - param1s: readonly number[], - op: BinaryOp -) { - return generateScalarBinaryToScalarCases(param0s, param1s, op, quantizeToU32, u32); -} - -/** - * @returns a Case for the input params with op applied - * @param scalar scalar param - * @param vector vector param (2, 3, or 4 elements) - * @param op the op to apply to scalar and vector - * @param quantize function to quantize all values in vectors and scalars - * @param scalarize function to convert numbers to Scalars - */ -function makeScalarVectorBinaryToVectorCase( - scalar: number, - vector: readonly number[], - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder -): Case | undefined { - scalar = quantize(scalar); - vector = vector.map(quantize); - const result = vector.map(v => op(scalar, v)); - if (result.includes(undefined)) { - return undefined; - } - return { - input: [scalarize(scalar), new Vector(vector.map(scalarize))], - expected: new Vector((result as readonly number[]).map(scalarize)), - }; -} - -/** - * @returns array of Case for the input params with op applied - * @param scalars array of scalar params - * @param vectors array of vector params (2, 3, or 4 elements) - * @param op the op to apply to each pair of scalar and vector - * @param quantize function to quantize all values in vectors and scalars - * @param scalarize function to convert numbers to Scalars - */ -function generateScalarVectorBinaryToVectorCases( - scalars: readonly number[], - vectors: ROArrayArray, - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder -): Case[] { - const cases = new Array(); - scalars.forEach(s => { - vectors.forEach(v => { - const c = makeScalarVectorBinaryToVectorCase(s, v, op, quantize, scalarize); - if (c !== undefined) { - cases.push(c); - } - }); - }); - return cases; -} - -/** - * @returns a Case for the input params with op applied - * @param vector vector param (2, 3, or 4 elements) - * @param scalar scalar param - * @param op the op to apply to vector and scalar - * @param quantize function to quantize all values in vectors and scalars - * @param scalarize function to convert numbers to Scalars - */ -function makeVectorScalarBinaryToVectorCase( - vector: readonly number[], - scalar: number, - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder -): Case | undefined { - vector = vector.map(quantize); - scalar = quantize(scalar); - const result = vector.map(v => op(v, scalar)); - if (result.includes(undefined)) { - return undefined; - } - return { - input: [new Vector(vector.map(scalarize)), scalarize(scalar)], - expected: new Vector((result as readonly number[]).map(scalarize)), - }; -} - -/** - * @returns array of Case for the input params with op applied - * @param vectors array of vector params (2, 3, or 4 elements) - * @param scalars array of scalar params - * @param op the op to apply to each pair of vector and scalar - * @param quantize function to quantize all values in vectors and scalars - * @param scalarize function to convert numbers to Scalars - */ -function generateVectorScalarBinaryToVectorCases( - vectors: ROArrayArray, - scalars: readonly number[], - op: BinaryOp, - quantize: QuantizeFunc, - scalarize: ScalarBuilder -): Case[] { - const cases = new Array(); - scalars.forEach(s => { - vectors.forEach(v => { - const c = makeVectorScalarBinaryToVectorCase(v, s, op, quantize, scalarize); - if (c !== undefined) { - cases.push(c); - } - }); - }); - return cases; -} - -/** - * @returns array of Case for the input params with op applied - * @param scalars array of scalar params - * @param vectors array of vector params (2, 3, or 4 elements) - * @param op he op to apply to each pair of scalar and vector - */ -export function generateU32VectorBinaryToVectorCases( - scalars: readonly number[], - vectors: ROArrayArray, - op: BinaryOp -): Case[] { - return generateScalarVectorBinaryToVectorCases(scalars, vectors, op, quantizeToU32, u32); -} - -/** - * @returns array of Case for the input params with op applied - * @param vectors array of vector params (2, 3, or 4 elements) - * @param scalars array of scalar params - * @param op he op to apply to each pair of vector and scalar - */ -export function generateVectorU32BinaryToVectorCases( - vectors: ROArrayArray, - scalars: readonly number[], - op: BinaryOp -): Case[] { - return generateVectorScalarBinaryToVectorCases(vectors, scalars, op, quantizeToU32, u32); -} - -/** - * @returns array of Case for the input params with op applied - * @param scalars array of scalar params - * @param vectors array of vector params (2, 3, or 4 elements) - * @param op he op to apply to each pair of scalar and vector - */ -export function generateI32VectorBinaryToVectorCases( - scalars: readonly number[], - vectors: ROArrayArray, - op: BinaryOp -): Case[] { - return generateScalarVectorBinaryToVectorCases(scalars, vectors, op, quantizeToI32, i32); -} - -/** - * @returns array of Case for the input params with op applied - * @param vectors array of vector params (2, 3, or 4 elements) - * @param scalars array of scalar params - * @param op he op to apply to each pair of vector and scalar - */ -export function generateVectorI32BinaryToVectorCases( - vectors: ROArrayArray, - scalars: readonly number[], - op: BinaryOp -): Case[] { - return generateVectorScalarBinaryToVectorCases(vectors, scalars, op, quantizeToI32, i32); -} diff --git a/src/webgpu/shader/execution/expression/interval_filter.ts b/src/webgpu/shader/execution/expression/interval_filter.ts new file mode 100644 index 000000000000..7471247e54ad --- /dev/null +++ b/src/webgpu/shader/execution/expression/interval_filter.ts @@ -0,0 +1,8 @@ +/** + * Indicates bounds that acceptance intervals need to be within to avoid inputs + * being filtered out. This is used for const-eval tests, since going OOB will + * cause a validation error not an execution error. + */ +export type IntervalFilter = + | 'finite' // Expected to be finite in the interval numeric space + | 'unfiltered'; // No expectations diff --git a/src/webgpu/shader/execution/expression/unary/af_arithmetic.cache.ts b/src/webgpu/shader/execution/expression/unary/af_arithmetic.cache.ts new file mode 100644 index 000000000000..4f274e892217 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/af_arithmetic.cache.ts @@ -0,0 +1,13 @@ +import { FP } from '../../../../util/floating_point.js'; +import { scalarF64Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/af_arithmetic', { + negation: () => { + return FP.abstract.generateScalarToIntervalCases( + scalarF64Range({ neg_norm: 250, neg_sub: 20, pos_sub: 20, pos_norm: 250 }), + 'unfiltered', + FP.abstract.negationInterval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts index 1ab64eb62d51..ef148833eb17 100644 --- a/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts @@ -5,25 +5,13 @@ Execution Tests for AbstractFloat arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeAbstractFloat } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { scalarF64Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { onlyConstInputSource, run } from '../expression.js'; +import { d } from './af_arithmetic.cache.js'; import { abstractUnary } from './unary.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/af_arithmetic', { - negation: () => { - return FP.abstract.generateScalarToIntervalCases( - scalarF64Range({ neg_norm: 250, neg_sub: 20, pos_sub: 20, pos_norm: 250 }), - 'unfiltered', - FP.abstract.negationInterval - ); - }, -}); - g.test('negation') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/unary/af_assignment.cache.ts b/src/webgpu/shader/execution/expression/unary/af_assignment.cache.ts new file mode 100644 index 000000000000..7c607927fe54 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/af_assignment.cache.ts @@ -0,0 +1,51 @@ +import { kValue } from '../../../../util/constants.js'; +import { abstractFloat } from '../../../../util/conversion.js'; +import { FP } from '../../../../util/floating_point.js'; +import { + isSubnormalNumberF64, + limitedScalarF64Range, + scalarF64Range, +} from '../../../../util/math.js'; +import { reinterpretU64AsF64 } from '../../../../util/reinterpret.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/af_assignment', { + abstract: () => { + const inputs = [ + // Values that are useful for debugging the underlying framework/shader code, since it cannot be directly unit tested. + 0, + 0.5, + 0.5, + 1, + -1, + reinterpretU64AsF64(0x7000_0000_0000_0001n), // smallest magnitude negative subnormal with non-zero mantissa + reinterpretU64AsF64(0x0000_0000_0000_0001n), // smallest magnitude positive subnormal with non-zero mantissa + reinterpretU64AsF64(0x600a_aaaa_5555_5555n), // negative subnormal with obvious pattern + reinterpretU64AsF64(0x000a_aaaa_5555_5555n), // positive subnormal with obvious pattern + reinterpretU64AsF64(0x0010_0000_0000_0001n), // smallest magnitude negative normal with non-zero mantissa + reinterpretU64AsF64(0x0010_0000_0000_0001n), // smallest magnitude positive normal with non-zero mantissa + reinterpretU64AsF64(0xf555_5555_aaaa_aaaan), // negative normal with obvious pattern + reinterpretU64AsF64(0x5555_5555_aaaa_aaaan), // positive normal with obvious pattern + reinterpretU64AsF64(0xffef_ffff_ffff_ffffn), // largest magnitude negative normal + reinterpretU64AsF64(0x7fef_ffff_ffff_ffffn), // largest magnitude positive normal + // WebGPU implementation stressing values + ...scalarF64Range(), + ]; + return inputs.map(f => { + return { + input: abstractFloat(f), + expected: isSubnormalNumberF64(f) ? abstractFloat(0) : abstractFloat(f), + }; + }); + }, + f32: () => { + return limitedScalarF64Range(kValue.f32.negative.min, kValue.f32.positive.max).map(f => { + return { input: abstractFloat(f), expected: FP.f32.correctlyRoundedInterval(f) }; + }); + }, + f16: () => { + return limitedScalarF64Range(kValue.f16.negative.min, kValue.f16.positive.max).map(f => { + return { input: abstractFloat(f), expected: FP.f16.correctlyRoundedInterval(f) }; + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts b/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts index d59649bbd40a..76e96fe80354 100644 --- a/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts @@ -4,24 +4,17 @@ Execution Tests for assignment of AbstractFloats import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { kValue } from '../../../../util/constants.js'; -import { abstractFloat, TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { - limitedScalarF64Range, - scalarF64Range, - isSubnormalNumberF64, -} from '../../../../util/math.js'; -import { reinterpretU64AsF64 } from '../../../../util/reinterpret.js'; -import { makeCaseCache } from '../case_cache.js'; +import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../util/conversion.js'; import { + ShaderBuilder, abstractFloatShaderBuilder, basicExpressionBuilder, onlyConstInputSource, run, - ShaderBuilder, } from '../expression.js'; +import { d } from './af_assignment.cache.js'; + function concrete_assignment(): ShaderBuilder { return basicExpressionBuilder(value => `${value}`); } @@ -32,47 +25,6 @@ function abstract_assignment(): ShaderBuilder { export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/af_assignment', { - abstract: () => { - const inputs = [ - // Values that are useful for debugging the underlying framework/shader code, since it cannot be directly unit tested. - 0, - 0.5, - 0.5, - 1, - -1, - reinterpretU64AsF64(0x7000_0000_0000_0001n), // smallest magnitude negative subnormal with non-zero mantissa - reinterpretU64AsF64(0x0000_0000_0000_0001n), // smallest magnitude positive subnormal with non-zero mantissa - reinterpretU64AsF64(0x600a_aaaa_5555_5555n), // negative subnormal with obvious pattern - reinterpretU64AsF64(0x000a_aaaa_5555_5555n), // positive subnormal with obvious pattern - reinterpretU64AsF64(0x0010_0000_0000_0001n), // smallest magnitude negative normal with non-zero mantissa - reinterpretU64AsF64(0x0010_0000_0000_0001n), // smallest magnitude positive normal with non-zero mantissa - reinterpretU64AsF64(0xf555_5555_aaaa_aaaan), // negative normal with obvious pattern - reinterpretU64AsF64(0x5555_5555_aaaa_aaaan), // positive normal with obvious pattern - reinterpretU64AsF64(0xffef_ffff_ffff_ffffn), // largest magnitude negative normal - reinterpretU64AsF64(0x7fef_ffff_ffff_ffffn), // largest magnitude positive normal - // WebGPU implementation stressing values - ...scalarF64Range(), - ]; - return inputs.map(f => { - return { - input: abstractFloat(f), - expected: isSubnormalNumberF64(f) ? abstractFloat(0) : abstractFloat(f), - }; - }); - }, - f32: () => { - return limitedScalarF64Range(kValue.f32.negative.min, kValue.f32.positive.max).map(f => { - return { input: abstractFloat(f), expected: FP.f32.correctlyRoundedInterval(f) }; - }); - }, - f16: () => { - return limitedScalarF64Range(kValue.f16.negative.min, kValue.f16.positive.max).map(f => { - return { input: abstractFloat(f), expected: FP.f16.correctlyRoundedInterval(f) }; - }); - }, -}); - g.test('abstract') .specURL('https://www.w3.org/TR/WGSL/#floating-point-conversion') .desc( diff --git a/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts b/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts new file mode 100644 index 000000000000..f64ec1cd4180 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts @@ -0,0 +1,54 @@ +import { anyOf } from '../../../../util/compare.js'; +import { Scalar, bool, f16, f32, i32, u32 } from '../../../../util/conversion.js'; +import { + fullI32Range, + fullU32Range, + isSubnormalNumberF16, + isSubnormalNumberF32, + scalarF16Range, + scalarF32Range, +} from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/bool_conversion', { + bool: () => { + return [ + { input: bool(true), expected: bool(true) }, + { input: bool(false), expected: bool(false) }, + ]; + }, + u32: () => { + return fullU32Range().map(u => { + return { input: u32(u), expected: u === 0 ? bool(false) : bool(true) }; + }); + }, + i32: () => { + return fullI32Range().map(i => { + return { input: i32(i), expected: i === 0 ? bool(false) : bool(true) }; + }); + }, + f32: () => { + return scalarF32Range().map(f => { + const expected: Scalar[] = []; + if (f !== 0) { + expected.push(bool(true)); + } + if (isSubnormalNumberF32(f)) { + expected.push(bool(false)); + } + return { input: f32(f), expected: anyOf(...expected) }; + }); + }, + f16: () => { + return scalarF16Range().map(f => { + const expected: Scalar[] = []; + if (f !== 0) { + expected.push(bool(true)); + } + if (isSubnormalNumberF16(f)) { + expected.push(bool(false)); + } + return { input: f16(f), expected: anyOf(...expected) }; + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts index 2d83a389cd49..5f5bb31397a3 100644 --- a/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts @@ -4,78 +4,14 @@ Execution Tests for the boolean conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { anyOf } from '../../../../util/compare.js'; -import { - bool, - f32, - f16, - i32, - Scalar, - TypeBool, - TypeF32, - TypeF16, - TypeI32, - TypeU32, - u32, -} from '../../../../util/conversion.js'; -import { - scalarF32Range, - scalarF16Range, - fullI32Range, - fullU32Range, - isSubnormalNumberF32, - isSubnormalNumberF16, -} from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, run, ShaderBuilder } from '../expression.js'; +import { TypeBool, TypeF16, TypeF32, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { ShaderBuilder, allInputSources, run } from '../expression.js'; +import { d } from './bool_conversion.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/bool_conversion', { - bool: () => { - return [ - { input: bool(true), expected: bool(true) }, - { input: bool(false), expected: bool(false) }, - ]; - }, - u32: () => { - return fullU32Range().map(u => { - return { input: u32(u), expected: u === 0 ? bool(false) : bool(true) }; - }); - }, - i32: () => { - return fullI32Range().map(i => { - return { input: i32(i), expected: i === 0 ? bool(false) : bool(true) }; - }); - }, - f32: () => { - return scalarF32Range().map(f => { - const expected: Scalar[] = []; - if (f !== 0) { - expected.push(bool(true)); - } - if (isSubnormalNumberF32(f)) { - expected.push(bool(false)); - } - return { input: f32(f), expected: anyOf(...expected) }; - }); - }, - f16: () => { - return scalarF16Range().map(f => { - const expected: Scalar[] = []; - if (f !== 0) { - expected.push(bool(true)); - } - if (isSubnormalNumberF16(f)) { - expected.push(bool(false)); - } - return { input: f16(f), expected: anyOf(...expected) }; - }); - }, -}); - /** Generate expression builder based on how the test case is to be vectorized */ function vectorizeToExpression(vectorize: undefined | 2 | 3 | 4): ShaderBuilder { return vectorize === undefined ? unary('bool') : unary(`vec${vectorize}`); diff --git a/src/webgpu/shader/execution/expression/unary/f16_arithmetic.cache.ts b/src/webgpu/shader/execution/expression/unary/f16_arithmetic.cache.ts new file mode 100644 index 000000000000..7d9ee35eec00 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/f16_arithmetic.cache.ts @@ -0,0 +1,13 @@ +import { FP } from '../../../../util/floating_point.js'; +import { scalarF16Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/f16_arithmetic', { + negation: () => { + return FP.f16.generateScalarToIntervalCases( + scalarF16Range({ neg_norm: 250, neg_sub: 20, pos_sub: 20, pos_norm: 250 }), + 'unfiltered', + FP.f16.negationInterval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts index e96bb354db8f..60e16e645221 100644 --- a/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts @@ -5,25 +5,13 @@ Execution Tests for the f16 arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF16 } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { scalarF16Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; +import { d } from './f16_arithmetic.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/f16_arithmetic', { - negation: () => { - return FP.f16.generateScalarToIntervalCases( - scalarF16Range({ neg_norm: 250, neg_sub: 20, pos_sub: 20, pos_norm: 250 }), - 'unfiltered', - FP.f16.negationInterval - ); - }, -}); - g.test('negation') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/unary/f16_conversion.cache.ts b/src/webgpu/shader/execution/expression/unary/f16_conversion.cache.ts new file mode 100644 index 000000000000..73d0d94c461a --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/f16_conversion.cache.ts @@ -0,0 +1,109 @@ +import { bool, f16, i32, u32 } from '../../../../util/conversion.js'; +import { FP, FPInterval } from '../../../../util/floating_point.js'; +import { + fullI32Range, + fullU32Range, + scalarF16Range, + scalarF32Range, + sparseMatrixF16Range, + sparseMatrixF32Range, +} from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +const f16FiniteRangeInterval = new FPInterval( + 'f32', + FP.f16.constants().negative.min, + FP.f16.constants().positive.max +); + +// Cases: f32_matCxR_[non_]const +// Note that f32 values may be not exactly representable in f16 and/or out of range. +const f32_mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`f32_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateMatrixToMatrixCases( + sparseMatrixF32Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f16.correctlyRoundedMatrix + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: f16_matCxR_[non_]const +const f16_mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`f16_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + // Input matrix is of f16 types, use f16.generateMatrixToMatrixCases. + return FP.f16.generateMatrixToMatrixCases( + sparseMatrixF16Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f16.correctlyRoundedMatrix + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('unary/f16_conversion', { + bool: () => { + return [ + { input: bool(true), expected: f16(1.0) }, + { input: bool(false), expected: f16(0.0) }, + ]; + }, + u32_non_const: () => { + return [...fullU32Range(), 65504].map(u => { + return { input: u32(u), expected: FP.f16.correctlyRoundedInterval(u) }; + }); + }, + u32_const: () => { + return [...fullU32Range(), 65504] + .filter(v => f16FiniteRangeInterval.contains(v)) + .map(u => { + return { input: u32(u), expected: FP.f16.correctlyRoundedInterval(u) }; + }); + }, + i32_non_const: () => { + return [...fullI32Range(), 65504, -65504].map(i => { + return { input: i32(i), expected: FP.f16.correctlyRoundedInterval(i) }; + }); + }, + i32_const: () => { + return [...fullI32Range(), 65504, -65504] + .filter(v => f16FiniteRangeInterval.contains(v)) + .map(i => { + return { input: i32(i), expected: FP.f16.correctlyRoundedInterval(i) }; + }); + }, + // Note that f32 values may be not exactly representable in f16 and/or out of range. + f32_non_const: () => { + return FP.f32.generateScalarToIntervalCases( + [...scalarF32Range(), 65535.996, -65535.996], + 'unfiltered', + FP.f16.correctlyRoundedInterval + ); + }, + f32_const: () => { + return FP.f32.generateScalarToIntervalCases( + [...scalarF32Range(), 65535.996, -65535.996], + 'finite', + FP.f16.correctlyRoundedInterval + ); + }, + // All f16 values are exactly representable in f16. + f16: () => { + return scalarF16Range().map(f => { + return { input: f16(f), expected: FP.f16.correctlyRoundedInterval(f) }; + }); + }, + ...f32_mat_cases, + ...f16_mat_cases, +}); diff --git a/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts index a4ebeef5fe18..a8da8ff3bd7f 100644 --- a/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts @@ -5,131 +5,20 @@ Execution Tests for the f32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { - bool, - f16, - i32, TypeBool, - TypeF32, TypeF16, + TypeF32, TypeI32, TypeMat, TypeU32, - u32, } from '../../../../util/conversion.js'; -import { FP, FPInterval } from '../../../../util/floating_point.js'; -import { - scalarF32Range, - scalarF16Range, - fullI32Range, - fullU32Range, - sparseMatrixF32Range, - sparseMatrixF16Range, -} from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, run, ShaderBuilder } from '../expression.js'; +import { ShaderBuilder, allInputSources, run } from '../expression.js'; +import { d } from './f16_conversion.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -const f16FiniteRangeInterval = new FPInterval( - 'f32', - FP.f16.constants().negative.min, - FP.f16.constants().positive.max -); - -// Cases: f32_matCxR_[non_]const -// Note that f32 values may be not exactly representable in f16 and/or out of range. -const f32_mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`f32_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateMatrixToMatrixCases( - sparseMatrixF32Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f16.correctlyRoundedMatrix - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: f16_matCxR_[non_]const -const f16_mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`f16_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - // Input matrix is of f16 types, use f16.generateMatrixToMatrixCases. - return FP.f16.generateMatrixToMatrixCases( - sparseMatrixF16Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f16.correctlyRoundedMatrix - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('unary/f16_conversion', { - bool: () => { - return [ - { input: bool(true), expected: f16(1.0) }, - { input: bool(false), expected: f16(0.0) }, - ]; - }, - u32_non_const: () => { - return [...fullU32Range(), 65504].map(u => { - return { input: u32(u), expected: FP.f16.correctlyRoundedInterval(u) }; - }); - }, - u32_const: () => { - return [...fullU32Range(), 65504] - .filter(v => f16FiniteRangeInterval.contains(v)) - .map(u => { - return { input: u32(u), expected: FP.f16.correctlyRoundedInterval(u) }; - }); - }, - i32_non_const: () => { - return [...fullI32Range(), 65504, -65504].map(i => { - return { input: i32(i), expected: FP.f16.correctlyRoundedInterval(i) }; - }); - }, - i32_const: () => { - return [...fullI32Range(), 65504, -65504] - .filter(v => f16FiniteRangeInterval.contains(v)) - .map(i => { - return { input: i32(i), expected: FP.f16.correctlyRoundedInterval(i) }; - }); - }, - // Note that f32 values may be not exactly representable in f16 and/or out of range. - f32_non_const: () => { - return FP.f32.generateScalarToIntervalCases( - [...scalarF32Range(), 65535.996, -65535.996], - 'unfiltered', - FP.f16.correctlyRoundedInterval - ); - }, - f32_const: () => { - return FP.f32.generateScalarToIntervalCases( - [...scalarF32Range(), 65535.996, -65535.996], - 'finite', - FP.f16.correctlyRoundedInterval - ); - }, - // All f16 values are exactly representable in f16. - f16: () => { - return scalarF16Range().map(f => { - return { input: f16(f), expected: FP.f16.correctlyRoundedInterval(f) }; - }); - }, - ...f32_mat_cases, - ...f16_mat_cases, -}); - /** Generate a ShaderBuilder based on how the test case is to be vectorized */ function vectorizeToExpression(vectorize: undefined | 2 | 3 | 4): ShaderBuilder { return vectorize === undefined ? unary('f16') : unary(`vec${vectorize}`); diff --git a/src/webgpu/shader/execution/expression/unary/f32_arithmetic.cache.ts b/src/webgpu/shader/execution/expression/unary/f32_arithmetic.cache.ts new file mode 100644 index 000000000000..b23ed3216bfa --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/f32_arithmetic.cache.ts @@ -0,0 +1,13 @@ +import { FP } from '../../../../util/floating_point.js'; +import { scalarF32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/f32_arithmetic', { + negation: () => { + return FP.f32.generateScalarToIntervalCases( + scalarF32Range({ neg_norm: 250, neg_sub: 20, pos_sub: 20, pos_norm: 250 }), + 'unfiltered', + FP.f32.negationInterval + ); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts index 305a7812dd13..c60e2c3d51b0 100644 --- a/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts @@ -5,25 +5,13 @@ Execution Tests for the f32 arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { TypeF32 } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { scalarF32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; import { allInputSources, run } from '../expression.js'; +import { d } from './f32_arithmetic.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/f32_arithmetic', { - negation: () => { - return FP.f32.generateScalarToIntervalCases( - scalarF32Range({ neg_norm: 250, neg_sub: 20, pos_sub: 20, pos_norm: 250 }), - 'unfiltered', - FP.f32.negationInterval - ); - }, -}); - g.test('negation') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/unary/f32_conversion.cache.ts b/src/webgpu/shader/execution/expression/unary/f32_conversion.cache.ts new file mode 100644 index 000000000000..f61435f07ce4 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/f32_conversion.cache.ts @@ -0,0 +1,79 @@ +import { bool, f16, f32, i32, u32 } from '../../../../util/conversion.js'; +import { FP } from '../../../../util/floating_point.js'; +import { + fullI32Range, + fullU32Range, + scalarF16Range, + scalarF32Range, + sparseMatrixF16Range, + sparseMatrixF32Range, +} from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: f32_matCxR_[non_]const +const f32_mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`f32_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + return FP.f32.generateMatrixToMatrixCases( + sparseMatrixF32Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f32.correctlyRoundedMatrix + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: f16_matCxR_[non_]const +// Note that all f16 values are exactly representable in f32. +const f16_mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).flatMap(rows => + ([true, false] as const).map(nonConst => ({ + [`f16_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { + // Input matrix is of f16 types, use f16.generateMatrixToMatrixCases. + return FP.f16.generateMatrixToMatrixCases( + sparseMatrixF16Range(cols, rows), + nonConst ? 'unfiltered' : 'finite', + FP.f32.correctlyRoundedMatrix + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('unary/f32_conversion', { + bool: () => { + return [ + { input: bool(true), expected: f32(1.0) }, + { input: bool(false), expected: f32(0.0) }, + ]; + }, + u32: () => { + return fullU32Range().map(u => { + return { input: u32(u), expected: FP.f32.correctlyRoundedInterval(u) }; + }); + }, + i32: () => { + return fullI32Range().map(i => { + return { input: i32(i), expected: FP.f32.correctlyRoundedInterval(i) }; + }); + }, + f32: () => { + return scalarF32Range().map(f => { + return { input: f32(f), expected: FP.f32.correctlyRoundedInterval(f) }; + }); + }, + // All f16 values are exactly representable in f32. + f16: () => { + return scalarF16Range().map(f => { + return { input: f16(f), expected: FP.f32.correctlyRoundedInterval(f) }; + }); + }, + ...f32_mat_cases, + ...f16_mat_cases, +}); diff --git a/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts index 1aa0b3e4a51c..3e2fbd540ace 100644 --- a/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts @@ -5,102 +5,20 @@ Execution Tests for the f32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { - bool, - f32, - f16, - i32, TypeBool, - TypeF32, TypeF16, + TypeF32, TypeI32, TypeMat, TypeU32, - u32, } from '../../../../util/conversion.js'; -import { FP } from '../../../../util/floating_point.js'; -import { - scalarF32Range, - scalarF16Range, - fullI32Range, - fullU32Range, - sparseMatrixF32Range, - sparseMatrixF16Range, -} from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, run, ShaderBuilder } from '../expression.js'; +import { ShaderBuilder, allInputSources, run } from '../expression.js'; +import { d } from './f32_conversion.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -// Cases: f32_matCxR_[non_]const -const f32_mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`f32_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - return FP.f32.generateMatrixToMatrixCases( - sparseMatrixF32Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f32.correctlyRoundedMatrix - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -// Cases: f16_matCxR_[non_]const -// Note that all f16 values are exactly representable in f32. -const f16_mat_cases = ([2, 3, 4] as const) - .flatMap(cols => - ([2, 3, 4] as const).flatMap(rows => - ([true, false] as const).map(nonConst => ({ - [`f16_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => { - // Input matrix is of f16 types, use f16.generateMatrixToMatrixCases. - return FP.f16.generateMatrixToMatrixCases( - sparseMatrixF16Range(cols, rows), - nonConst ? 'unfiltered' : 'finite', - FP.f32.correctlyRoundedMatrix - ); - }, - })) - ) - ) - .reduce((a, b) => ({ ...a, ...b }), {}); - -export const d = makeCaseCache('unary/f32_conversion', { - bool: () => { - return [ - { input: bool(true), expected: f32(1.0) }, - { input: bool(false), expected: f32(0.0) }, - ]; - }, - u32: () => { - return fullU32Range().map(u => { - return { input: u32(u), expected: FP.f32.correctlyRoundedInterval(u) }; - }); - }, - i32: () => { - return fullI32Range().map(i => { - return { input: i32(i), expected: FP.f32.correctlyRoundedInterval(i) }; - }); - }, - f32: () => { - return scalarF32Range().map(f => { - return { input: f32(f), expected: FP.f32.correctlyRoundedInterval(f) }; - }); - }, - // All f16 values are exactly representable in f32. - f16: () => { - return scalarF16Range().map(f => { - return { input: f16(f), expected: FP.f32.correctlyRoundedInterval(f) }; - }); - }, - ...f32_mat_cases, - ...f16_mat_cases, -}); - /** Generate a ShaderBuilder based on how the test case is to be vectorized */ function vectorizeToExpression(vectorize: undefined | 2 | 3 | 4): ShaderBuilder { return vectorize === undefined ? unary('f32') : unary(`vec${vectorize}`); diff --git a/src/webgpu/shader/execution/expression/unary/i32_arithmetic.cache.ts b/src/webgpu/shader/execution/expression/unary/i32_arithmetic.cache.ts new file mode 100644 index 000000000000..b7206bcf451a --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/i32_arithmetic.cache.ts @@ -0,0 +1,11 @@ +import { i32 } from '../../../../util/conversion.js'; +import { fullI32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/i32_arithmetic', { + negation: () => { + return fullI32Range().map(e => { + return { input: i32(e), expected: i32(-e) }; + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts index 14519b89675b..a6fe77ada165 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts @@ -4,23 +4,14 @@ Execution Tests for the i32 arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { i32, TypeI32 } from '../../../../util/conversion.js'; -import { fullI32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; +import { TypeI32 } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; +import { d } from './i32_arithmetic.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/i32_arithmetic', { - negation: () => { - return fullI32Range().map(e => { - return { input: i32(e), expected: i32(-e) }; - }); - }, -}); - g.test('negation') .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') .desc( diff --git a/src/webgpu/shader/execution/expression/unary/i32_complement.cache.ts b/src/webgpu/shader/execution/expression/unary/i32_complement.cache.ts new file mode 100644 index 000000000000..2ae18e68fe36 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/i32_complement.cache.ts @@ -0,0 +1,11 @@ +import { i32 } from '../../../../util/conversion.js'; +import { fullI32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/i32_complement', { + complement: () => { + return fullI32Range().map(e => { + return { input: i32(e), expected: i32(~e) }; + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts index e8bda51b51a9..0555da133279 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts @@ -4,23 +4,14 @@ Execution Tests for the i32 bitwise complement operation import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { i32, TypeI32 } from '../../../../util/conversion.js'; -import { fullI32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; +import { TypeI32 } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; +import { d } from './i32_complement.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/i32_complement', { - complement: () => { - return fullI32Range().map(e => { - return { input: i32(e), expected: i32(~e) }; - }); - }, -}); - g.test('i32_complement') .specURL('https://www.w3.org/TR/WGSL/#bit-expr') .desc( diff --git a/src/webgpu/shader/execution/expression/unary/i32_conversion.cache.ts b/src/webgpu/shader/execution/expression/unary/i32_conversion.cache.ts new file mode 100644 index 000000000000..aea80176cc40 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/i32_conversion.cache.ts @@ -0,0 +1,81 @@ +import { kValue } from '../../../../util/constants.js'; +import { bool, f16, f32, i32, u32 } from '../../../../util/conversion.js'; +import { + fullI32Range, + fullU32Range, + quantizeToF16, + quantizeToF32, + scalarF16Range, + scalarF32Range, +} from '../../../../util/math.js'; +import { reinterpretU32AsI32 } from '../../../../util/reinterpret.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/i32_conversion', { + bool: () => { + return [ + { input: bool(true), expected: i32(1) }, + { input: bool(false), expected: i32(0) }, + ]; + }, + u32: () => { + return fullU32Range().map(u => { + return { input: u32(u), expected: i32(reinterpretU32AsI32(u)) }; + }); + }, + i32: () => { + return fullI32Range().map(i => { + return { input: i32(i), expected: i32(i) }; + }); + }, + f32: () => { + return scalarF32Range().map(f => { + // Handles zeros and subnormals + if (Math.abs(f) < 1.0) { + return { input: f32(f), expected: i32(0) }; + } + + if (f <= kValue.i32.negative.min) { + return { input: f32(f), expected: i32(kValue.i32.negative.min) }; + } + + if (f >= kValue.i32.positive.max) { + return { input: f32(f), expected: i32(kValue.i32.positive.max) }; + } + + // All f32 no larger than 2^24 has a precise interger part and a fractional part, just need + // to trunc towards 0 for the result integer. + if (Math.abs(f) <= 2 ** 24) { + return { input: f32(f), expected: i32(Math.trunc(f)) }; + } + + // All f32s between 2 ** 24 and kValue.i32.negative.min/.positive.max are + // integers, so in theory one could use them directly, expect that number + // is actually f64 internally, so they need to be quantized to f32 first. + // Cannot just use trunc here, since that might produce a i32 value that + // is precise in f64, but not in f32. + return { input: f32(f), expected: i32(quantizeToF32(f)) }; + }); + }, + f16: () => { + // Note that finite f16 values are always in range of i32. + return scalarF16Range().map(f => { + // Handles zeros and subnormals + if (Math.abs(f) < 1.0) { + return { input: f16(f), expected: i32(0) }; + } + + // All f16 no larger than <= 2^12 has a precise interger part and a fractional part, just need + // to trunc towards 0 for the result integer. + if (Math.abs(f) <= 2 ** 12) { + return { input: f16(f), expected: i32(Math.trunc(f)) }; + } + + // All f16s larger than 2 ** 12 are integers, so in theory one could use them directly, expect + // that number is actually f64 internally, so they need to be quantized to f16 first. + // Cannot just use trunc here, since that might produce a i32 value that is precise in f64, + // but not in f16. + return { input: f16(f), expected: i32(quantizeToF16(f)) }; + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts index 92379f22c9cf..4ad3c4e7424f 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts @@ -4,104 +4,14 @@ Execution Tests for the i32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { kValue } from '../../../../util/constants.js'; -import { - bool, - f32, - f16, - i32, - TypeBool, - TypeF32, - TypeF16, - TypeI32, - TypeU32, - u32, -} from '../../../../util/conversion.js'; -import { - scalarF32Range, - scalarF16Range, - fullI32Range, - fullU32Range, - quantizeToF32, - quantizeToF16, -} from '../../../../util/math.js'; -import { reinterpretU32AsI32 } from '../../../../util/reinterpret.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, run, ShaderBuilder } from '../expression.js'; +import { TypeBool, TypeF16, TypeF32, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { ShaderBuilder, allInputSources, run } from '../expression.js'; +import { d } from './i32_conversion.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/i32_conversion', { - bool: () => { - return [ - { input: bool(true), expected: i32(1) }, - { input: bool(false), expected: i32(0) }, - ]; - }, - u32: () => { - return fullU32Range().map(u => { - return { input: u32(u), expected: i32(reinterpretU32AsI32(u)) }; - }); - }, - i32: () => { - return fullI32Range().map(i => { - return { input: i32(i), expected: i32(i) }; - }); - }, - f32: () => { - return scalarF32Range().map(f => { - // Handles zeros and subnormals - if (Math.abs(f) < 1.0) { - return { input: f32(f), expected: i32(0) }; - } - - if (f <= kValue.i32.negative.min) { - return { input: f32(f), expected: i32(kValue.i32.negative.min) }; - } - - if (f >= kValue.i32.positive.max) { - return { input: f32(f), expected: i32(kValue.i32.positive.max) }; - } - - // All f32 no larger than 2^24 has a precise interger part and a fractional part, just need - // to trunc towards 0 for the result integer. - if (Math.abs(f) <= 2 ** 24) { - return { input: f32(f), expected: i32(Math.trunc(f)) }; - } - - // All f32s between 2 ** 24 and kValue.i32.negative.min/.positive.max are - // integers, so in theory one could use them directly, expect that number - // is actually f64 internally, so they need to be quantized to f32 first. - // Cannot just use trunc here, since that might produce a i32 value that - // is precise in f64, but not in f32. - return { input: f32(f), expected: i32(quantizeToF32(f)) }; - }); - }, - f16: () => { - // Note that finite f16 values are always in range of i32. - return scalarF16Range().map(f => { - // Handles zeros and subnormals - if (Math.abs(f) < 1.0) { - return { input: f16(f), expected: i32(0) }; - } - - // All f16 no larger than <= 2^12 has a precise interger part and a fractional part, just need - // to trunc towards 0 for the result integer. - if (Math.abs(f) <= 2 ** 12) { - return { input: f16(f), expected: i32(Math.trunc(f)) }; - } - - // All f16s larger than 2 ** 12 are integers, so in theory one could use them directly, expect - // that number is actually f64 internally, so they need to be quantized to f16 first. - // Cannot just use trunc here, since that might produce a i32 value that is precise in f64, - // but not in f16. - return { input: f16(f), expected: i32(quantizeToF16(f)) }; - }); - }, -}); - /** Generate a ShaderBuilder based on how the test case is to be vectorized */ function vectorizeToExpression(vectorize: undefined | 2 | 3 | 4): ShaderBuilder { return vectorize === undefined ? unary('i32') : unary(`vec${vectorize}`); diff --git a/src/webgpu/shader/execution/expression/unary/u32_complement.cache.ts b/src/webgpu/shader/execution/expression/unary/u32_complement.cache.ts new file mode 100644 index 000000000000..67e38b42a9cf --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/u32_complement.cache.ts @@ -0,0 +1,11 @@ +import { u32 } from '../../../../util/conversion.js'; +import { fullU32Range } from '../../../../util/math.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/u32_complement', { + complement: () => { + return fullU32Range().map(e => { + return { input: u32(e), expected: u32(~e) }; + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts b/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts index 446e0918bd18..f5f859054724 100644 --- a/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts @@ -4,23 +4,14 @@ Execution Tests for the u32 bitwise complement operation import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { u32, TypeU32 } from '../../../../util/conversion.js'; -import { fullU32Range } from '../../../../util/math.js'; -import { makeCaseCache } from '../case_cache.js'; +import { TypeU32 } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; +import { d } from './u32_complement.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/u32_complement', { - complement: () => { - return fullU32Range().map(e => { - return { input: u32(e), expected: u32(~e) }; - }); - }, -}); - g.test('u32_complement') .specURL('https://www.w3.org/TR/WGSL/#bit-expr') .desc( diff --git a/src/webgpu/shader/execution/expression/unary/u32_conversion.cache.ts b/src/webgpu/shader/execution/expression/unary/u32_conversion.cache.ts new file mode 100644 index 000000000000..0187415999b8 --- /dev/null +++ b/src/webgpu/shader/execution/expression/unary/u32_conversion.cache.ts @@ -0,0 +1,77 @@ +import { kValue } from '../../../../util/constants.js'; +import { bool, f16, f32, i32, u32 } from '../../../../util/conversion.js'; +import { + fullI32Range, + fullU32Range, + quantizeToF16, + quantizeToF32, + scalarF16Range, + scalarF32Range, +} from '../../../../util/math.js'; +import { reinterpretI32AsU32 } from '../../../../util/reinterpret.js'; +import { makeCaseCache } from '../case_cache.js'; + +export const d = makeCaseCache('unary/u32_conversion', { + bool: () => { + return [ + { input: bool(true), expected: u32(1) }, + { input: bool(false), expected: u32(0) }, + ]; + }, + u32: () => { + return fullU32Range().map(u => { + return { input: u32(u), expected: u32(u) }; + }); + }, + i32: () => { + return fullI32Range().map(i => { + return { input: i32(i), expected: u32(reinterpretI32AsU32(i)) }; + }); + }, + f32: () => { + return scalarF32Range().map(f => { + // Handles zeros, subnormals, and negatives + if (f < 1.0) { + return { input: f32(f), expected: u32(0) }; + } + + if (f >= kValue.u32.max) { + return { input: f32(f), expected: u32(kValue.u32.max) }; + } + + // All f32 no larger than 2^24 has a precise interger part and a fractional part, just need + // to trunc towards 0 for the result integer. + if (f <= 2 ** 24) { + return { input: f32(f), expected: u32(Math.floor(f)) }; + } + + // All f32s between 2 ** 24 and kValue.u32.max are integers, so in theory + // one could use them directly, expect that number is actually f64 + // internally, so they need to be quantized to f32 first. + // Cannot just use floor here, since that might produce a u32 value that + // is precise in f64, but not in f32. + return { input: f32(f), expected: u32(quantizeToF32(f)) }; + }); + }, + f16: () => { + // Note that all positive finite f16 values are in range of u32. + return scalarF16Range().map(f => { + // Handles zeros, subnormals, and negatives + if (f < 1.0) { + return { input: f16(f), expected: u32(0) }; + } + + // All f16 no larger than <= 2^12 has a precise interger part and a fractional part, just need + // to trunc towards 0 for the result integer. + if (f <= 2 ** 12) { + return { input: f16(f), expected: u32(Math.trunc(f)) }; + } + + // All f16s larger than 2 ** 12 are integers, so in theory one could use them directly, expect + // that number is actually f64 internally, so they need to be quantized to f16 first. + // Cannot just use trunc here, since that might produce a u32 value that is precise in f64, + // but not in f16. + return { input: f16(f), expected: u32(quantizeToF16(f)) }; + }); + }, +}); diff --git a/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts index c469f262c93d..d684824362cf 100644 --- a/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts @@ -4,100 +4,14 @@ Execution Tests for the u32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { kValue } from '../../../../util/constants.js'; -import { - bool, - f32, - f16, - i32, - TypeBool, - TypeF32, - TypeF16, - TypeI32, - TypeU32, - u32, -} from '../../../../util/conversion.js'; -import { - scalarF32Range, - scalarF16Range, - fullI32Range, - fullU32Range, - quantizeToF32, - quantizeToF16, -} from '../../../../util/math.js'; -import { reinterpretI32AsU32 } from '../../../../util/reinterpret.js'; -import { makeCaseCache } from '../case_cache.js'; -import { allInputSources, run, ShaderBuilder } from '../expression.js'; +import { TypeBool, TypeF16, TypeF32, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { ShaderBuilder, allInputSources, run } from '../expression.js'; +import { d } from './u32_conversion.cache.js'; import { unary } from './unary.js'; export const g = makeTestGroup(GPUTest); -export const d = makeCaseCache('unary/u32_conversion', { - bool: () => { - return [ - { input: bool(true), expected: u32(1) }, - { input: bool(false), expected: u32(0) }, - ]; - }, - u32: () => { - return fullU32Range().map(u => { - return { input: u32(u), expected: u32(u) }; - }); - }, - i32: () => { - return fullI32Range().map(i => { - return { input: i32(i), expected: u32(reinterpretI32AsU32(i)) }; - }); - }, - f32: () => { - return scalarF32Range().map(f => { - // Handles zeros, subnormals, and negatives - if (f < 1.0) { - return { input: f32(f), expected: u32(0) }; - } - - if (f >= kValue.u32.max) { - return { input: f32(f), expected: u32(kValue.u32.max) }; - } - - // All f32 no larger than 2^24 has a precise interger part and a fractional part, just need - // to trunc towards 0 for the result integer. - if (f <= 2 ** 24) { - return { input: f32(f), expected: u32(Math.floor(f)) }; - } - - // All f32s between 2 ** 24 and kValue.u32.max are integers, so in theory - // one could use them directly, expect that number is actually f64 - // internally, so they need to be quantized to f32 first. - // Cannot just use floor here, since that might produce a u32 value that - // is precise in f64, but not in f32. - return { input: f32(f), expected: u32(quantizeToF32(f)) }; - }); - }, - f16: () => { - // Note that all positive finite f16 values are in range of u32. - return scalarF16Range().map(f => { - // Handles zeros, subnormals, and negatives - if (f < 1.0) { - return { input: f16(f), expected: u32(0) }; - } - - // All f16 no larger than <= 2^12 has a precise interger part and a fractional part, just need - // to trunc towards 0 for the result integer. - if (f <= 2 ** 12) { - return { input: f16(f), expected: u32(Math.trunc(f)) }; - } - - // All f16s larger than 2 ** 12 are integers, so in theory one could use them directly, expect - // that number is actually f64 internally, so they need to be quantized to f16 first. - // Cannot just use trunc here, since that might produce a u32 value that is precise in f64, - // but not in f16. - return { input: f16(f), expected: u32(quantizeToF16(f)) }; - }); - }, -}); - /** Generate a ShaderBuilder based on how the test case is to be vectorized */ function vectorizeToExpression(vectorize: undefined | 2 | 3 | 4): ShaderBuilder { return vectorize === undefined ? unary('u32') : unary(`vec${vectorize}`); diff --git a/src/webgpu/util/compare.ts b/src/webgpu/util/compare.ts index 45599d25f63c..95573e6ceae2 100644 --- a/src/webgpu/util/compare.ts +++ b/src/webgpu/util/compare.ts @@ -5,7 +5,7 @@ import { deserializeExpectation, serializeExpectation, } from '../shader/execution/expression/case_cache.js'; -import { Expectation, toComparator } from '../shader/execution/expression/expression.js'; +import { Expectation, toComparator } from '../shader/execution/expression/expectation.js'; import BinaryStream from './binary_stream.js'; import { isFloatValue, Matrix, Scalar, Value, Vector } from './conversion.js'; diff --git a/src/webgpu/util/device_pool.ts b/src/webgpu/util/device_pool.ts index 1e6c0402cb8f..4e45aac76bb8 100644 --- a/src/webgpu/util/device_pool.ts +++ b/src/webgpu/util/device_pool.ts @@ -9,6 +9,9 @@ import { } from '../../common/util/util.js'; import { getDefaultLimits, kLimits } from '../capability_info.js'; +// MUST_NOT_BE_IMPORTED_BY_DATA_CACHE +// This file should not be transitively imported by .cache.ts files + export interface DeviceProvider { readonly device: GPUDevice; expectDeviceLost(reason: GPUDeviceLostReason): void; diff --git a/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index 5dcc11e828d2..ed70413f28ab 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -1,7 +1,8 @@ import { ROArrayArray, ROArrayArrayArray } from '../../common/util/types.js'; import { assert, unreachable } from '../../common/util/util.js'; import { Float16Array } from '../../external/petamoriken/float16/float16.js'; -import { Case, IntervalFilter } from '../shader/execution/expression/expression.js'; +import { Case } from '../shader/execution/expression/case.js'; +import { IntervalFilter } from '../shader/execution/expression/interval_filter.js'; import BinaryStream from './binary_stream.js'; import { anyOf } from './compare.js';