diff options
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call')
111 files changed, 10757 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts new file mode 100644 index 0000000000..272d0190a5 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts @@ -0,0 +1,167 @@ +export const description = ` +Execution tests for the 'abs' builtin function + +S is AbstractInt, i32, or u32 +T is S or vecN<S> +@const fn abs(e: T ) -> T +The absolute value of e. Component-wise when T is a vector. If e is a signed +integral scalar type and evaluates to the largest negative value, then the +result is e. If e is an unsigned integral type, then the result is e. + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn abs(e: T ) -> T +Returns the absolute value of e (e.g. e with a positive sign bit). +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { kBit } from '../../../../../util/constants.js'; +import { i32Bits, TypeF32, TypeI32, TypeU32, u32Bits } from '../../../../../util/conversion.js'; +import { absInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('abs', { + f32: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', absInterval); + }, +}); + +g.test('abstract_int') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`abstract int tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`unsigned int tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + await run(t, builtin('abs'), [TypeU32], TypeU32, t.params, [ + // Min and Max u32 + { input: u32Bits(kBit.u32.min), expected: u32Bits(kBit.u32.min) }, + { input: u32Bits(kBit.u32.max), expected: u32Bits(kBit.u32.max) }, + // Powers of 2: -2^i: 0 =< i =< 31 + { input: u32Bits(kBit.powTwo.to0), expected: u32Bits(kBit.powTwo.to0) }, + { input: u32Bits(kBit.powTwo.to1), expected: u32Bits(kBit.powTwo.to1) }, + { input: u32Bits(kBit.powTwo.to2), expected: u32Bits(kBit.powTwo.to2) }, + { input: u32Bits(kBit.powTwo.to3), expected: u32Bits(kBit.powTwo.to3) }, + { input: u32Bits(kBit.powTwo.to4), expected: u32Bits(kBit.powTwo.to4) }, + { input: u32Bits(kBit.powTwo.to5), expected: u32Bits(kBit.powTwo.to5) }, + { input: u32Bits(kBit.powTwo.to6), expected: u32Bits(kBit.powTwo.to6) }, + { input: u32Bits(kBit.powTwo.to7), expected: u32Bits(kBit.powTwo.to7) }, + { input: u32Bits(kBit.powTwo.to8), expected: u32Bits(kBit.powTwo.to8) }, + { input: u32Bits(kBit.powTwo.to9), expected: u32Bits(kBit.powTwo.to9) }, + { input: u32Bits(kBit.powTwo.to10), expected: u32Bits(kBit.powTwo.to10) }, + { input: u32Bits(kBit.powTwo.to11), expected: u32Bits(kBit.powTwo.to11) }, + { input: u32Bits(kBit.powTwo.to12), expected: u32Bits(kBit.powTwo.to12) }, + { input: u32Bits(kBit.powTwo.to13), expected: u32Bits(kBit.powTwo.to13) }, + { input: u32Bits(kBit.powTwo.to14), expected: u32Bits(kBit.powTwo.to14) }, + { input: u32Bits(kBit.powTwo.to15), expected: u32Bits(kBit.powTwo.to15) }, + { input: u32Bits(kBit.powTwo.to16), expected: u32Bits(kBit.powTwo.to16) }, + { input: u32Bits(kBit.powTwo.to17), expected: u32Bits(kBit.powTwo.to17) }, + { input: u32Bits(kBit.powTwo.to18), expected: u32Bits(kBit.powTwo.to18) }, + { input: u32Bits(kBit.powTwo.to19), expected: u32Bits(kBit.powTwo.to19) }, + { input: u32Bits(kBit.powTwo.to20), expected: u32Bits(kBit.powTwo.to20) }, + { input: u32Bits(kBit.powTwo.to21), expected: u32Bits(kBit.powTwo.to21) }, + { input: u32Bits(kBit.powTwo.to22), expected: u32Bits(kBit.powTwo.to22) }, + { input: u32Bits(kBit.powTwo.to23), expected: u32Bits(kBit.powTwo.to23) }, + { input: u32Bits(kBit.powTwo.to24), expected: u32Bits(kBit.powTwo.to24) }, + { input: u32Bits(kBit.powTwo.to25), expected: u32Bits(kBit.powTwo.to25) }, + { input: u32Bits(kBit.powTwo.to26), expected: u32Bits(kBit.powTwo.to26) }, + { input: u32Bits(kBit.powTwo.to27), expected: u32Bits(kBit.powTwo.to27) }, + { input: u32Bits(kBit.powTwo.to28), expected: u32Bits(kBit.powTwo.to28) }, + { input: u32Bits(kBit.powTwo.to29), expected: u32Bits(kBit.powTwo.to29) }, + { input: u32Bits(kBit.powTwo.to30), expected: u32Bits(kBit.powTwo.to30) }, + { input: u32Bits(kBit.powTwo.to31), expected: u32Bits(kBit.powTwo.to31) }, + ]); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`signed int tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + await run(t, builtin('abs'), [TypeI32], TypeI32, t.params, [ + // Min and max i32 + // If e evaluates to the largest negative value, then the result is e. + { input: i32Bits(kBit.i32.negative.min), expected: i32Bits(kBit.i32.negative.min) }, + { input: i32Bits(kBit.i32.negative.max), expected: i32Bits(kBit.i32.positive.min) }, + { input: i32Bits(kBit.i32.positive.max), expected: i32Bits(kBit.i32.positive.max) }, + { input: i32Bits(kBit.i32.positive.min), expected: i32Bits(kBit.i32.positive.min) }, + // input: -1 * pow(2, n), n = {-31, ..., 0 }, expected: pow(2, n), n = {-31, ..., 0}] + { input: i32Bits(kBit.negPowTwo.to0), expected: i32Bits(kBit.powTwo.to0) }, + { input: i32Bits(kBit.negPowTwo.to1), expected: i32Bits(kBit.powTwo.to1) }, + { input: i32Bits(kBit.negPowTwo.to2), expected: i32Bits(kBit.powTwo.to2) }, + { input: i32Bits(kBit.negPowTwo.to3), expected: i32Bits(kBit.powTwo.to3) }, + { input: i32Bits(kBit.negPowTwo.to4), expected: i32Bits(kBit.powTwo.to4) }, + { input: i32Bits(kBit.negPowTwo.to5), expected: i32Bits(kBit.powTwo.to5) }, + { input: i32Bits(kBit.negPowTwo.to6), expected: i32Bits(kBit.powTwo.to6) }, + { input: i32Bits(kBit.negPowTwo.to7), expected: i32Bits(kBit.powTwo.to7) }, + { input: i32Bits(kBit.negPowTwo.to8), expected: i32Bits(kBit.powTwo.to8) }, + { input: i32Bits(kBit.negPowTwo.to9), expected: i32Bits(kBit.powTwo.to9) }, + { input: i32Bits(kBit.negPowTwo.to10), expected: i32Bits(kBit.powTwo.to10) }, + { input: i32Bits(kBit.negPowTwo.to11), expected: i32Bits(kBit.powTwo.to11) }, + { input: i32Bits(kBit.negPowTwo.to12), expected: i32Bits(kBit.powTwo.to12) }, + { input: i32Bits(kBit.negPowTwo.to13), expected: i32Bits(kBit.powTwo.to13) }, + { input: i32Bits(kBit.negPowTwo.to14), expected: i32Bits(kBit.powTwo.to14) }, + { input: i32Bits(kBit.negPowTwo.to15), expected: i32Bits(kBit.powTwo.to15) }, + { input: i32Bits(kBit.negPowTwo.to16), expected: i32Bits(kBit.powTwo.to16) }, + { input: i32Bits(kBit.negPowTwo.to17), expected: i32Bits(kBit.powTwo.to17) }, + { input: i32Bits(kBit.negPowTwo.to18), expected: i32Bits(kBit.powTwo.to18) }, + { input: i32Bits(kBit.negPowTwo.to19), expected: i32Bits(kBit.powTwo.to19) }, + { input: i32Bits(kBit.negPowTwo.to20), expected: i32Bits(kBit.powTwo.to20) }, + { input: i32Bits(kBit.negPowTwo.to21), expected: i32Bits(kBit.powTwo.to21) }, + { input: i32Bits(kBit.negPowTwo.to22), expected: i32Bits(kBit.powTwo.to22) }, + { input: i32Bits(kBit.negPowTwo.to23), expected: i32Bits(kBit.powTwo.to23) }, + { input: i32Bits(kBit.negPowTwo.to24), expected: i32Bits(kBit.powTwo.to24) }, + { input: i32Bits(kBit.negPowTwo.to25), expected: i32Bits(kBit.powTwo.to25) }, + { input: i32Bits(kBit.negPowTwo.to26), expected: i32Bits(kBit.powTwo.to26) }, + { input: i32Bits(kBit.negPowTwo.to27), expected: i32Bits(kBit.powTwo.to27) }, + { input: i32Bits(kBit.negPowTwo.to28), expected: i32Bits(kBit.powTwo.to28) }, + { input: i32Bits(kBit.negPowTwo.to29), expected: i32Bits(kBit.powTwo.to29) }, + { input: i32Bits(kBit.negPowTwo.to30), expected: i32Bits(kBit.powTwo.to30) }, + { input: i32Bits(kBit.negPowTwo.to31), expected: i32Bits(kBit.powTwo.to31) }, + ]); + }); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`float 32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('abs'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts new file mode 100644 index 0000000000..312b3160f2 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts @@ -0,0 +1,61 @@ +export const description = ` +Execution tests for the 'acos' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn acos(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { acosInterval } from '../../../../../util/f32_interval.js'; +import { linearRange, fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +const inputs = [ + ...linearRange(-1, 1, 100), // acos is defined on [-1, 1] + ...fullF32Range(), +]; + +export const d = makeCaseCache('acos', { + f32_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'f32-only', acosInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'unfiltered', acosInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('acos'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts new file mode 100644 index 0000000000..89887d05ef --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts @@ -0,0 +1,65 @@ +export const description = ` +Execution tests for the 'acosh' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn acosh(e: T ) -> T +Returns the hyperbolic arc cosine of e. The result is 0 when e < 1. +Computes the non-negative functional inverse of cosh. +Component-wise when T is a vector. +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 } from '../../../../../util/conversion.js'; +import { acoshIntervals } from '../../../../../util/f32_interval.js'; +import { biasedRange, fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +const inputs = [ + ...biasedRange(1, 2, 100), // x near 1 can be problematic to implement + ...fullF32Range(), +]; + +export const d = makeCaseCache('acosh', { + f32_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'f32-only', ...acoshIntervals); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'unfiltered', ...acoshIntervals); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('acosh'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts new file mode 100644 index 0000000000..9a2938c1d5 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts @@ -0,0 +1,92 @@ +export const description = ` +Execution tests for the 'all' builtin function + +S is a bool +T is S or vecN<S> +@const fn all(e: T) -> bool +Returns e if e is scalar. +Returns true if each component of e is true if e is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { + False, + True, + TypeBool, + TypeVec, + vec2, + vec3, + vec4, +} from '../../../../../util/conversion.js'; +import { allInputSources, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('bool') + .specURL('https://www.w3.org/TR/WGSL/#logical-builtin-functions') + .desc(`bool tests`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('overload', ['scalar', 'vec2', 'vec3', 'vec4'] as const) + ) + .fn(async t => { + const overloads = { + scalar: { + type: TypeBool, + cases: [ + { input: False, expected: False }, + { input: True, expected: True }, + ], + }, + vec2: { + type: TypeVec(2, TypeBool), + cases: [ + { input: vec2(False, False), expected: False }, + { input: vec2(True, False), expected: False }, + { input: vec2(False, True), expected: False }, + { input: vec2(True, True), expected: True }, + ], + }, + vec3: { + type: TypeVec(3, TypeBool), + cases: [ + { input: vec3(False, False, False), expected: False }, + { input: vec3(True, False, False), expected: False }, + { input: vec3(False, True, False), expected: False }, + { input: vec3(True, True, False), expected: False }, + { input: vec3(False, False, True), expected: False }, + { input: vec3(True, False, True), expected: False }, + { input: vec3(False, True, True), expected: False }, + { input: vec3(True, True, True), expected: True }, + ], + }, + vec4: { + type: TypeVec(4, TypeBool), + cases: [ + { input: vec4(False, False, False, False), expected: False }, + { input: vec4(False, True, False, False), expected: False }, + { input: vec4(False, False, True, False), expected: False }, + { input: vec4(False, True, True, False), expected: False }, + { input: vec4(False, False, False, True), expected: False }, + { input: vec4(False, True, False, True), expected: False }, + { input: vec4(False, False, True, True), expected: False }, + { input: vec4(False, True, True, True), expected: False }, + { input: vec4(True, False, False, False), expected: False }, + { input: vec4(True, False, False, True), expected: False }, + { input: vec4(True, False, True, False), expected: False }, + { input: vec4(True, False, True, True), expected: False }, + { input: vec4(True, True, False, False), expected: False }, + { input: vec4(True, True, False, True), expected: False }, + { input: vec4(True, True, True, False), expected: False }, + { input: vec4(True, True, True, True), expected: True }, + ], + }, + }; + const overload = overloads[t.params.overload]; + + await run(t, builtin('all'), [overload.type], TypeBool, t.params, overload.cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts new file mode 100644 index 0000000000..19ed7d186f --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts @@ -0,0 +1,92 @@ +export const description = ` +Execution tests for the 'any' builtin function + +S is a bool +T is S or vecN<S> +@const fn all(e) -> bool +Returns e if e is scalar. +Returns true if any component of e is true if e is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { + False, + True, + TypeBool, + TypeVec, + vec2, + vec3, + vec4, +} from '../../../../../util/conversion.js'; +import { allInputSources, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('bool') + .specURL('https://www.w3.org/TR/WGSL/#logical-builtin-functions') + .desc(`bool tests`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('overload', ['scalar', 'vec2', 'vec3', 'vec4'] as const) + ) + .fn(async t => { + const overloads = { + scalar: { + type: TypeBool, + cases: [ + { input: False, expected: False }, + { input: True, expected: True }, + ], + }, + vec2: { + type: TypeVec(2, TypeBool), + cases: [ + { input: vec2(False, False), expected: False }, + { input: vec2(True, False), expected: True }, + { input: vec2(False, True), expected: True }, + { input: vec2(True, True), expected: True }, + ], + }, + vec3: { + type: TypeVec(3, TypeBool), + cases: [ + { input: vec3(False, False, False), expected: False }, + { input: vec3(True, False, False), expected: True }, + { input: vec3(False, True, False), expected: True }, + { input: vec3(True, True, False), expected: True }, + { input: vec3(False, False, True), expected: True }, + { input: vec3(True, False, True), expected: True }, + { input: vec3(False, True, True), expected: True }, + { input: vec3(True, True, True), expected: True }, + ], + }, + vec4: { + type: TypeVec(4, TypeBool), + cases: [ + { input: vec4(False, False, False, False), expected: False }, + { input: vec4(False, True, False, False), expected: True }, + { input: vec4(False, False, True, False), expected: True }, + { input: vec4(False, True, True, False), expected: True }, + { input: vec4(False, False, False, True), expected: True }, + { input: vec4(False, True, False, True), expected: True }, + { input: vec4(False, False, True, True), expected: True }, + { input: vec4(False, True, True, True), expected: True }, + { input: vec4(True, False, False, False), expected: True }, + { input: vec4(True, False, False, True), expected: True }, + { input: vec4(True, False, True, False), expected: True }, + { input: vec4(True, False, True, True), expected: True }, + { input: vec4(True, True, False, False), expected: True }, + { input: vec4(True, True, False, True), expected: True }, + { input: vec4(True, True, True, False), expected: True }, + { input: vec4(True, True, True, True), expected: True }, + ], + }, + }; + const overload = overloads[t.params.overload]; + + await run(t, builtin('any'), [overload.type], TypeBool, t.params, overload.cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/arrayLength.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/arrayLength.spec.ts new file mode 100644 index 0000000000..0b1c62c773 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/arrayLength.spec.ts @@ -0,0 +1,16 @@ +export const description = ` +Execution tests for the 'arrayLength' builtin function + +fn arrayLength(e: ptr<storage,array<T>> ) -> u32 +Returns the number of elements in the runtime-sized array. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('array') + .specURL('https://www.w3.org/TR/WGSL/#array-builtin-functions') + .desc(`array length tests`) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts new file mode 100644 index 0000000000..6ff62d2431 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts @@ -0,0 +1,61 @@ +export const description = ` +Execution tests for the 'asin' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn asin(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { asinInterval } from '../../../../../util/f32_interval.js'; +import { linearRange, fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +const inputs = [ + ...linearRange(-1, 1, 100), // asin is defined on [-1, 1] + ...fullF32Range(), +]; + +export const d = makeCaseCache('asin', { + f32_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'f32-only', asinInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'unfiltered', asinInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('asin'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts new file mode 100644 index 0000000000..3c2b4fcc13 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts @@ -0,0 +1,56 @@ +export const description = ` +Execution tests for the 'sinh' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn asinh(e: T ) -> T +Returns the hyperbolic arc sine of e. +Computes the functional inverse of sinh. +Component-wise when T is a vector. + +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { asinhInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('asinh', { + f32: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', asinhInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float test`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('asinh'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts new file mode 100644 index 0000000000..218ccfc792 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts @@ -0,0 +1,76 @@ +export const description = ` +Execution tests for the 'atan' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn atan(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { atanInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +const inputs = [ + // Known values + -Math.sqrt(3), + -1, + -1 / Math.sqrt(3), + 0, + 1, + 1 / Math.sqrt(3), + Math.sqrt(3), + + ...fullF32Range(), +]; + +export const d = makeCaseCache('atan', { + f32_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'f32-only', atanInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'unfiltered', atanInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +f32 tests + +TODO(#792): Decide what the ground-truth is for these tests. [1] +` + ) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('atan'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts new file mode 100644 index 0000000000..c1ba3f4c51 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts @@ -0,0 +1,71 @@ +export const description = ` +Execution tests for the 'atan2' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn atan2(e1: T ,e2: T ) -> T +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 { kValue } from '../../../../../util/constants.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { atan2Interval } from '../../../../../util/f32_interval.js'; +import { linearRange, sparseF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateBinaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('atan2', { + f32: () => { + // Using sparse, since there a N^2 cases being generated, but including extra values around 0, since that is where + // there is a discontinuity that implementations tend to behave badly at. + const numeric_range = [ + ...sparseF32Range(), + ...linearRange(kValue.f32.negative.max, kValue.f32.positive.min, 10), + ]; + return generateBinaryToF32IntervalCases( + numeric_range, + numeric_range, + 'unfiltered', + atan2Interval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +f32 tests + +TODO(#792): Decide what the ground-truth is for these tests. [1] +` + ) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('atan2'), [TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts new file mode 100644 index 0000000000..11267930c8 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts @@ -0,0 +1,68 @@ +export const description = ` +Execution tests for the 'atanh' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn atanh(e: T ) -> T +Returns the hyperbolic arc tangent of e. The result is 0 when abs(e) ≥ 1. +Computes the functional inverse of tanh. +Component-wise when T is a vector. +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 { kValue } from '../../../../../util/constants.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { atanhInterval } from '../../../../../util/f32_interval.js'; +import { biasedRange, fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +const inputs = [ + ...biasedRange(kValue.f32.negative.less_than_one, -0.9, 20), // discontinuity at x = -1 + -1, + ...biasedRange(kValue.f32.positive.less_than_one, 0.9, 20), // discontinuity at x = 1 + 1, + ...fullF32Range(), +]; + +export const d = makeCaseCache('atanh', { + f32_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'f32-only', atanhInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'unfiltered', atanhInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('atanh'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAdd.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAdd.spec.ts new file mode 100644 index 0000000000..9dcae3a062 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAdd.spec.ts @@ -0,0 +1,39 @@ +export const description = ` +Atomically read, add and store value. + + * Load the original value pointed to by atomic_ptr. + * Obtains a new value by adding with the value v. + * Store the new value using atomic_ptr. + +Returns the original value stored in the atomic object. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('add') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicAdd(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAnd.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAnd.spec.ts new file mode 100644 index 0000000000..3e896af780 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicAnd.spec.ts @@ -0,0 +1,39 @@ +export const description = ` +Atomically read, and and store value. + + * Load the original value pointed to by atomic_ptr. + * Obtains a new value by anding with the value v. + * Store the new value using atomic_ptr. + +Returns the original value stored in the atomic object. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('and') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicAnd(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicCompareExchangeWeak.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicCompareExchangeWeak.spec.ts new file mode 100644 index 0000000000..9172e3a638 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicCompareExchangeWeak.spec.ts @@ -0,0 +1,49 @@ +export const description = ` +Performs the following steps atomically: + * Load the original value pointed to by atomic_ptr. + * Compare the original value to the value v using an equality operation. + * Store the value v only if the result of the equality comparison was true. + +Returns a two member structure, where the first member, old_value, is the original +value of the atomic object and the second member, exchanged, is whether or not +the comparison succeeded. + +Note: the equality comparison may spuriously fail on some implementations. +That is, the second component of the result vector may be false even if the first +component of the result vector equals cmp. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('exchange') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicCompareExchangeWeak(atomic_ptr: ptr<SC, atomic<T>, read_write>, cmp: T, v: T) -> __atomic_compare_exchange_result<T> + +struct __atomic_compare_exchange_result<T> { + old_value : T, // old value stored in the atomic + exchanged : bool, // true if the exchange was done +} +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicExchange.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicExchange.spec.ts new file mode 100644 index 0000000000..cd7fa072c5 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicExchange.spec.ts @@ -0,0 +1,33 @@ +export const description = ` +Atomically stores the value v in the atomic object pointed to atomic_ptr and returns the original value stored in the atomic object. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('exchange') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicExchange(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicLoad.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicLoad.spec.ts new file mode 100644 index 0000000000..c2e6a7e3d8 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicLoad.spec.ts @@ -0,0 +1,34 @@ +export const description = ` +Returns the atomically loaded the value pointed to by atomic_ptr. It does not modify the object. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-load') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('load') + .specURL('https://www.w3.org/TR/WGSL/#atomic-load') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicLoad(atomic_ptr: ptr<SC, atomic<T>, read_write>) -> T + +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMax.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMax.spec.ts new file mode 100644 index 0000000000..b321524839 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMax.spec.ts @@ -0,0 +1,39 @@ +export const description = ` +Atomically read, max and store value. + + * Load the original value pointed to by atomic_ptr. + * Obtains a new value by taking the max with the value v. + * Store the new value using atomic_ptr. + +Returns the original value stored in the atomic object. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('max') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicMax(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMin.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMin.spec.ts new file mode 100644 index 0000000000..deea6a4105 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicMin.spec.ts @@ -0,0 +1,39 @@ +export const description = ` +Atomically read, min and store value. + + * Load the original value pointed to by atomic_ptr. + * Obtains a new value by take the min with the value v. + * Store the new value using atomic_ptr. + +Returns the original value stored in the atomic object. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('min') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicMin(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicOr.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicOr.spec.ts new file mode 100644 index 0000000000..0b8f6cdc41 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicOr.spec.ts @@ -0,0 +1,39 @@ +export const description = ` +Atomically read, or and store value. + + * Load the original value pointed to by atomic_ptr. + * Obtains a new value by or'ing with the value v. + * Store the new value using atomic_ptr. + +Returns the original value stored in the atomic object. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('or') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicOr(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicStore.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicStore.spec.ts new file mode 100644 index 0000000000..18b713ba2e --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicStore.spec.ts @@ -0,0 +1,33 @@ +export const description = ` +Atomically stores the value v in the atomic object pointed to by atomic_ptr. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-store') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('store') + .specURL('https://www.w3.org/TR/WGSL/#atomic-store') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicStore(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicSub.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicSub.spec.ts new file mode 100644 index 0000000000..cd150c59fb --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicSub.spec.ts @@ -0,0 +1,39 @@ +export const description = ` +Atomically read, subtract and store value. + + * Load the original value pointed to by atomic_ptr. + * Obtains a new value by subtracting with the value v. + * Store the new value using atomic_ptr. + +Returns the original value stored in the atomic object. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('sub') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicSub(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicXor.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicXor.spec.ts new file mode 100644 index 0000000000..d8efd32f11 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/atomicXor.spec.ts @@ -0,0 +1,39 @@ +export const description = ` +Atomically read, xor and store value. + + * Load the original value pointed to by atomic_ptr. + * Obtains a new value by xor'ing with the value v. + * Store the new value using atomic_ptr. + +Returns the original value stored in the atomic object. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +Atomic built-in functions must not be used in a vertex shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('xor') + .specURL('https://www.w3.org/TR/WGSL/#atomic-rmw') + .desc( + ` +SC is storage or workgroup +T is i32 or u32 + +fn atomicXor(atomic_ptr: ptr<SC, atomic<T>, read_write>, v: T) -> T +` + ) + .params(u => + u.combine('SC', ['storage', 'uniform'] as const).combine('T', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts new file mode 100644 index 0000000000..c54245b699 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/builtin.ts @@ -0,0 +1,6 @@ +import { ExpressionBuilder } from '../../expression.js'; + +/* @returns an ExpressionBuilder that calls the builtin with the given name */ +export function builtin(name: string): ExpressionBuilder { + return values => `${name}(${values.join(', ')})`; +} diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts new file mode 100644 index 0000000000..49626628ab --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts @@ -0,0 +1,72 @@ +export const description = ` +Execution tests for the 'ceil' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn ceil(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { ceilInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('ceil', { + f32: () => { + return generateUnaryToF32IntervalCases( + [ + // Small positive numbers + 0.1, + 0.9, + 1.0, + 1.1, + 1.9, + // Small negative numbers + -0.1, + -0.9, + -1.0, + -1.1, + -1.9, + ...fullF32Range(), + ], + 'unfiltered', + ceilInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('ceil'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts new file mode 100644 index 0000000000..9411d667be --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts @@ -0,0 +1,172 @@ +export const description = ` +Execution tests for the 'clamp' builtin function + +S is AbstractInt, i32, or u32 +T is S or vecN<S> +@const fn clamp(e: T , low: T, high: T) -> T +Returns min(max(e,low),high). Component-wise when T is a vector. + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const clamp(e: T , low: T , high: T) -> T +Returns either min(max(e,low),high), or the median of the three values e, low, high. +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { kBit } from '../../../../../util/constants.js'; +import { + i32, + i32Bits, + Scalar, + TypeF32, + TypeI32, + TypeU32, + u32, + u32Bits, +} from '../../../../../util/conversion.js'; +import { clampIntervals } from '../../../../../util/f32_interval.js'; +import { sparseF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, Case, generateTernaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('clamp', { + u32: () => { + // This array must be strictly increasing, since that ordering determines + // the expected values. + const test_values: Array<Scalar> = [ + u32Bits(kBit.u32.min), + u32(1), + u32(2), + u32(0x70000000), + u32(0x80000000), + u32Bits(kBit.u32.max), + ]; + + return generateIntegerTestCases(test_values); + }, + i32: () => { + // This array must be strictly increasing, since that ordering determines + // the expected values. + const test_values: Array<Scalar> = [ + i32Bits(kBit.i32.negative.min), + i32(-2), + i32(-1), + i32(0), + i32(1), + i32(2), + i32Bits(0x70000000), + i32Bits(kBit.i32.positive.max), + ]; + + return generateIntegerTestCases(test_values); + }, + f32_const: () => { + return generateTernaryToF32IntervalCases( + sparseF32Range(), + sparseF32Range(), + sparseF32Range(), + 'f32-only', + ...clampIntervals + ); + }, + f32_non_const: () => { + return generateTernaryToF32IntervalCases( + sparseF32Range(), + sparseF32Range(), + sparseF32Range(), + 'unfiltered', + ...clampIntervals + ); + }, +}); + +/** + * Calculates clamp using the min-max formula. + * clamp(e, f, g) = min(max(e, f), g) + * + * Operates on indices of an ascending sorted array, instead of the actual + * values to avoid rounding issues. + * + * @returns the index of the clamped value + */ +function calculateMinMaxClamp(ei: number, fi: number, gi: number): number { + return Math.min(Math.max(ei, fi), gi); +} + +/** @returns a set of clamp test cases from an ascending list of integer values */ +function generateIntegerTestCases(test_values: Array<Scalar>): Array<Case> { + const cases = new Array<Case>(); + test_values.forEach((e, ei) => { + test_values.forEach((f, fi) => { + test_values.forEach((g, gi) => { + const expected_idx = calculateMinMaxClamp(ei, fi, gi); + const expected = test_values[expected_idx]; + cases.push({ input: [e, f, g], expected }); + }); + }); + }); + return cases; +} + +g.test('abstract_int') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`abstract int tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('u32'); + await run(t, builtin('clamp'), [TypeU32, TypeU32, TypeU32], TypeU32, t.params, cases); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('i32'); + await run(t, builtin('clamp'), [TypeI32, TypeI32, TypeI32], TypeI32, t.params, cases); + }); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('clamp'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts new file mode 100644 index 0000000000..c81b985dc5 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts @@ -0,0 +1,68 @@ +export const description = ` +Execution tests for the 'cos' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn cos(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { cosInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('cos', { + f32: () => { + return generateUnaryToF32IntervalCases( + [ + // Well defined accuracy range + ...linearRange(-Math.PI, Math.PI, 1000), + ...fullF32Range(), + ], + 'unfiltered', + cosInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +f32 tests + +TODO(#792): Decide what the ground-truth is for these tests. [1] +` + ) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('cos'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts new file mode 100644 index 0000000000..2d2e0fdf80 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts @@ -0,0 +1,56 @@ +export const description = ` +Execution tests for the 'cosh' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn cosh(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { coshInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('cosh', { + f32_const: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'f32-only', coshInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', coshInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('cosh'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts new file mode 100644 index 0000000000..cfae4bb6e0 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts @@ -0,0 +1,250 @@ +export const description = ` +Execution tests for the 'countLeadingZeros' builtin function + +S is i32 or u32 +T is S or vecN<S> +@const fn countLeadingZeros(e: T ) -> T +The number of consecutive 0 bits starting from the most significant bit of e, +when T is a scalar type. +Component-wise when T is a vector. +Also known as "clz" in some languages. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeU32, u32Bits, u32, TypeI32, i32Bits, i32 } from '../../../../../util/conversion.js'; +import { allInputSources, Config, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('countLeadingZeros'), [TypeU32], TypeU32, cfg, [ + // Zero + { input: u32Bits(0b00000000000000000000000000000000), expected: u32(32) }, + + // One + { input: u32Bits(0b00000000000000000000000000000001), expected: u32(31) }, + + // 0's after leading 1 + { input: u32Bits(0b00000000000000000000000000000010), expected: u32(30) }, + { input: u32Bits(0b00000000000000000000000000000100), expected: u32(29) }, + { input: u32Bits(0b00000000000000000000000000001000), expected: u32(28) }, + { input: u32Bits(0b00000000000000000000000000010000), expected: u32(27) }, + { input: u32Bits(0b00000000000000000000000000100000), expected: u32(26) }, + { input: u32Bits(0b00000000000000000000000001000000), expected: u32(25) }, + { input: u32Bits(0b00000000000000000000000010000000), expected: u32(24) }, + { input: u32Bits(0b00000000000000000000000100000000), expected: u32(23) }, + { input: u32Bits(0b00000000000000000000001000000000), expected: u32(22) }, + { input: u32Bits(0b00000000000000000000010000000000), expected: u32(21) }, + { input: u32Bits(0b00000000000000000000100000000000), expected: u32(20) }, + { input: u32Bits(0b00000000000000000001000000000000), expected: u32(19) }, + { input: u32Bits(0b00000000000000000010000000000000), expected: u32(18) }, + { input: u32Bits(0b00000000000000000100000000000000), expected: u32(17) }, + { input: u32Bits(0b00000000000000001000000000000000), expected: u32(16) }, + { input: u32Bits(0b00000000000000010000000000000000), expected: u32(15) }, + { input: u32Bits(0b00000000000000100000000000000000), expected: u32(14) }, + { input: u32Bits(0b00000000000001000000000000000000), expected: u32(13) }, + { input: u32Bits(0b00000000000010000000000000000000), expected: u32(12) }, + { input: u32Bits(0b00000000000100000000000000000000), expected: u32(11) }, + { input: u32Bits(0b00000000001000000000000000000000), expected: u32(10) }, + { input: u32Bits(0b00000000010000000000000000000000), expected: u32(9) }, + { input: u32Bits(0b00000000100000000000000000000000), expected: u32(8) }, + { input: u32Bits(0b00000001000000000000000000000000), expected: u32(7) }, + { input: u32Bits(0b00000010000000000000000000000000), expected: u32(6) }, + { input: u32Bits(0b00000100000000000000000000000000), expected: u32(5) }, + { input: u32Bits(0b00001000000000000000000000000000), expected: u32(4) }, + { input: u32Bits(0b00010000000000000000000000000000), expected: u32(3) }, + { input: u32Bits(0b00100000000000000000000000000000), expected: u32(2) }, + { input: u32Bits(0b01000000000000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b10000000000000000000000000000000), expected: u32(0) }, + + // 1's after leading 1 + { input: u32Bits(0b00000000000000000000000000000011), expected: u32(30) }, + { input: u32Bits(0b00000000000000000000000000000111), expected: u32(29) }, + { input: u32Bits(0b00000000000000000000000000001111), expected: u32(28) }, + { input: u32Bits(0b00000000000000000000000000011111), expected: u32(27) }, + { input: u32Bits(0b00000000000000000000000000111111), expected: u32(26) }, + { input: u32Bits(0b00000000000000000000000001111111), expected: u32(25) }, + { input: u32Bits(0b00000000000000000000000011111111), expected: u32(24) }, + { input: u32Bits(0b00000000000000000000000111111111), expected: u32(23) }, + { input: u32Bits(0b00000000000000000000001111111111), expected: u32(22) }, + { input: u32Bits(0b00000000000000000000011111111111), expected: u32(21) }, + { input: u32Bits(0b00000000000000000000111111111111), expected: u32(20) }, + { input: u32Bits(0b00000000000000000001111111111111), expected: u32(19) }, + { input: u32Bits(0b00000000000000000011111111111111), expected: u32(18) }, + { input: u32Bits(0b00000000000000000111111111111111), expected: u32(17) }, + { input: u32Bits(0b00000000000000001111111111111111), expected: u32(16) }, + { input: u32Bits(0b00000000000000011111111111111111), expected: u32(15) }, + { input: u32Bits(0b00000000000000111111111111111111), expected: u32(14) }, + { input: u32Bits(0b00000000000001111111111111111111), expected: u32(13) }, + { input: u32Bits(0b00000000000011111111111111111111), expected: u32(12) }, + { input: u32Bits(0b00000000000111111111111111111111), expected: u32(11) }, + { input: u32Bits(0b00000000001111111111111111111111), expected: u32(10) }, + { input: u32Bits(0b00000000011111111111111111111111), expected: u32(9) }, + { input: u32Bits(0b00000000111111111111111111111111), expected: u32(8) }, + { input: u32Bits(0b00000001111111111111111111111111), expected: u32(7) }, + { input: u32Bits(0b00000011111111111111111111111111), expected: u32(6) }, + { input: u32Bits(0b00000111111111111111111111111111), expected: u32(5) }, + { input: u32Bits(0b00001111111111111111111111111111), expected: u32(4) }, + { input: u32Bits(0b00011111111111111111111111111111), expected: u32(3) }, + { input: u32Bits(0b00111111111111111111111111111111), expected: u32(2) }, + { input: u32Bits(0b01111111111111111111111111111111), expected: u32(1) }, + { input: u32Bits(0b11111111111111111111111111111111), expected: u32(0) }, + + // random after leading 1 + { input: u32Bits(0b00000000000000000000000000000110), expected: u32(29) }, + { input: u32Bits(0b00000000000000000000000000001101), expected: u32(28) }, + { input: u32Bits(0b00000000000000000000000000011101), expected: u32(27) }, + { input: u32Bits(0b00000000000000000000000000111001), expected: u32(26) }, + { input: u32Bits(0b00000000000000000000000001101111), expected: u32(25) }, + { input: u32Bits(0b00000000000000000000000011111111), expected: u32(24) }, + { input: u32Bits(0b00000000000000000000000111101111), expected: u32(23) }, + { input: u32Bits(0b00000000000000000000001111111111), expected: u32(22) }, + { input: u32Bits(0b00000000000000000000011111110001), expected: u32(21) }, + { input: u32Bits(0b00000000000000000000111011011101), expected: u32(20) }, + { input: u32Bits(0b00000000000000000001101101111111), expected: u32(19) }, + { input: u32Bits(0b00000000000000000011111111011111), expected: u32(18) }, + { input: u32Bits(0b00000000000000000101111001110101), expected: u32(17) }, + { input: u32Bits(0b00000000000000001101111011110111), expected: u32(16) }, + { input: u32Bits(0b00000000000000011111111111110011), expected: u32(15) }, + { input: u32Bits(0b00000000000000111111111110111111), expected: u32(14) }, + { input: u32Bits(0b00000000000001111111011111111111), expected: u32(13) }, + { input: u32Bits(0b00000000000011111111111111111111), expected: u32(12) }, + { input: u32Bits(0b00000000000111110101011110111111), expected: u32(11) }, + { input: u32Bits(0b00000000001111101111111111110111), expected: u32(10) }, + { input: u32Bits(0b00000000011111111111010000101111), expected: u32(9) }, + { input: u32Bits(0b00000000111111111111001111111011), expected: u32(8) }, + { input: u32Bits(0b00000001111111011111101111111111), expected: u32(7) }, + { input: u32Bits(0b00000011101011111011110111111011), expected: u32(6) }, + { input: u32Bits(0b00000111111110111111111111111111), expected: u32(5) }, + { input: u32Bits(0b00001111000000011011011110111111), expected: u32(4) }, + { input: u32Bits(0b00011110101111011111111111111111), expected: u32(3) }, + { input: u32Bits(0b00110110111111100111111110111101), expected: u32(2) }, + { input: u32Bits(0b01010111111101111111011111011111), expected: u32(1) }, + { input: u32Bits(0b11100010011110101101101110101111), expected: u32(0) }, + ]); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('countLeadingZeros'), [TypeI32], TypeI32, cfg, [ + // Zero + { input: i32Bits(0b00000000000000000000000000000000), expected: i32(32) }, + + // One + { input: i32Bits(0b00000000000000000000000000000001), expected: i32(31) }, + + // 0's after leading 1 + { input: i32Bits(0b00000000000000000000000000000010), expected: i32(30) }, + { input: i32Bits(0b00000000000000000000000000000100), expected: i32(29) }, + { input: i32Bits(0b00000000000000000000000000001000), expected: i32(28) }, + { input: i32Bits(0b00000000000000000000000000010000), expected: i32(27) }, + { input: i32Bits(0b00000000000000000000000000100000), expected: i32(26) }, + { input: i32Bits(0b00000000000000000000000001000000), expected: i32(25) }, + { input: i32Bits(0b00000000000000000000000010000000), expected: i32(24) }, + { input: i32Bits(0b00000000000000000000000100000000), expected: i32(23) }, + { input: i32Bits(0b00000000000000000000001000000000), expected: i32(22) }, + { input: i32Bits(0b00000000000000000000010000000000), expected: i32(21) }, + { input: i32Bits(0b00000000000000000000100000000000), expected: i32(20) }, + { input: i32Bits(0b00000000000000000001000000000000), expected: i32(19) }, + { input: i32Bits(0b00000000000000000010000000000000), expected: i32(18) }, + { input: i32Bits(0b00000000000000000100000000000000), expected: i32(17) }, + { input: i32Bits(0b00000000000000001000000000000000), expected: i32(16) }, + { input: i32Bits(0b00000000000000010000000000000000), expected: i32(15) }, + { input: i32Bits(0b00000000000000100000000000000000), expected: i32(14) }, + { input: i32Bits(0b00000000000001000000000000000000), expected: i32(13) }, + { input: i32Bits(0b00000000000010000000000000000000), expected: i32(12) }, + { input: i32Bits(0b00000000000100000000000000000000), expected: i32(11) }, + { input: i32Bits(0b00000000001000000000000000000000), expected: i32(10) }, + { input: i32Bits(0b00000000010000000000000000000000), expected: i32(9) }, + { input: i32Bits(0b00000000100000000000000000000000), expected: i32(8) }, + { input: i32Bits(0b00000001000000000000000000000000), expected: i32(7) }, + { input: i32Bits(0b00000010000000000000000000000000), expected: i32(6) }, + { input: i32Bits(0b00000100000000000000000000000000), expected: i32(5) }, + { input: i32Bits(0b00001000000000000000000000000000), expected: i32(4) }, + { input: i32Bits(0b00010000000000000000000000000000), expected: i32(3) }, + { input: i32Bits(0b00100000000000000000000000000000), expected: i32(2) }, + { input: i32Bits(0b01000000000000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b10000000000000000000000000000000), expected: i32(0) }, + + // 1's after leading 1 + { input: i32Bits(0b00000000000000000000000000000011), expected: i32(30) }, + { input: i32Bits(0b00000000000000000000000000000111), expected: i32(29) }, + { input: i32Bits(0b00000000000000000000000000001111), expected: i32(28) }, + { input: i32Bits(0b00000000000000000000000000011111), expected: i32(27) }, + { input: i32Bits(0b00000000000000000000000000111111), expected: i32(26) }, + { input: i32Bits(0b00000000000000000000000001111111), expected: i32(25) }, + { input: i32Bits(0b00000000000000000000000011111111), expected: i32(24) }, + { input: i32Bits(0b00000000000000000000000111111111), expected: i32(23) }, + { input: i32Bits(0b00000000000000000000001111111111), expected: i32(22) }, + { input: i32Bits(0b00000000000000000000011111111111), expected: i32(21) }, + { input: i32Bits(0b00000000000000000000111111111111), expected: i32(20) }, + { input: i32Bits(0b00000000000000000001111111111111), expected: i32(19) }, + { input: i32Bits(0b00000000000000000011111111111111), expected: i32(18) }, + { input: i32Bits(0b00000000000000000111111111111111), expected: i32(17) }, + { input: i32Bits(0b00000000000000001111111111111111), expected: i32(16) }, + { input: i32Bits(0b00000000000000011111111111111111), expected: i32(15) }, + { input: i32Bits(0b00000000000000111111111111111111), expected: i32(14) }, + { input: i32Bits(0b00000000000001111111111111111111), expected: i32(13) }, + { input: i32Bits(0b00000000000011111111111111111111), expected: i32(12) }, + { input: i32Bits(0b00000000000111111111111111111111), expected: i32(11) }, + { input: i32Bits(0b00000000001111111111111111111111), expected: i32(10) }, + { input: i32Bits(0b00000000011111111111111111111111), expected: i32(9) }, + { input: i32Bits(0b00000000111111111111111111111111), expected: i32(8) }, + { input: i32Bits(0b00000001111111111111111111111111), expected: i32(7) }, + { input: i32Bits(0b00000011111111111111111111111111), expected: i32(6) }, + { input: i32Bits(0b00000111111111111111111111111111), expected: i32(5) }, + { input: i32Bits(0b00001111111111111111111111111111), expected: i32(4) }, + { input: i32Bits(0b00011111111111111111111111111111), expected: i32(3) }, + { input: i32Bits(0b00111111111111111111111111111111), expected: i32(2) }, + { input: i32Bits(0b01111111111111111111111111111111), expected: i32(1) }, + { input: i32Bits(0b11111111111111111111111111111111), expected: i32(0) }, + + // random after leading 1 + { input: i32Bits(0b00000000000000000000000000000110), expected: i32(29) }, + { input: i32Bits(0b00000000000000000000000000001101), expected: i32(28) }, + { input: i32Bits(0b00000000000000000000000000011101), expected: i32(27) }, + { input: i32Bits(0b00000000000000000000000000111001), expected: i32(26) }, + { input: i32Bits(0b00000000000000000000000001101111), expected: i32(25) }, + { input: i32Bits(0b00000000000000000000000011111111), expected: i32(24) }, + { input: i32Bits(0b00000000000000000000000111101111), expected: i32(23) }, + { input: i32Bits(0b00000000000000000000001111111111), expected: i32(22) }, + { input: i32Bits(0b00000000000000000000011111110001), expected: i32(21) }, + { input: i32Bits(0b00000000000000000000111011011101), expected: i32(20) }, + { input: i32Bits(0b00000000000000000001101101111111), expected: i32(19) }, + { input: i32Bits(0b00000000000000000011111111011111), expected: i32(18) }, + { input: i32Bits(0b00000000000000000101111001110101), expected: i32(17) }, + { input: i32Bits(0b00000000000000001101111011110111), expected: i32(16) }, + { input: i32Bits(0b00000000000000011111111111110011), expected: i32(15) }, + { input: i32Bits(0b00000000000000111111111110111111), expected: i32(14) }, + { input: i32Bits(0b00000000000001111111011111111111), expected: i32(13) }, + { input: i32Bits(0b00000000000011111111111111111111), expected: i32(12) }, + { input: i32Bits(0b00000000000111110101011110111111), expected: i32(11) }, + { input: i32Bits(0b00000000001111101111111111110111), expected: i32(10) }, + { input: i32Bits(0b00000000011111111111010000101111), expected: i32(9) }, + { input: i32Bits(0b00000000111111111111001111111011), expected: i32(8) }, + { input: i32Bits(0b00000001111111011111101111111111), expected: i32(7) }, + { input: i32Bits(0b00000011101011111011110111111011), expected: i32(6) }, + { input: i32Bits(0b00000111111110111111111111111111), expected: i32(5) }, + { input: i32Bits(0b00001111000000011011011110111111), expected: i32(4) }, + { input: i32Bits(0b00011110101111011111111111111111), expected: i32(3) }, + { input: i32Bits(0b00110110111111100111111110111101), expected: i32(2) }, + { input: i32Bits(0b01010111111101111111011111011111), expected: i32(1) }, + { input: i32Bits(0b11100010011110101101101110101111), expected: i32(0) }, + ]); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts new file mode 100644 index 0000000000..f0be916285 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts @@ -0,0 +1,249 @@ +export const description = ` +Execution tests for the 'countOneBits' builtin function + +S is i32 or u32 +T is S or vecN<S> +@const fn countOneBits(e: T ) -> T +The number of 1 bits in the representation of e. +Also known as "population count". +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeU32, u32Bits, u32, TypeI32, i32Bits, i32 } from '../../../../../util/conversion.js'; +import { allInputSources, Config, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('countOneBits'), [TypeU32], TypeU32, cfg, [ + // Zero + { input: u32Bits(0b00000000000000000000000000000000), expected: u32(0) }, + + // One + { input: u32Bits(0b00000000000000000000000000000001), expected: u32(1) }, + + // 0's after leading 1 + { input: u32Bits(0b00000000000000000000000000000010), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000000000100), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000000001000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000000010000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000000100000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000001000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000010000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000100000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000001000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000010000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000100000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000001000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000010000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000000100000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000001000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000010000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000000100000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000001000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000010000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000000100000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000001000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000010000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000000100000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000001000000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000010000000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00000100000000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00001000000000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00010000000000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b00100000000000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b01000000000000000000000000000000), expected: u32(1) }, + { input: u32Bits(0b10000000000000000000000000000000), expected: u32(1) }, + + // 1's after leading 1 + { input: u32Bits(0b00000000000000000000000000000011), expected: u32(2) }, + { input: u32Bits(0b00000000000000000000000000000111), expected: u32(3) }, + { input: u32Bits(0b00000000000000000000000000001111), expected: u32(4) }, + { input: u32Bits(0b00000000000000000000000000011111), expected: u32(5) }, + { input: u32Bits(0b00000000000000000000000000111111), expected: u32(6) }, + { input: u32Bits(0b00000000000000000000000001111111), expected: u32(7) }, + { input: u32Bits(0b00000000000000000000000011111111), expected: u32(8) }, + { input: u32Bits(0b00000000000000000000000111111111), expected: u32(9) }, + { input: u32Bits(0b00000000000000000000001111111111), expected: u32(10) }, + { input: u32Bits(0b00000000000000000000011111111111), expected: u32(11) }, + { input: u32Bits(0b00000000000000000000111111111111), expected: u32(12) }, + { input: u32Bits(0b00000000000000000001111111111111), expected: u32(13) }, + { input: u32Bits(0b00000000000000000011111111111111), expected: u32(14) }, + { input: u32Bits(0b00000000000000000111111111111111), expected: u32(15) }, + { input: u32Bits(0b00000000000000001111111111111111), expected: u32(16) }, + { input: u32Bits(0b00000000000000011111111111111111), expected: u32(17) }, + { input: u32Bits(0b00000000000000111111111111111111), expected: u32(18) }, + { input: u32Bits(0b00000000000001111111111111111111), expected: u32(19) }, + { input: u32Bits(0b00000000000011111111111111111111), expected: u32(20) }, + { input: u32Bits(0b00000000000111111111111111111111), expected: u32(21) }, + { input: u32Bits(0b00000000001111111111111111111111), expected: u32(22) }, + { input: u32Bits(0b00000000011111111111111111111111), expected: u32(23) }, + { input: u32Bits(0b00000000111111111111111111111111), expected: u32(24) }, + { input: u32Bits(0b00000001111111111111111111111111), expected: u32(25) }, + { input: u32Bits(0b00000011111111111111111111111111), expected: u32(26) }, + { input: u32Bits(0b00000111111111111111111111111111), expected: u32(27) }, + { input: u32Bits(0b00001111111111111111111111111111), expected: u32(28) }, + { input: u32Bits(0b00011111111111111111111111111111), expected: u32(29) }, + { input: u32Bits(0b00111111111111111111111111111111), expected: u32(30) }, + { input: u32Bits(0b01111111111111111111111111111111), expected: u32(31) }, + { input: u32Bits(0b11111111111111111111111111111111), expected: u32(32) }, + + // random after leading 1 + { input: u32Bits(0b00000000000000000000000000000110), expected: u32(2) }, + { input: u32Bits(0b00000000000000000000000000001101), expected: u32(3) }, + { input: u32Bits(0b00000000000000000000000000011101), expected: u32(4) }, + { input: u32Bits(0b00000000000000000000000000111001), expected: u32(4) }, + { input: u32Bits(0b00000000000000000000000001101111), expected: u32(6) }, + { input: u32Bits(0b00000000000000000000000011111111), expected: u32(8) }, + { input: u32Bits(0b00000000000000000000000111101111), expected: u32(8) }, + { input: u32Bits(0b00000000000000000000001111111111), expected: u32(10) }, + { input: u32Bits(0b00000000000000000000011111110001), expected: u32(8) }, + { input: u32Bits(0b00000000000000000000111011011101), expected: u32(9) }, + { input: u32Bits(0b00000000000000000001101101111111), expected: u32(11) }, + { input: u32Bits(0b00000000000000000011111111011111), expected: u32(13) }, + { input: u32Bits(0b00000000000000000101111001110101), expected: u32(10) }, + { input: u32Bits(0b00000000000000001101111011110111), expected: u32(13) }, + { input: u32Bits(0b00000000000000011111111111110011), expected: u32(15) }, + { input: u32Bits(0b00000000000000111111111110111111), expected: u32(17) }, + { input: u32Bits(0b00000000000001111111011111111111), expected: u32(18) }, + { input: u32Bits(0b00000000000011111111111111111111), expected: u32(20) }, + { input: u32Bits(0b00000000000111110101011110111111), expected: u32(17) }, + { input: u32Bits(0b00000000001111101111111111110111), expected: u32(20) }, + { input: u32Bits(0b00000000011111111111010000101111), expected: u32(17) }, + { input: u32Bits(0b00000000111111111111001111111011), expected: u32(21) }, + { input: u32Bits(0b00000001111111011111101111111111), expected: u32(23) }, + { input: u32Bits(0b00000011101011111011110111111011), expected: u32(21) }, + { input: u32Bits(0b00000111111110111111111111111111), expected: u32(26) }, + { input: u32Bits(0b00001111000000011011011110111111), expected: u32(18) }, + { input: u32Bits(0b00011110101111011111111111111111), expected: u32(26) }, + { input: u32Bits(0b00110110111111100111111110111101), expected: u32(24) }, + { input: u32Bits(0b01010111111101111111011111011111), expected: u32(26) }, + { input: u32Bits(0b11100010011110101101101110101111), expected: u32(21) }, + ]); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('countOneBits'), [TypeI32], TypeI32, cfg, [ + // Zero + { input: i32Bits(0b00000000000000000000000000000000), expected: i32(0) }, + + // One + { input: i32Bits(0b00000000000000000000000000000001), expected: i32(1) }, + + // 0's after leading 1 + { input: i32Bits(0b00000000000000000000000000000010), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000000000100), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000000001000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000000010000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000000100000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000001000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000010000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000100000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000001000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000010000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000100000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000001000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000010000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000000100000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000001000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000010000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000000100000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000001000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000010000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000000100000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000001000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000010000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000000100000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000001000000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000010000000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00000100000000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00001000000000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00010000000000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b00100000000000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b01000000000000000000000000000000), expected: i32(1) }, + { input: i32Bits(0b10000000000000000000000000000000), expected: i32(1) }, + + // 1's after leading 1 + { input: i32Bits(0b00000000000000000000000000000011), expected: i32(2) }, + { input: i32Bits(0b00000000000000000000000000000111), expected: i32(3) }, + { input: i32Bits(0b00000000000000000000000000001111), expected: i32(4) }, + { input: i32Bits(0b00000000000000000000000000011111), expected: i32(5) }, + { input: i32Bits(0b00000000000000000000000000111111), expected: i32(6) }, + { input: i32Bits(0b00000000000000000000000001111111), expected: i32(7) }, + { input: i32Bits(0b00000000000000000000000011111111), expected: i32(8) }, + { input: i32Bits(0b00000000000000000000000111111111), expected: i32(9) }, + { input: i32Bits(0b00000000000000000000001111111111), expected: i32(10) }, + { input: i32Bits(0b00000000000000000000011111111111), expected: i32(11) }, + { input: i32Bits(0b00000000000000000000111111111111), expected: i32(12) }, + { input: i32Bits(0b00000000000000000001111111111111), expected: i32(13) }, + { input: i32Bits(0b00000000000000000011111111111111), expected: i32(14) }, + { input: i32Bits(0b00000000000000000111111111111111), expected: i32(15) }, + { input: i32Bits(0b00000000000000001111111111111111), expected: i32(16) }, + { input: i32Bits(0b00000000000000011111111111111111), expected: i32(17) }, + { input: i32Bits(0b00000000000000111111111111111111), expected: i32(18) }, + { input: i32Bits(0b00000000000001111111111111111111), expected: i32(19) }, + { input: i32Bits(0b00000000000011111111111111111111), expected: i32(20) }, + { input: i32Bits(0b00000000000111111111111111111111), expected: i32(21) }, + { input: i32Bits(0b00000000001111111111111111111111), expected: i32(22) }, + { input: i32Bits(0b00000000011111111111111111111111), expected: i32(23) }, + { input: i32Bits(0b00000000111111111111111111111111), expected: i32(24) }, + { input: i32Bits(0b00000001111111111111111111111111), expected: i32(25) }, + { input: i32Bits(0b00000011111111111111111111111111), expected: i32(26) }, + { input: i32Bits(0b00000111111111111111111111111111), expected: i32(27) }, + { input: i32Bits(0b00001111111111111111111111111111), expected: i32(28) }, + { input: i32Bits(0b00011111111111111111111111111111), expected: i32(29) }, + { input: i32Bits(0b00111111111111111111111111111111), expected: i32(30) }, + { input: i32Bits(0b01111111111111111111111111111111), expected: i32(31) }, + { input: i32Bits(0b11111111111111111111111111111111), expected: i32(32) }, + + // random after leading 1 + { input: i32Bits(0b00000000000000000000000000000110), expected: i32(2) }, + { input: i32Bits(0b00000000000000000000000000001101), expected: i32(3) }, + { input: i32Bits(0b00000000000000000000000000011101), expected: i32(4) }, + { input: i32Bits(0b00000000000000000000000000111001), expected: i32(4) }, + { input: i32Bits(0b00000000000000000000000001101111), expected: i32(6) }, + { input: i32Bits(0b00000000000000000000000011111111), expected: i32(8) }, + { input: i32Bits(0b00000000000000000000000111101111), expected: i32(8) }, + { input: i32Bits(0b00000000000000000000001111111111), expected: i32(10) }, + { input: i32Bits(0b00000000000000000000011111110001), expected: i32(8) }, + { input: i32Bits(0b00000000000000000000111011011101), expected: i32(9) }, + { input: i32Bits(0b00000000000000000001101101111111), expected: i32(11) }, + { input: i32Bits(0b00000000000000000011111111011111), expected: i32(13) }, + { input: i32Bits(0b00000000000000000101111001110101), expected: i32(10) }, + { input: i32Bits(0b00000000000000001101111011110111), expected: i32(13) }, + { input: i32Bits(0b00000000000000011111111111110011), expected: i32(15) }, + { input: i32Bits(0b00000000000000111111111110111111), expected: i32(17) }, + { input: i32Bits(0b00000000000001111111011111111111), expected: i32(18) }, + { input: i32Bits(0b00000000000011111111111111111111), expected: i32(20) }, + { input: i32Bits(0b00000000000111110101011110111111), expected: i32(17) }, + { input: i32Bits(0b00000000001111101111111111110111), expected: i32(20) }, + { input: i32Bits(0b00000000011111111111010000101111), expected: i32(17) }, + { input: i32Bits(0b00000000111111111111001111111011), expected: i32(21) }, + { input: i32Bits(0b00000001111111011111101111111111), expected: i32(23) }, + { input: i32Bits(0b00000011101011111011110111111011), expected: i32(21) }, + { input: i32Bits(0b00000111111110111111111111111111), expected: i32(26) }, + { input: i32Bits(0b00001111000000011011011110111111), expected: i32(18) }, + { input: i32Bits(0b00011110101111011111111111111111), expected: i32(26) }, + { input: i32Bits(0b00110110111111100111111110111101), expected: i32(24) }, + { input: i32Bits(0b01010111111101111111011111011111), expected: i32(26) }, + { input: i32Bits(0b11100010011110101101101110101111), expected: i32(21) }, + ]); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts new file mode 100644 index 0000000000..d0b3198f49 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts @@ -0,0 +1,250 @@ +export const description = ` +Execution tests for the 'countTrailingZeros' builtin function + +S is i32 or u32 +T is S or vecN<S> +@const fn countTrailingZeros(e: T ) -> T +The number of consecutive 0 bits starting from the least significant bit of e, +when T is a scalar type. +Component-wise when T is a vector. +Also known as "ctz" in some languages. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js'; +import { allInputSources, Config, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('countTrailingZeros'), [TypeU32], TypeU32, cfg, [ + // Zero + { input: u32Bits(0b00000000000000000000000000000000), expected: u32(32) }, + + // High bit + { input: u32Bits(0b10000000000000000000000000000000), expected: u32(31) }, + + // 0's before trailing 1 + { input: u32Bits(0b00000000000000000000000000000001), expected: u32(0) }, + { input: u32Bits(0b00000000000000000000000000000010), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000000000100), expected: u32(2) }, + { input: u32Bits(0b00000000000000000000000000001000), expected: u32(3) }, + { input: u32Bits(0b00000000000000000000000000010000), expected: u32(4) }, + { input: u32Bits(0b00000000000000000000000000100000), expected: u32(5) }, + { input: u32Bits(0b00000000000000000000000001000000), expected: u32(6) }, + { input: u32Bits(0b00000000000000000000000010000000), expected: u32(7) }, + { input: u32Bits(0b00000000000000000000000100000000), expected: u32(8) }, + { input: u32Bits(0b00000000000000000000001000000000), expected: u32(9) }, + { input: u32Bits(0b00000000000000000000010000000000), expected: u32(10) }, + { input: u32Bits(0b00000000000000000000100000000000), expected: u32(11) }, + { input: u32Bits(0b00000000000000000001000000000000), expected: u32(12) }, + { input: u32Bits(0b00000000000000000010000000000000), expected: u32(13) }, + { input: u32Bits(0b00000000000000000100000000000000), expected: u32(14) }, + { input: u32Bits(0b00000000000000001000000000000000), expected: u32(15) }, + { input: u32Bits(0b00000000000000010000000000000000), expected: u32(16) }, + { input: u32Bits(0b00000000000000100000000000000000), expected: u32(17) }, + { input: u32Bits(0b00000000000001000000000000000000), expected: u32(18) }, + { input: u32Bits(0b00000000000010000000000000000000), expected: u32(19) }, + { input: u32Bits(0b00000000000100000000000000000000), expected: u32(20) }, + { input: u32Bits(0b00000000001000000000000000000000), expected: u32(21) }, + { input: u32Bits(0b00000000010000000000000000000000), expected: u32(22) }, + { input: u32Bits(0b00000000100000000000000000000000), expected: u32(23) }, + { input: u32Bits(0b00000001000000000000000000000000), expected: u32(24) }, + { input: u32Bits(0b00000010000000000000000000000000), expected: u32(25) }, + { input: u32Bits(0b00000100000000000000000000000000), expected: u32(26) }, + { input: u32Bits(0b00001000000000000000000000000000), expected: u32(27) }, + { input: u32Bits(0b00010000000000000000000000000000), expected: u32(28) }, + { input: u32Bits(0b00100000000000000000000000000000), expected: u32(29) }, + { input: u32Bits(0b01000000000000000000000000000000), expected: u32(30) }, + + // 1's before trailing 1 + { input: u32Bits(0b11111111111111111111111111111111), expected: u32(0) }, + { input: u32Bits(0b11111111111111111111111111111110), expected: u32(1) }, + { input: u32Bits(0b11111111111111111111111111111100), expected: u32(2) }, + { input: u32Bits(0b11111111111111111111111111111000), expected: u32(3) }, + { input: u32Bits(0b11111111111111111111111111110000), expected: u32(4) }, + { input: u32Bits(0b11111111111111111111111111100000), expected: u32(5) }, + { input: u32Bits(0b11111111111111111111111111000000), expected: u32(6) }, + { input: u32Bits(0b11111111111111111111111110000000), expected: u32(7) }, + { input: u32Bits(0b11111111111111111111111100000000), expected: u32(8) }, + { input: u32Bits(0b11111111111111111111111000000000), expected: u32(9) }, + { input: u32Bits(0b11111111111111111111110000000000), expected: u32(10) }, + { input: u32Bits(0b11111111111111111111100000000000), expected: u32(11) }, + { input: u32Bits(0b11111111111111111111000000000000), expected: u32(12) }, + { input: u32Bits(0b11111111111111111110000000000000), expected: u32(13) }, + { input: u32Bits(0b11111111111111111100000000000000), expected: u32(14) }, + { input: u32Bits(0b11111111111111111000000000000000), expected: u32(15) }, + { input: u32Bits(0b11111111111111110000000000000000), expected: u32(16) }, + { input: u32Bits(0b11111111111111100000000000000000), expected: u32(17) }, + { input: u32Bits(0b11111111111111000000000000000000), expected: u32(18) }, + { input: u32Bits(0b11111111111110000000000000000000), expected: u32(19) }, + { input: u32Bits(0b11111111111100000000000000000000), expected: u32(20) }, + { input: u32Bits(0b11111111111000000000000000000000), expected: u32(21) }, + { input: u32Bits(0b11111111110000000000000000000000), expected: u32(22) }, + { input: u32Bits(0b11111111100000000000000000000000), expected: u32(23) }, + { input: u32Bits(0b11111111000000000000000000000000), expected: u32(24) }, + { input: u32Bits(0b11111110000000000000000000000000), expected: u32(25) }, + { input: u32Bits(0b11111100000000000000000000000000), expected: u32(26) }, + { input: u32Bits(0b11111000000000000000000000000000), expected: u32(27) }, + { input: u32Bits(0b11110000000000000000000000000000), expected: u32(28) }, + { input: u32Bits(0b11100000000000000000000000000000), expected: u32(29) }, + { input: u32Bits(0b11000000000000000000000000000000), expected: u32(30) }, + + // random before trailing 1 + { input: u32Bits(0b11110000001111111101111010001111), expected: u32(0) }, + { input: u32Bits(0b11011110111111100101110011110010), expected: u32(1) }, + { input: u32Bits(0b11110111011011111111010000111100), expected: u32(2) }, + { input: u32Bits(0b11010011011101111111010011101000), expected: u32(3) }, + { input: u32Bits(0b11010111110111110001111110110000), expected: u32(4) }, + { input: u32Bits(0b11111101111101111110101111100000), expected: u32(5) }, + { input: u32Bits(0b11111001111011111001111011000000), expected: u32(6) }, + { input: u32Bits(0b11001110110111110111111010000000), expected: u32(7) }, + { input: u32Bits(0b11101111011111101110101100000000), expected: u32(8) }, + { input: u32Bits(0b11111101111011111111111000000000), expected: u32(9) }, + { input: u32Bits(0b10011111011101110110110000000000), expected: u32(10) }, + { input: u32Bits(0b11111111101101111011100000000000), expected: u32(11) }, + { input: u32Bits(0b11111011010110111011000000000000), expected: u32(12) }, + { input: u32Bits(0b00111101010000111010000000000000), expected: u32(13) }, + { input: u32Bits(0b11111011110001101100000000000000), expected: u32(14) }, + { input: u32Bits(0b10111111010111111000000000000000), expected: u32(15) }, + { input: u32Bits(0b11011101111010110000000000000000), expected: u32(16) }, + { input: u32Bits(0b01110100110110100000000000000000), expected: u32(17) }, + { input: u32Bits(0b11100111001011000000000000000000), expected: u32(18) }, + { input: u32Bits(0b11111001110110000000000000000000), expected: u32(19) }, + { input: u32Bits(0b00110100100100000000000000000000), expected: u32(20) }, + { input: u32Bits(0b11111010011000000000000000000000), expected: u32(21) }, + { input: u32Bits(0b00000010110000000000000000000000), expected: u32(22) }, + { input: u32Bits(0b11100111100000000000000000000000), expected: u32(23) }, + { input: u32Bits(0b00101101000000000000000000000000), expected: u32(24) }, + { input: u32Bits(0b11011010000000000000000000000000), expected: u32(25) }, + { input: u32Bits(0b11010100000000000000000000000000), expected: u32(26) }, + { input: u32Bits(0b10111000000000000000000000000000), expected: u32(27) }, + { input: u32Bits(0b01110000000000000000000000000000), expected: u32(28) }, + { input: u32Bits(0b10100000000000000000000000000000), expected: u32(29) }, + ]); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('countTrailingZeros'), [TypeI32], TypeI32, cfg, [ + // Zero + { input: i32Bits(0b00000000000000000000000000000000), expected: i32(32) }, + + // High bit + { input: i32Bits(0b10000000000000000000000000000000), expected: i32(31) }, + + // 0's before trailing 1 + { input: i32Bits(0b00000000000000000000000000000001), expected: i32(0) }, + { input: i32Bits(0b00000000000000000000000000000010), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000000000100), expected: i32(2) }, + { input: i32Bits(0b00000000000000000000000000001000), expected: i32(3) }, + { input: i32Bits(0b00000000000000000000000000010000), expected: i32(4) }, + { input: i32Bits(0b00000000000000000000000000100000), expected: i32(5) }, + { input: i32Bits(0b00000000000000000000000001000000), expected: i32(6) }, + { input: i32Bits(0b00000000000000000000000010000000), expected: i32(7) }, + { input: i32Bits(0b00000000000000000000000100000000), expected: i32(8) }, + { input: i32Bits(0b00000000000000000000001000000000), expected: i32(9) }, + { input: i32Bits(0b00000000000000000000010000000000), expected: i32(10) }, + { input: i32Bits(0b00000000000000000000100000000000), expected: i32(11) }, + { input: i32Bits(0b00000000000000000001000000000000), expected: i32(12) }, + { input: i32Bits(0b00000000000000000010000000000000), expected: i32(13) }, + { input: i32Bits(0b00000000000000000100000000000000), expected: i32(14) }, + { input: i32Bits(0b00000000000000001000000000000000), expected: i32(15) }, + { input: i32Bits(0b00000000000000010000000000000000), expected: i32(16) }, + { input: i32Bits(0b00000000000000100000000000000000), expected: i32(17) }, + { input: i32Bits(0b00000000000001000000000000000000), expected: i32(18) }, + { input: i32Bits(0b00000000000010000000000000000000), expected: i32(19) }, + { input: i32Bits(0b00000000000100000000000000000000), expected: i32(20) }, + { input: i32Bits(0b00000000001000000000000000000000), expected: i32(21) }, + { input: i32Bits(0b00000000010000000000000000000000), expected: i32(22) }, + { input: i32Bits(0b00000000100000000000000000000000), expected: i32(23) }, + { input: i32Bits(0b00000001000000000000000000000000), expected: i32(24) }, + { input: i32Bits(0b00000010000000000000000000000000), expected: i32(25) }, + { input: i32Bits(0b00000100000000000000000000000000), expected: i32(26) }, + { input: i32Bits(0b00001000000000000000000000000000), expected: i32(27) }, + { input: i32Bits(0b00010000000000000000000000000000), expected: i32(28) }, + { input: i32Bits(0b00100000000000000000000000000000), expected: i32(29) }, + { input: i32Bits(0b01000000000000000000000000000000), expected: i32(30) }, + + // 1's before trailing 1 + { input: i32Bits(0b11111111111111111111111111111111), expected: i32(0) }, + { input: i32Bits(0b11111111111111111111111111111110), expected: i32(1) }, + { input: i32Bits(0b11111111111111111111111111111100), expected: i32(2) }, + { input: i32Bits(0b11111111111111111111111111111000), expected: i32(3) }, + { input: i32Bits(0b11111111111111111111111111110000), expected: i32(4) }, + { input: i32Bits(0b11111111111111111111111111100000), expected: i32(5) }, + { input: i32Bits(0b11111111111111111111111111000000), expected: i32(6) }, + { input: i32Bits(0b11111111111111111111111110000000), expected: i32(7) }, + { input: i32Bits(0b11111111111111111111111100000000), expected: i32(8) }, + { input: i32Bits(0b11111111111111111111111000000000), expected: i32(9) }, + { input: i32Bits(0b11111111111111111111110000000000), expected: i32(10) }, + { input: i32Bits(0b11111111111111111111100000000000), expected: i32(11) }, + { input: i32Bits(0b11111111111111111111000000000000), expected: i32(12) }, + { input: i32Bits(0b11111111111111111110000000000000), expected: i32(13) }, + { input: i32Bits(0b11111111111111111100000000000000), expected: i32(14) }, + { input: i32Bits(0b11111111111111111000000000000000), expected: i32(15) }, + { input: i32Bits(0b11111111111111110000000000000000), expected: i32(16) }, + { input: i32Bits(0b11111111111111100000000000000000), expected: i32(17) }, + { input: i32Bits(0b11111111111111000000000000000000), expected: i32(18) }, + { input: i32Bits(0b11111111111110000000000000000000), expected: i32(19) }, + { input: i32Bits(0b11111111111100000000000000000000), expected: i32(20) }, + { input: i32Bits(0b11111111111000000000000000000000), expected: i32(21) }, + { input: i32Bits(0b11111111110000000000000000000000), expected: i32(22) }, + { input: i32Bits(0b11111111100000000000000000000000), expected: i32(23) }, + { input: i32Bits(0b11111111000000000000000000000000), expected: i32(24) }, + { input: i32Bits(0b11111110000000000000000000000000), expected: i32(25) }, + { input: i32Bits(0b11111100000000000000000000000000), expected: i32(26) }, + { input: i32Bits(0b11111000000000000000000000000000), expected: i32(27) }, + { input: i32Bits(0b11110000000000000000000000000000), expected: i32(28) }, + { input: i32Bits(0b11100000000000000000000000000000), expected: i32(29) }, + { input: i32Bits(0b11000000000000000000000000000000), expected: i32(30) }, + + // random before trailing 1 + { input: i32Bits(0b11110000001111111101111010001111), expected: i32(0) }, + { input: i32Bits(0b11011110111111100101110011110010), expected: i32(1) }, + { input: i32Bits(0b11110111011011111111010000111100), expected: i32(2) }, + { input: i32Bits(0b11010011011101111111010011101000), expected: i32(3) }, + { input: i32Bits(0b11010111110111110001111110110000), expected: i32(4) }, + { input: i32Bits(0b11111101111101111110101111100000), expected: i32(5) }, + { input: i32Bits(0b11111001111011111001111011000000), expected: i32(6) }, + { input: i32Bits(0b11001110110111110111111010000000), expected: i32(7) }, + { input: i32Bits(0b11101111011111101110101100000000), expected: i32(8) }, + { input: i32Bits(0b11111101111011111111111000000000), expected: i32(9) }, + { input: i32Bits(0b10011111011101110110110000000000), expected: i32(10) }, + { input: i32Bits(0b11111111101101111011100000000000), expected: i32(11) }, + { input: i32Bits(0b11111011010110111011000000000000), expected: i32(12) }, + { input: i32Bits(0b00111101010000111010000000000000), expected: i32(13) }, + { input: i32Bits(0b11111011110001101100000000000000), expected: i32(14) }, + { input: i32Bits(0b10111111010111111000000000000000), expected: i32(15) }, + { input: i32Bits(0b11011101111010110000000000000000), expected: i32(16) }, + { input: i32Bits(0b01110100110110100000000000000000), expected: i32(17) }, + { input: i32Bits(0b11100111001011000000000000000000), expected: i32(18) }, + { input: i32Bits(0b11111001110110000000000000000000), expected: i32(19) }, + { input: i32Bits(0b00110100100100000000000000000000), expected: i32(20) }, + { input: i32Bits(0b11111010011000000000000000000000), expected: i32(21) }, + { input: i32Bits(0b00000010110000000000000000000000), expected: i32(22) }, + { input: i32Bits(0b11100111100000000000000000000000), expected: i32(23) }, + { input: i32Bits(0b00101101000000000000000000000000), expected: i32(24) }, + { input: i32Bits(0b11011010000000000000000000000000), expected: i32(25) }, + { input: i32Bits(0b11010100000000000000000000000000), expected: i32(26) }, + { input: i32Bits(0b10111000000000000000000000000000), expected: i32(27) }, + { input: i32Bits(0b01110000000000000000000000000000), expected: i32(28) }, + { input: i32Bits(0b10100000000000000000000000000000), expected: i32(29) }, + ]); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts new file mode 100644 index 0000000000..5bce44049e --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts @@ -0,0 +1,66 @@ +export const description = ` +Execution tests for the 'cross' builtin function + +T is AbstractFloat, f32, or f16 +@const fn cross(e1: vec3<T> ,e2: vec3<T>) -> vec3<T> +Returns the cross product of e1 and e2. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { crossInterval } from '../../../../../util/f32_interval.js'; +import { vectorF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateVectorPairToVectorCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('cross', { + f32_const: () => { + return generateVectorPairToVectorCases( + vectorF32Range(3), + vectorF32Range(3), + 'f32-only', + crossInterval + ); + }, + f32_non_const: () => { + return generateVectorPairToVectorCases( + vectorF32Range(3), + vectorF32Range(3), + 'unfiltered', + crossInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run( + t, + builtin('cross'), + [TypeVec(3, TypeF32), TypeVec(3, TypeF32)], + TypeVec(3, TypeF32), + t.params, + cases + ); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts new file mode 100644 index 0000000000..9b4346408b --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts @@ -0,0 +1,56 @@ +export const description = ` +Execution tests for the 'degrees' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<T> +@const fn degrees(e1: T ) -> T +Converts radians to degrees, approximating e1 × 180 ÷ π. Component-wise when T is a vector +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { degreesInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('degrees', { + f32_const: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'f32-only', degreesInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', degreesInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('degrees'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts new file mode 100644 index 0000000000..6ea24ceb6f --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts @@ -0,0 +1,32 @@ +export const description = ` +Execution tests for the 'determinant' builtin function + +T is AbstractFloat, f32, or f16 +@const determinant(e: matCxC<T> ) -> T +Returns the determinant of e. + +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions') + .desc(`abstract float tests`) + .params(u => u.combine('inputSource', allInputSources).combine('dimension', [2, 3, 4] as const)) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions') + .desc(`f32 tests`) + .params(u => u.combine('inputSource', allInputSources).combine('dimension', [2, 3, 4] as const)) + .unimplemented(); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions') + .desc(`f16 tests`) + .params(u => u.combine('inputSource', allInputSources).combine('dimension', [2, 3, 4] as const)) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts new file mode 100644 index 0000000000..f6c2239a02 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts @@ -0,0 +1,172 @@ +export const description = ` +Execution tests for the 'distance' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn distance(e1: T ,e2: T ) -> f32 +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, TypeVec } from '../../../../../util/conversion.js'; +import { distanceInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range, sparseVectorF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { + allInputSources, + generateBinaryToF32IntervalCases, + generateVectorPairToF32IntervalCases, + run, +} from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('distance', { + f32_const: () => { + return generateBinaryToF32IntervalCases( + fullF32Range(), + fullF32Range(), + 'f32-only', + distanceInterval + ); + }, + f32_non_const: () => { + return generateBinaryToF32IntervalCases( + fullF32Range(), + fullF32Range(), + 'unfiltered', + distanceInterval + ); + }, + f32_vec2_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(2), + sparseVectorF32Range(2), + 'f32-only', + distanceInterval + ); + }, + f32_vec2_non_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(2), + sparseVectorF32Range(2), + 'unfiltered', + distanceInterval + ); + }, + f32_vec3_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + 'f32-only', + distanceInterval + ); + }, + f32_vec3_non_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + 'unfiltered', + distanceInterval + ); + }, + f32_vec4_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + 'f32-only', + distanceInterval + ); + }, + f32_vec4_non_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + 'unfiltered', + distanceInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('distance'), [TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('f32_vec2') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec2s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' + ); + await run( + t, + builtin('distance'), + [TypeVec(2, TypeF32), TypeVec(2, TypeF32)], + TypeF32, + t.params, + cases + ); + }); + +g.test('f32_vec3') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec3s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' + ); + await run( + t, + builtin('distance'), + [TypeVec(3, TypeF32), TypeVec(3, TypeF32)], + TypeF32, + t.params, + cases + ); + }); + +g.test('f32_vec4') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec4s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' + ); + await run( + t, + builtin('distance'), + [TypeVec(4, TypeF32), TypeVec(4, TypeF32)], + TypeF32, + t.params, + cases + ); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts new file mode 100644 index 0000000000..e76b4c56a9 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts @@ -0,0 +1,156 @@ +export const description = ` +Execution tests for the 'dot' builtin function + +T is AbstractInt, AbstractFloat, i32, u32, f32, or f16 +@const fn dot(e1: vecN<T>,e2: vecN<T>) -> T +Returns the dot product of e1 and e2. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { dotInterval } from '../../../../../util/f32_interval.js'; +import { sparseVectorF32Range, vectorF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateVectorPairToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +// vec3 and vec4 require calculating all possible permutations, so their runtime is much longer per test, so only using +// sparse vectors for them +export const d = makeCaseCache('dot', { + f32_vec2_const: () => { + return generateVectorPairToF32IntervalCases( + vectorF32Range(2), + vectorF32Range(2), + 'f32-only', + dotInterval + ); + }, + f32_vec2_non_const: () => { + return generateVectorPairToF32IntervalCases( + vectorF32Range(2), + vectorF32Range(2), + 'unfiltered', + dotInterval + ); + }, + f32_vec3_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + 'f32-only', + dotInterval + ); + }, + f32_vec3_non_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + 'unfiltered', + dotInterval + ); + }, + f32_vec4_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + 'f32-only', + dotInterval + ); + }, + f32_vec4_non_const: () => { + return generateVectorPairToF32IntervalCases( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + 'unfiltered', + dotInterval + ); + }, +}); + +g.test('abstract_int') + .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions') + .desc(`abstract int tests`) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions') + .desc(`i32 tests`) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions') + .desc(`u32 tests`) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions') + .desc(`abstract float test`) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('f32_vec2') + .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions') + .desc(`f32 tests using vec2s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' + ); + await run( + t, + builtin('dot'), + [TypeVec(2, TypeF32), TypeVec(2, TypeF32)], + TypeF32, + t.params, + cases + ); + }); + +g.test('f32_vec3') + .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions') + .desc(`f32 tests using vec3s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' + ); + await run( + t, + builtin('dot'), + [TypeVec(3, TypeF32), TypeVec(3, TypeF32)], + TypeF32, + t.params, + cases + ); + }); + +g.test('f32_vec4') + .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions') + .desc(`f32 tests using vec4s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' + ); + await run( + t, + builtin('dot'), + [TypeVec(4, TypeF32), TypeVec(4, TypeF32)], + TypeF32, + t.params, + cases + ); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#vector-builtin-functions') + .desc(`f16 tests`) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts new file mode 100644 index 0000000000..287a51c699 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdx.spec.ts @@ -0,0 +1,23 @@ +export const description = ` +Execution tests for the 'dpdx' builtin function + +T is f32 or vecN<f32> +fn dpdx(e:T) -> T +Partial derivative of e with respect to window x coordinates. +The result is the same as either dpdxFine(e) or dpdxCoarse(e). + +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts new file mode 100644 index 0000000000..67a75bb010 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxCoarse.spec.ts @@ -0,0 +1,22 @@ +export const description = ` +Execution tests for the 'dpdxCoarse' builtin function + +T is f32 or vecN<f32> +fn dpdxCoarse(e:T) ->T +Returns the partial derivative of e with respect to window x coordinates using local differences. +This may result in fewer unique positions that dpdxFine(e). +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts new file mode 100644 index 0000000000..91d65b990b --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdxFine.spec.ts @@ -0,0 +1,21 @@ +export const description = ` +Execution tests for the 'dpdxFine' builtin function + +T is f32 or vecN<f32> +fn dpdxFine(e:T) ->T +Returns the partial derivative of e with respect to window x coordinates. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts new file mode 100644 index 0000000000..0cd9cafdb9 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdy.spec.ts @@ -0,0 +1,22 @@ +export const description = ` +Execution tests for the 'dpdy' builtin function + +T is f32 or vecN<f32> +fn dpdy(e:T) ->T +Partial derivative of e with respect to window y coordinates. +The result is the same as either dpdyFine(e) or dpdyCoarse(e). +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts new file mode 100644 index 0000000000..f06869fdc2 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyCoarse.spec.ts @@ -0,0 +1,22 @@ +export const description = ` +Execution tests for the 'dpdyCoarse' builtin function + +T is f32 or vecN<f32> +fn dpdyCoarse(e:T) ->T +Returns the partial derivative of e with respect to window y coordinates using local differences. +This may result in fewer unique positions that dpdyFine(e). +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions') + .desc(`f32 test`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts new file mode 100644 index 0000000000..e09761de95 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/dpdyFine.spec.ts @@ -0,0 +1,21 @@ +export const description = ` +Execution tests for the 'dpdyFine' builtin function + +T is f32 or vecN<f32> +fn dpdyFine(e:T) ->T +Returns the partial derivative of e with respect to window y coordinates. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts new file mode 100644 index 0000000000..1f43ac9d53 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts @@ -0,0 +1,68 @@ +export const description = ` +Execution tests for the 'exp' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn exp(e1: T ) -> T +Returns the natural exponentiation of e1 (e.g. e^e1). 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 { expInterval } from '../../../../../util/f32_interval.js'; +import { biasedRange, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.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 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 +]; + +export const d = makeCaseCache('exp', { + f32_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'f32-only', expInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'unfiltered', expInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('exp'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts new file mode 100644 index 0000000000..e0321387c1 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts @@ -0,0 +1,68 @@ +export const description = ` +Execution tests for the 'exp2' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn exp2(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { exp2Interval } from '../../../../../util/f32_interval.js'; +import { biasedRange, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.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 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 +]; + +export const d = makeCaseCache('exp2', { + f32_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'f32-only', exp2Interval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'unfiltered', exp2Interval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('exp2'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts new file mode 100644 index 0000000000..d535bf5d74 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts @@ -0,0 +1,337 @@ +export const description = ` +Execution tests for the 'extractBits' builtin function + +T is u32 or vecN<u32> +@const fn extractBits(e: T, offset: u32, count: u32) -> T +Reads bits from an integer, without sign extension. + +When T is a scalar type, then: + w is the bit width of T + o = min(offset,w) + c = min(count, w - o) + +The result is 0 if c is 0. +Otherwise, bits 0..c-1 of the result are copied from bits o..o+c-1 of e. +Other bits of the result are 0. +Component-wise when T is a vector. + + +T is i32 or vecN<i32> +@const fn extractBits(e: T, offset: u32, count: u32) -> T +Reads bits from an integer, with sign extension. + +When T is a scalar type, then: + w is the bit width of T + o = min(offset,w) + c = min(count, w - o) + +The result is 0 if c is 0. +Otherwise, bits 0..c-1 of the result are copied from bits o..o+c-1 of e. +Other bits of the result are the same as bit c-1 of the result. +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { + i32Bits, + TypeI32, + u32, + TypeU32, + u32Bits, + vec2, + vec3, + vec4, + TypeVec, +} from '../../../../../util/conversion.js'; +import { allInputSources, Config, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => u.combine('inputSource', allInputSources).combine('width', [1, 2, 3, 4])) + .fn(async t => { + const cfg: Config = t.params; + + const T = t.params.width === 1 ? TypeU32 : TypeVec(t.params.width, TypeU32); + + const V = (x: number, y?: number, z?: number, w?: number) => { + y = y === undefined ? x : y; + z = z === undefined ? x : z; + w = w === undefined ? x : w; + + switch (t.params.width) { + case 1: + return u32Bits(x); + case 2: + return vec2(u32Bits(x), u32Bits(y)); + case 3: + return vec3(u32Bits(x), u32Bits(y), u32Bits(z)); + default: + return vec4(u32Bits(x), u32Bits(y), u32Bits(z), u32Bits(w)); + } + }; + + const all_1 = V(0b11111111111111111111111111111111); + const all_0 = V(0b00000000000000000000000000000000); + const low_1 = V(0b00000000000000000000000000000001); + const high_1 = V(0b10000000000000000000000000000000); + const pattern = V( + 0b00000000000111011100000000000000, + 0b11111111111000000011111111111111, + 0b00000000010101010101000000000000, + 0b00000000001010101010100000000000 + ); + + const cases = [ + { input: [all_0, u32(0), u32(32)], expected: all_0 }, + { input: [all_0, u32(1), u32(10)], expected: all_0 }, + { input: [all_0, u32(2), u32(5)], expected: all_0 }, + { input: [all_0, u32(0), u32(1)], expected: all_0 }, + { input: [all_0, u32(31), u32(1)], expected: all_0 }, + + { input: [all_1, u32(0), u32(32)], expected: all_1 }, + { + input: [all_1, u32(1), u32(10)], + expected: V(0b00000000000000000000001111111111), + }, + { + input: [all_1, u32(2), u32(5)], + expected: V(0b00000000000000000000000000011111), + }, + { input: [all_1, u32(0), u32(1)], expected: low_1 }, + { input: [all_1, u32(31), u32(1)], expected: low_1 }, + + // Patterns + { input: [pattern, u32(0), u32(32)], expected: pattern }, + { + input: [pattern, u32(1), u32(31)], + expected: V( + 0b00000000000011101110000000000000, + 0b01111111111100000001111111111111, + 0b00000000001010101010100000000000, + 0b00000000000101010101010000000000 + ), + }, + { + input: [pattern, u32(14), u32(18)], + expected: V( + 0b00000000000000000000000001110111, + 0b00000000000000111111111110000000, + 0b00000000000000000000000101010101, + 0b00000000000000000000000010101010 + ), + }, + { + input: [pattern, u32(14), u32(7)], + expected: V( + 0b00000000000000000000000001110111, + 0b00000000000000000000000000000000, + 0b00000000000000000000000001010101, + 0b00000000000000000000000000101010 + ), + }, + { + input: [pattern, u32(14), u32(4)], + expected: V( + 0b00000000000000000000000000000111, + 0b00000000000000000000000000000000, + 0b00000000000000000000000000000101, + 0b00000000000000000000000000001010 + ), + }, + { + input: [pattern, u32(14), u32(3)], + expected: V( + 0b00000000000000000000000000000111, + 0b00000000000000000000000000000000, + 0b00000000000000000000000000000101, + 0b00000000000000000000000000000010 + ), + }, + { + input: [pattern, u32(18), u32(3)], + expected: V( + 0b00000000000000000000000000000111, + 0b00000000000000000000000000000000, + 0b00000000000000000000000000000101, + 0b00000000000000000000000000000010 + ), + }, + { input: [low_1, u32(0), u32(1)], expected: low_1 }, + { input: [high_1, u32(31), u32(1)], expected: low_1 }, + + // Zero count + { input: [all_1, u32(0), u32(0)], expected: all_0 }, + { input: [all_0, u32(0), u32(0)], expected: all_0 }, + { input: [low_1, u32(0), u32(0)], expected: all_0 }, + { input: [high_1, u32(31), u32(0)], expected: all_0 }, + { input: [pattern, u32(0), u32(0)], expected: all_0 }, + ]; + + if (t.params.inputSource !== 'const') { + cases.push( + ...[ + // End overflow + { input: [low_1, u32(0), u32(99)], expected: low_1 }, + { input: [high_1, u32(31), u32(99)], expected: low_1 }, + { input: [pattern, u32(0), u32(99)], expected: pattern }, + { + input: [pattern, u32(14), u32(99)], + expected: V( + 0b00000000000000000000000001110111, + 0b00000000000000111111111110000000, + 0b00000000000000000000000101010101, + 0b00000000000000000000000010101010 + ), + }, + ] + ); + } + + await run(t, builtin('extractBits'), [T, TypeU32, TypeU32], T, cfg, cases); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => u.combine('inputSource', allInputSources).combine('width', [1, 2, 3, 4])) + .fn(async t => { + const cfg: Config = t.params; + + const T = t.params.width === 1 ? TypeI32 : TypeVec(t.params.width, TypeI32); + + const V = (x: number, y?: number, z?: number, w?: number) => { + y = y === undefined ? x : y; + z = z === undefined ? x : z; + w = w === undefined ? x : w; + + switch (t.params.width) { + case 1: + return i32Bits(x); + case 2: + return vec2(i32Bits(x), i32Bits(y)); + case 3: + return vec3(i32Bits(x), i32Bits(y), i32Bits(z)); + default: + return vec4(i32Bits(x), i32Bits(y), i32Bits(z), i32Bits(w)); + } + }; + + const all_1 = V(0b11111111111111111111111111111111); + const all_0 = V(0b00000000000000000000000000000000); + const low_1 = V(0b00000000000000000000000000000001); + const high_1 = V(0b10000000000000000000000000000000); + const pattern = V( + 0b00000000000111011100000000000000, + 0b11111111111000000011111111111111, + 0b00000000010101010101000000000000, + 0b00000000001010101010100000000000 + ); + + const cases = [ + { input: [all_0, u32(0), u32(32)], expected: all_0 }, + { input: [all_0, u32(1), u32(10)], expected: all_0 }, + { input: [all_0, u32(2), u32(5)], expected: all_0 }, + { input: [all_0, u32(0), u32(1)], expected: all_0 }, + { input: [all_0, u32(31), u32(1)], expected: all_0 }, + + { input: [all_1, u32(0), u32(32)], expected: all_1 }, + { input: [all_1, u32(1), u32(10)], expected: all_1 }, + { input: [all_1, u32(2), u32(5)], expected: all_1 }, + { input: [all_1, u32(0), u32(1)], expected: all_1 }, + { input: [all_1, u32(31), u32(1)], expected: all_1 }, + + // Patterns + { input: [pattern, u32(0), u32(32)], expected: pattern }, + { + input: [pattern, u32(1), u32(31)], + expected: V( + 0b00000000000011101110000000000000, + 0b11111111111100000001111111111111, + 0b00000000001010101010100000000000, + 0b00000000000101010101010000000000 + ), + }, + { + input: [pattern, u32(14), u32(18)], + expected: V( + 0b00000000000000000000000001110111, + 0b11111111111111111111111110000000, + 0b00000000000000000000000101010101, + 0b00000000000000000000000010101010 + ), + }, + { + input: [pattern, u32(14), u32(7)], + expected: V( + 0b11111111111111111111111111110111, + 0b00000000000000000000000000000000, + 0b11111111111111111111111111010101, + 0b00000000000000000000000000101010 + ), + }, + { + input: [pattern, u32(14), u32(4)], + expected: V( + 0b00000000000000000000000000000111, + 0b00000000000000000000000000000000, + 0b00000000000000000000000000000101, + 0b11111111111111111111111111111010 + ), + }, + { + input: [pattern, u32(14), u32(3)], + expected: V( + 0b11111111111111111111111111111111, + 0b00000000000000000000000000000000, + 0b11111111111111111111111111111101, + 0b00000000000000000000000000000010 + ), + }, + { + input: [pattern, u32(18), u32(3)], + expected: V( + 0b11111111111111111111111111111111, + 0b00000000000000000000000000000000, + 0b11111111111111111111111111111101, + 0b00000000000000000000000000000010 + ), + }, + { input: [low_1, u32(0), u32(1)], expected: all_1 }, + { input: [high_1, u32(31), u32(1)], expected: all_1 }, + + // Zero count + { input: [all_1, u32(0), u32(0)], expected: all_0 }, + { input: [all_0, u32(0), u32(0)], expected: all_0 }, + { input: [low_1, u32(0), u32(0)], expected: all_0 }, + { input: [high_1, u32(31), u32(0)], expected: all_0 }, + { input: [pattern, u32(0), u32(0)], expected: all_0 }, + ]; + + if (t.params.inputSource !== 'const') { + cases.push( + ...[ + // End overflow + { input: [low_1, u32(0), u32(99)], expected: low_1 }, + { input: [high_1, u32(31), u32(99)], expected: all_1 }, + { input: [pattern, u32(0), u32(99)], expected: pattern }, + { + input: [pattern, u32(14), u32(99)], + expected: V( + 0b00000000000000000000000001110111, + 0b11111111111111111111111110000000, + 0b00000000000000000000000101010101, + 0b00000000000000000000000010101010 + ), + }, + ] + ); + } + + await run(t, builtin('extractBits'), [T, TypeU32, TypeU32], T, cfg, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts new file mode 100644 index 0000000000..22897f449a --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts @@ -0,0 +1,201 @@ +export const description = ` +Execution tests for the 'faceForward' builtin function + +T is vecN<AbstractFloat>, vecN<f32>, or vecN<f16> +@const fn faceForward(e1: T ,e2: T ,e3: T ) -> T +Returns e1 if dot(e2,e3) is negative, and -e1 otherwise. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { anyOf } from '../../../../../util/compare.js'; +import { f32, TypeF32, TypeVec, Vector } from '../../../../../util/conversion.js'; +import { F32Vector, faceForwardIntervals } from '../../../../../util/f32_interval.js'; +import { cartesianProduct, quantizeToF32, sparseVectorF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, Case, IntervalFilter, run } from '../../expression.js'; + +import { builtin } from './builtin.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 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 makeCaseF32( + x: number[], + y: number[], + z: number[], + check: IntervalFilter +): Case | undefined { + x = x.map(quantizeToF32); + y = y.map(quantizeToF32); + z = z.map(quantizeToF32); + + const x_f32 = x.map(f32); + const y_f32 = y.map(f32); + const z_f32 = z.map(f32); + + const results = faceForwardIntervals(x, y, z); + if (check === 'f32-only' && 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 F32Vector => r !== undefined); + + return { + input: [new Vector(x_f32), new Vector(y_f32), new Vector(z_f32)], + expected: anyOf(...define_results), + }; +} + +/** + * @returns an array of Cases for `faceForward` + * @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 generateCasesF32( + xs: number[][], + ys: number[][], + zs: number[][], + check: IntervalFilter +): Case[] { + // Cannot use `cartesianProduct` here due to heterogeneous param types + return cartesianProduct(xs, ys, zs) + .map(e => makeCaseF32(e[0], e[1], e[2], check)) + .filter((c): c is Case => c !== undefined); +} + +export const d = makeCaseCache('faceForward', { + f32_vec2_const: () => { + return generateCasesF32( + sparseVectorF32Range(2), + sparseVectorF32Range(2), + sparseVectorF32Range(2), + 'f32-only' + ); + }, + f32_vec2_non_const: () => { + return generateCasesF32( + sparseVectorF32Range(2), + sparseVectorF32Range(2), + sparseVectorF32Range(2), + 'unfiltered' + ); + }, + f32_vec3_const: () => { + return generateCasesF32( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + sparseVectorF32Range(3), + 'f32-only' + ); + }, + f32_vec3_non_const: () => { + return generateCasesF32( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + sparseVectorF32Range(3), + 'unfiltered' + ); + }, + f32_vec4_const: () => { + return generateCasesF32( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + sparseVectorF32Range(4), + 'f32-only' + ); + }, + f32_vec4_non_const: () => { + return generateCasesF32( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + sparseVectorF32Range(4), + 'unfiltered' + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); + +g.test('f32_vec2') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec2s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' + ); + await run( + t, + builtin('faceForward'), + [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeVec(2, TypeF32)], + TypeVec(2, TypeF32), + t.params, + cases + ); + }); + +g.test('f32_vec3') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec3s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' + ); + await run( + t, + builtin('faceForward'), + [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeVec(3, TypeF32)], + TypeVec(3, TypeF32), + t.params, + cases + ); + }); + +g.test('f32_vec4') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec4s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' + ); + await run( + t, + builtin('faceForward'), + [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeVec(4, TypeF32)], + TypeVec(4, TypeF32), + t.params, + cases + ); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts new file mode 100644 index 0000000000..a04103304a --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts @@ -0,0 +1,347 @@ +export const description = ` +Execution tests for the 'firstLeadingBit' builtin function + +T is u32 or vecN<u32> +@const fn firstLeadingBit(e: T ) -> T +For scalar T, the result is: T(-1) if e is zero. +Otherwise the position of the most significant 1 bit in e. +Component-wise when T is a vector. + +T is i32 or vecN<i32> +@const fn firstLeadingBit(e: T ) -> T +For scalar T, the result is: -1 if e is 0 or -1. +Otherwise the position of the most significant bit in e that is different from e’s sign bit. +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js'; +import { allInputSources, Config, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('firstLeadingBit'), [TypeU32], TypeU32, cfg, [ + // Zero + { input: u32Bits(0b00000000000000000000000000000000), expected: u32(-1) }, + + // One + { input: u32Bits(0b00000000000000000000000000000001), expected: u32(0) }, + + // 0's after leading 1 + { input: u32Bits(0b00000000000000000000000000000010), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000000000100), expected: u32(2) }, + { input: u32Bits(0b00000000000000000000000000001000), expected: u32(3) }, + { input: u32Bits(0b00000000000000000000000000010000), expected: u32(4) }, + { input: u32Bits(0b00000000000000000000000000100000), expected: u32(5) }, + { input: u32Bits(0b00000000000000000000000001000000), expected: u32(6) }, + { input: u32Bits(0b00000000000000000000000010000000), expected: u32(7) }, + { input: u32Bits(0b00000000000000000000000100000000), expected: u32(8) }, + { input: u32Bits(0b00000000000000000000001000000000), expected: u32(9) }, + { input: u32Bits(0b00000000000000000000010000000000), expected: u32(10) }, + { input: u32Bits(0b00000000000000000000100000000000), expected: u32(11) }, + { input: u32Bits(0b00000000000000000001000000000000), expected: u32(12) }, + { input: u32Bits(0b00000000000000000010000000000000), expected: u32(13) }, + { input: u32Bits(0b00000000000000000100000000000000), expected: u32(14) }, + { input: u32Bits(0b00000000000000001000000000000000), expected: u32(15) }, + { input: u32Bits(0b00000000000000010000000000000000), expected: u32(16) }, + { input: u32Bits(0b00000000000000100000000000000000), expected: u32(17) }, + { input: u32Bits(0b00000000000001000000000000000000), expected: u32(18) }, + { input: u32Bits(0b00000000000010000000000000000000), expected: u32(19) }, + { input: u32Bits(0b00000000000100000000000000000000), expected: u32(20) }, + { input: u32Bits(0b00000000001000000000000000000000), expected: u32(21) }, + { input: u32Bits(0b00000000010000000000000000000000), expected: u32(22) }, + { input: u32Bits(0b00000000100000000000000000000000), expected: u32(23) }, + { input: u32Bits(0b00000001000000000000000000000000), expected: u32(24) }, + { input: u32Bits(0b00000010000000000000000000000000), expected: u32(25) }, + { input: u32Bits(0b00000100000000000000000000000000), expected: u32(26) }, + { input: u32Bits(0b00001000000000000000000000000000), expected: u32(27) }, + { input: u32Bits(0b00010000000000000000000000000000), expected: u32(28) }, + { input: u32Bits(0b00100000000000000000000000000000), expected: u32(29) }, + { input: u32Bits(0b01000000000000000000000000000000), expected: u32(30) }, + { input: u32Bits(0b10000000000000000000000000000000), expected: u32(31) }, + + // 1's after leading 1 + { input: u32Bits(0b00000000000000000000000000000011), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000000000111), expected: u32(2) }, + { input: u32Bits(0b00000000000000000000000000001111), expected: u32(3) }, + { input: u32Bits(0b00000000000000000000000000011111), expected: u32(4) }, + { input: u32Bits(0b00000000000000000000000000111111), expected: u32(5) }, + { input: u32Bits(0b00000000000000000000000001111111), expected: u32(6) }, + { input: u32Bits(0b00000000000000000000000011111111), expected: u32(7) }, + { input: u32Bits(0b00000000000000000000000111111111), expected: u32(8) }, + { input: u32Bits(0b00000000000000000000001111111111), expected: u32(9) }, + { input: u32Bits(0b00000000000000000000011111111111), expected: u32(10) }, + { input: u32Bits(0b00000000000000000000111111111111), expected: u32(11) }, + { input: u32Bits(0b00000000000000000001111111111111), expected: u32(12) }, + { input: u32Bits(0b00000000000000000011111111111111), expected: u32(13) }, + { input: u32Bits(0b00000000000000000111111111111111), expected: u32(14) }, + { input: u32Bits(0b00000000000000001111111111111111), expected: u32(15) }, + { input: u32Bits(0b00000000000000011111111111111111), expected: u32(16) }, + { input: u32Bits(0b00000000000000111111111111111111), expected: u32(17) }, + { input: u32Bits(0b00000000000001111111111111111111), expected: u32(18) }, + { input: u32Bits(0b00000000000011111111111111111111), expected: u32(19) }, + { input: u32Bits(0b00000000000111111111111111111111), expected: u32(20) }, + { input: u32Bits(0b00000000001111111111111111111111), expected: u32(21) }, + { input: u32Bits(0b00000000011111111111111111111111), expected: u32(22) }, + { input: u32Bits(0b00000000111111111111111111111111), expected: u32(23) }, + { input: u32Bits(0b00000001111111111111111111111111), expected: u32(24) }, + { input: u32Bits(0b00000011111111111111111111111111), expected: u32(25) }, + { input: u32Bits(0b00000111111111111111111111111111), expected: u32(26) }, + { input: u32Bits(0b00001111111111111111111111111111), expected: u32(27) }, + { input: u32Bits(0b00011111111111111111111111111111), expected: u32(28) }, + { input: u32Bits(0b00111111111111111111111111111111), expected: u32(29) }, + { input: u32Bits(0b01111111111111111111111111111111), expected: u32(30) }, + { input: u32Bits(0b11111111111111111111111111111111), expected: u32(31) }, + + // random after leading 1 + { input: u32Bits(0b00000000000000000000000000000110), expected: u32(2) }, + { input: u32Bits(0b00000000000000000000000000001101), expected: u32(3) }, + { input: u32Bits(0b00000000000000000000000000011101), expected: u32(4) }, + { input: u32Bits(0b00000000000000000000000000111001), expected: u32(5) }, + { input: u32Bits(0b00000000000000000000000001101111), expected: u32(6) }, + { input: u32Bits(0b00000000000000000000000011111111), expected: u32(7) }, + { input: u32Bits(0b00000000000000000000000111101111), expected: u32(8) }, + { input: u32Bits(0b00000000000000000000001111111111), expected: u32(9) }, + { input: u32Bits(0b00000000000000000000011111110001), expected: u32(10) }, + { input: u32Bits(0b00000000000000000000111011011101), expected: u32(11) }, + { input: u32Bits(0b00000000000000000001101101111111), expected: u32(12) }, + { input: u32Bits(0b00000000000000000011111111011111), expected: u32(13) }, + { input: u32Bits(0b00000000000000000101111001110101), expected: u32(14) }, + { input: u32Bits(0b00000000000000001101111011110111), expected: u32(15) }, + { input: u32Bits(0b00000000000000011111111111110011), expected: u32(16) }, + { input: u32Bits(0b00000000000000111111111110111111), expected: u32(17) }, + { input: u32Bits(0b00000000000001111111011111111111), expected: u32(18) }, + { input: u32Bits(0b00000000000011111111111111111111), expected: u32(19) }, + { input: u32Bits(0b00000000000111110101011110111111), expected: u32(20) }, + { input: u32Bits(0b00000000001111101111111111110111), expected: u32(21) }, + { input: u32Bits(0b00000000011111111111010000101111), expected: u32(22) }, + { input: u32Bits(0b00000000111111111111001111111011), expected: u32(23) }, + { input: u32Bits(0b00000001111111011111101111111111), expected: u32(24) }, + { input: u32Bits(0b00000011101011111011110111111011), expected: u32(25) }, + { input: u32Bits(0b00000111111110111111111111111111), expected: u32(26) }, + { input: u32Bits(0b00001111000000011011011110111111), expected: u32(27) }, + { input: u32Bits(0b00011110101111011111111111111111), expected: u32(28) }, + { input: u32Bits(0b00110110111111100111111110111101), expected: u32(29) }, + { input: u32Bits(0b01010111111101111111011111011111), expected: u32(30) }, + { input: u32Bits(0b11100010011110101101101110101111), expected: u32(31) }, + ]); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('firstLeadingBit'), [TypeI32], TypeI32, cfg, [ + // Zero + { input: i32Bits(0b00000000000000000000000000000000), expected: i32(-1) }, + + // One + { input: i32Bits(0b00000000000000000000000000000001), expected: i32(0) }, + + // Positive: 0's after leading 1 + { input: i32Bits(0b00000000000000000000000000000010), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000000000100), expected: i32(2) }, + { input: i32Bits(0b00000000000000000000000000001000), expected: i32(3) }, + { input: i32Bits(0b00000000000000000000000000010000), expected: i32(4) }, + { input: i32Bits(0b00000000000000000000000000100000), expected: i32(5) }, + { input: i32Bits(0b00000000000000000000000001000000), expected: i32(6) }, + { input: i32Bits(0b00000000000000000000000010000000), expected: i32(7) }, + { input: i32Bits(0b00000000000000000000000100000000), expected: i32(8) }, + { input: i32Bits(0b00000000000000000000001000000000), expected: i32(9) }, + { input: i32Bits(0b00000000000000000000010000000000), expected: i32(10) }, + { input: i32Bits(0b00000000000000000000100000000000), expected: i32(11) }, + { input: i32Bits(0b00000000000000000001000000000000), expected: i32(12) }, + { input: i32Bits(0b00000000000000000010000000000000), expected: i32(13) }, + { input: i32Bits(0b00000000000000000100000000000000), expected: i32(14) }, + { input: i32Bits(0b00000000000000001000000000000000), expected: i32(15) }, + { input: i32Bits(0b00000000000000010000000000000000), expected: i32(16) }, + { input: i32Bits(0b00000000000000100000000000000000), expected: i32(17) }, + { input: i32Bits(0b00000000000001000000000000000000), expected: i32(18) }, + { input: i32Bits(0b00000000000010000000000000000000), expected: i32(19) }, + { input: i32Bits(0b00000000000100000000000000000000), expected: i32(20) }, + { input: i32Bits(0b00000000001000000000000000000000), expected: i32(21) }, + { input: i32Bits(0b00000000010000000000000000000000), expected: i32(22) }, + { input: i32Bits(0b00000000100000000000000000000000), expected: i32(23) }, + { input: i32Bits(0b00000001000000000000000000000000), expected: i32(24) }, + { input: i32Bits(0b00000010000000000000000000000000), expected: i32(25) }, + { input: i32Bits(0b00000100000000000000000000000000), expected: i32(26) }, + { input: i32Bits(0b00001000000000000000000000000000), expected: i32(27) }, + { input: i32Bits(0b00010000000000000000000000000000), expected: i32(28) }, + { input: i32Bits(0b00100000000000000000000000000000), expected: i32(29) }, + { input: i32Bits(0b01000000000000000000000000000000), expected: i32(30) }, + + // Negative: 0's after leading 0 + { input: i32Bits(0b11111111111111111111111111111110), expected: i32(0) }, + { input: i32Bits(0b11111111111111111111111111111100), expected: i32(1) }, + { input: i32Bits(0b11111111111111111111111111111000), expected: i32(2) }, + { input: i32Bits(0b11111111111111111111111111110000), expected: i32(3) }, + { input: i32Bits(0b11111111111111111111111111100000), expected: i32(4) }, + { input: i32Bits(0b11111111111111111111111111000000), expected: i32(5) }, + { input: i32Bits(0b11111111111111111111111110000000), expected: i32(6) }, + { input: i32Bits(0b11111111111111111111111100000000), expected: i32(7) }, + { input: i32Bits(0b11111111111111111111111000000000), expected: i32(8) }, + { input: i32Bits(0b11111111111111111111110000000000), expected: i32(9) }, + { input: i32Bits(0b11111111111111111111100000000000), expected: i32(10) }, + { input: i32Bits(0b11111111111111111111000000000000), expected: i32(11) }, + { input: i32Bits(0b11111111111111111110000000000000), expected: i32(12) }, + { input: i32Bits(0b11111111111111111100000000000000), expected: i32(13) }, + { input: i32Bits(0b11111111111111111000000000000000), expected: i32(14) }, + { input: i32Bits(0b11111111111111110000000000000000), expected: i32(15) }, + { input: i32Bits(0b11111111111111100000000000000000), expected: i32(16) }, + { input: i32Bits(0b11111111111111000000000000000000), expected: i32(17) }, + { input: i32Bits(0b11111111111110000000000000000000), expected: i32(18) }, + { input: i32Bits(0b11111111111100000000000000000000), expected: i32(19) }, + { input: i32Bits(0b11111111111000000000000000000000), expected: i32(20) }, + { input: i32Bits(0b11111111110000000000000000000000), expected: i32(21) }, + { input: i32Bits(0b11111111100000000000000000000000), expected: i32(22) }, + { input: i32Bits(0b11111111000000000000000000000000), expected: i32(23) }, + { input: i32Bits(0b11111110000000000000000000000000), expected: i32(24) }, + { input: i32Bits(0b11111100000000000000000000000000), expected: i32(25) }, + { input: i32Bits(0b11111000000000000000000000000000), expected: i32(26) }, + { input: i32Bits(0b11110000000000000000000000000000), expected: i32(27) }, + { input: i32Bits(0b11100000000000000000000000000000), expected: i32(28) }, + { input: i32Bits(0b11000000000000000000000000000000), expected: i32(29) }, + { input: i32Bits(0b10000000000000000000000000000000), expected: i32(30) }, + + // Positive: 1's after leading 1 + { input: i32Bits(0b00000000000000000000000000000011), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000000000111), expected: i32(2) }, + { input: i32Bits(0b00000000000000000000000000001111), expected: i32(3) }, + { input: i32Bits(0b00000000000000000000000000011111), expected: i32(4) }, + { input: i32Bits(0b00000000000000000000000000111111), expected: i32(5) }, + { input: i32Bits(0b00000000000000000000000001111111), expected: i32(6) }, + { input: i32Bits(0b00000000000000000000000011111111), expected: i32(7) }, + { input: i32Bits(0b00000000000000000000000111111111), expected: i32(8) }, + { input: i32Bits(0b00000000000000000000001111111111), expected: i32(9) }, + { input: i32Bits(0b00000000000000000000011111111111), expected: i32(10) }, + { input: i32Bits(0b00000000000000000000111111111111), expected: i32(11) }, + { input: i32Bits(0b00000000000000000001111111111111), expected: i32(12) }, + { input: i32Bits(0b00000000000000000011111111111111), expected: i32(13) }, + { input: i32Bits(0b00000000000000000111111111111111), expected: i32(14) }, + { input: i32Bits(0b00000000000000001111111111111111), expected: i32(15) }, + { input: i32Bits(0b00000000000000011111111111111111), expected: i32(16) }, + { input: i32Bits(0b00000000000000111111111111111111), expected: i32(17) }, + { input: i32Bits(0b00000000000001111111111111111111), expected: i32(18) }, + { input: i32Bits(0b00000000000011111111111111111111), expected: i32(19) }, + { input: i32Bits(0b00000000000111111111111111111111), expected: i32(20) }, + { input: i32Bits(0b00000000001111111111111111111111), expected: i32(21) }, + { input: i32Bits(0b00000000011111111111111111111111), expected: i32(22) }, + { input: i32Bits(0b00000000111111111111111111111111), expected: i32(23) }, + { input: i32Bits(0b00000001111111111111111111111111), expected: i32(24) }, + { input: i32Bits(0b00000011111111111111111111111111), expected: i32(25) }, + { input: i32Bits(0b00000111111111111111111111111111), expected: i32(26) }, + { input: i32Bits(0b00001111111111111111111111111111), expected: i32(27) }, + { input: i32Bits(0b00011111111111111111111111111111), expected: i32(28) }, + { input: i32Bits(0b00111111111111111111111111111111), expected: i32(29) }, + { input: i32Bits(0b01111111111111111111111111111111), expected: i32(30) }, + + // Negative: 1's after leading 0 + { input: i32Bits(0b11111111111111111111111111111101), expected: i32(1) }, + { input: i32Bits(0b11111111111111111111111111111011), expected: i32(2) }, + { input: i32Bits(0b11111111111111111111111111110111), expected: i32(3) }, + { input: i32Bits(0b11111111111111111111111111101111), expected: i32(4) }, + { input: i32Bits(0b11111111111111111111111111011111), expected: i32(5) }, + { input: i32Bits(0b11111111111111111111111110111111), expected: i32(6) }, + { input: i32Bits(0b11111111111111111111111101111111), expected: i32(7) }, + { input: i32Bits(0b11111111111111111111111011111111), expected: i32(8) }, + { input: i32Bits(0b11111111111111111111110111111111), expected: i32(9) }, + { input: i32Bits(0b11111111111111111111101111111111), expected: i32(10) }, + { input: i32Bits(0b11111111111111111111011111111111), expected: i32(11) }, + { input: i32Bits(0b11111111111111111110111111111111), expected: i32(12) }, + { input: i32Bits(0b11111111111111111101111111111111), expected: i32(13) }, + { input: i32Bits(0b11111111111111111011111111111111), expected: i32(14) }, + { input: i32Bits(0b11111111111111110111111111111111), expected: i32(15) }, + { input: i32Bits(0b11111111111111101111111111111111), expected: i32(16) }, + { input: i32Bits(0b11111111111111011111111111111111), expected: i32(17) }, + { input: i32Bits(0b11111111111110111111111111111111), expected: i32(18) }, + { input: i32Bits(0b11111111111101111111111111111111), expected: i32(19) }, + { input: i32Bits(0b11111111111011111111111111111111), expected: i32(20) }, + { input: i32Bits(0b11111111110111111111111111111111), expected: i32(21) }, + { input: i32Bits(0b11111111101111111111111111111111), expected: i32(22) }, + { input: i32Bits(0b11111111011111111111111111111111), expected: i32(23) }, + { input: i32Bits(0b11111110111111111111111111111111), expected: i32(24) }, + { input: i32Bits(0b11111101111111111111111111111111), expected: i32(25) }, + { input: i32Bits(0b11111011111111111111111111111111), expected: i32(26) }, + { input: i32Bits(0b11110111111111111111111111111111), expected: i32(27) }, + { input: i32Bits(0b11101111111111111111111111111111), expected: i32(28) }, + { input: i32Bits(0b11011111111111111111111111111111), expected: i32(29) }, + { input: i32Bits(0b10111111111111111111111111111111), expected: i32(30) }, + + // Positive: random after leading 1 + { input: i32Bits(0b00000000000000000000000000000110), expected: i32(2) }, + { input: i32Bits(0b00000000000000000000000000001101), expected: i32(3) }, + { input: i32Bits(0b00000000000000000000000000011101), expected: i32(4) }, + { input: i32Bits(0b00000000000000000000000000111001), expected: i32(5) }, + { input: i32Bits(0b00000000000000000000000001101111), expected: i32(6) }, + { input: i32Bits(0b00000000000000000000000011111111), expected: i32(7) }, + { input: i32Bits(0b00000000000000000000000111101111), expected: i32(8) }, + { input: i32Bits(0b00000000000000000000001111111111), expected: i32(9) }, + { input: i32Bits(0b00000000000000000000011111110001), expected: i32(10) }, + { input: i32Bits(0b00000000000000000000111011011101), expected: i32(11) }, + { input: i32Bits(0b00000000000000000001101101111111), expected: i32(12) }, + { input: i32Bits(0b00000000000000000011111111011111), expected: i32(13) }, + { input: i32Bits(0b00000000000000000101111001110101), expected: i32(14) }, + { input: i32Bits(0b00000000000000001101111011110111), expected: i32(15) }, + { input: i32Bits(0b00000000000000011111111111110011), expected: i32(16) }, + { input: i32Bits(0b00000000000000111111111110111111), expected: i32(17) }, + { input: i32Bits(0b00000000000001111111011111111111), expected: i32(18) }, + { input: i32Bits(0b00000000000011111111111111111111), expected: i32(19) }, + { input: i32Bits(0b00000000000111110101011110111111), expected: i32(20) }, + { input: i32Bits(0b00000000001111101111111111110111), expected: i32(21) }, + { input: i32Bits(0b00000000011111111111010000101111), expected: i32(22) }, + { input: i32Bits(0b00000000111111111111001111111011), expected: i32(23) }, + { input: i32Bits(0b00000001111111011111101111111111), expected: i32(24) }, + { input: i32Bits(0b00000011101011111011110111111011), expected: i32(25) }, + { input: i32Bits(0b00000111111110111111111111111111), expected: i32(26) }, + { input: i32Bits(0b00001111000000011011011110111111), expected: i32(27) }, + { input: i32Bits(0b00011110101111011111111111111111), expected: i32(28) }, + { input: i32Bits(0b00110110111111100111111110111101), expected: i32(29) }, + { input: i32Bits(0b01010111111101111111011111011111), expected: i32(30) }, + + // Negative: random after leading 0 + { input: i32Bits(0b11111111111111111111111111111010), expected: i32(2) }, + { input: i32Bits(0b11111111111111111111111111110110), expected: i32(3) }, + { input: i32Bits(0b11111111111111111111111111101101), expected: i32(4) }, + { input: i32Bits(0b11111111111111111111111111011101), expected: i32(5) }, + { input: i32Bits(0b11111111111111111111111110111001), expected: i32(6) }, + { input: i32Bits(0b11111111111111111111111101101111), expected: i32(7) }, + { input: i32Bits(0b11111111111111111111111011111111), expected: i32(8) }, + { input: i32Bits(0b11111111111111111111110111101111), expected: i32(9) }, + { input: i32Bits(0b11111111111111111111101111111111), expected: i32(10) }, + { input: i32Bits(0b11111111111111111111011111110001), expected: i32(11) }, + { input: i32Bits(0b11111111111111111110111011011101), expected: i32(12) }, + { input: i32Bits(0b11111111111111111101101101111111), expected: i32(13) }, + { input: i32Bits(0b11111111111111111011111111011111), expected: i32(14) }, + { input: i32Bits(0b11111111111111110101111001110101), expected: i32(15) }, + { input: i32Bits(0b11111111111111101101111011110111), expected: i32(16) }, + { input: i32Bits(0b11111111111111011111111111110011), expected: i32(17) }, + { input: i32Bits(0b11111111111110111111111110111111), expected: i32(18) }, + { input: i32Bits(0b11111111111101111111011111111111), expected: i32(19) }, + { input: i32Bits(0b11111111111011111111111111111111), expected: i32(20) }, + { input: i32Bits(0b11111111110111110101011110111111), expected: i32(21) }, + { input: i32Bits(0b11111111101111101111111111110111), expected: i32(22) }, + { input: i32Bits(0b11111111011111111111010000101111), expected: i32(23) }, + { input: i32Bits(0b11111110111111111111001111111011), expected: i32(24) }, + { input: i32Bits(0b11111101111111011111101111111111), expected: i32(25) }, + { input: i32Bits(0b11111011101011111011110111111011), expected: i32(26) }, + { input: i32Bits(0b11110111111110111111111111111111), expected: i32(27) }, + { input: i32Bits(0b11101111000000011011011110111111), expected: i32(28) }, + { input: i32Bits(0b11011110101111011111111111111111), expected: i32(29) }, + { input: i32Bits(0b10110110111111100111111110111101), expected: i32(30) }, + ]); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts new file mode 100644 index 0000000000..5c65f59d28 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts @@ -0,0 +1,250 @@ +export const description = ` +Execution tests for the 'firstTrailingBit' builtin function + +S is i32, u32 +T is S or vecN<S> +@const fn firstTrailingBit(e: T ) -> T +For scalar T, the result is: T(-1) if e is zero. +Otherwise the position of the least significant 1 bit in e. +Component-wise when T is a vector. + +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js'; +import { allInputSources, Config, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('firstTrailingBit'), [TypeU32], TypeU32, cfg, [ + // Zero + { input: u32Bits(0b00000000000000000000000000000000), expected: u32(-1) }, + + // High bit + { input: u32Bits(0b10000000000000000000000000000000), expected: u32(31) }, + + // 0's before trailing 1 + { input: u32Bits(0b00000000000000000000000000000001), expected: u32(0) }, + { input: u32Bits(0b00000000000000000000000000000010), expected: u32(1) }, + { input: u32Bits(0b00000000000000000000000000000100), expected: u32(2) }, + { input: u32Bits(0b00000000000000000000000000001000), expected: u32(3) }, + { input: u32Bits(0b00000000000000000000000000010000), expected: u32(4) }, + { input: u32Bits(0b00000000000000000000000000100000), expected: u32(5) }, + { input: u32Bits(0b00000000000000000000000001000000), expected: u32(6) }, + { input: u32Bits(0b00000000000000000000000010000000), expected: u32(7) }, + { input: u32Bits(0b00000000000000000000000100000000), expected: u32(8) }, + { input: u32Bits(0b00000000000000000000001000000000), expected: u32(9) }, + { input: u32Bits(0b00000000000000000000010000000000), expected: u32(10) }, + { input: u32Bits(0b00000000000000000000100000000000), expected: u32(11) }, + { input: u32Bits(0b00000000000000000001000000000000), expected: u32(12) }, + { input: u32Bits(0b00000000000000000010000000000000), expected: u32(13) }, + { input: u32Bits(0b00000000000000000100000000000000), expected: u32(14) }, + { input: u32Bits(0b00000000000000001000000000000000), expected: u32(15) }, + { input: u32Bits(0b00000000000000010000000000000000), expected: u32(16) }, + { input: u32Bits(0b00000000000000100000000000000000), expected: u32(17) }, + { input: u32Bits(0b00000000000001000000000000000000), expected: u32(18) }, + { input: u32Bits(0b00000000000010000000000000000000), expected: u32(19) }, + { input: u32Bits(0b00000000000100000000000000000000), expected: u32(20) }, + { input: u32Bits(0b00000000001000000000000000000000), expected: u32(21) }, + { input: u32Bits(0b00000000010000000000000000000000), expected: u32(22) }, + { input: u32Bits(0b00000000100000000000000000000000), expected: u32(23) }, + { input: u32Bits(0b00000001000000000000000000000000), expected: u32(24) }, + { input: u32Bits(0b00000010000000000000000000000000), expected: u32(25) }, + { input: u32Bits(0b00000100000000000000000000000000), expected: u32(26) }, + { input: u32Bits(0b00001000000000000000000000000000), expected: u32(27) }, + { input: u32Bits(0b00010000000000000000000000000000), expected: u32(28) }, + { input: u32Bits(0b00100000000000000000000000000000), expected: u32(29) }, + { input: u32Bits(0b01000000000000000000000000000000), expected: u32(30) }, + + // 1's before trailing 1 + { input: u32Bits(0b11111111111111111111111111111111), expected: u32(0) }, + { input: u32Bits(0b11111111111111111111111111111110), expected: u32(1) }, + { input: u32Bits(0b11111111111111111111111111111100), expected: u32(2) }, + { input: u32Bits(0b11111111111111111111111111111000), expected: u32(3) }, + { input: u32Bits(0b11111111111111111111111111110000), expected: u32(4) }, + { input: u32Bits(0b11111111111111111111111111100000), expected: u32(5) }, + { input: u32Bits(0b11111111111111111111111111000000), expected: u32(6) }, + { input: u32Bits(0b11111111111111111111111110000000), expected: u32(7) }, + { input: u32Bits(0b11111111111111111111111100000000), expected: u32(8) }, + { input: u32Bits(0b11111111111111111111111000000000), expected: u32(9) }, + { input: u32Bits(0b11111111111111111111110000000000), expected: u32(10) }, + { input: u32Bits(0b11111111111111111111100000000000), expected: u32(11) }, + { input: u32Bits(0b11111111111111111111000000000000), expected: u32(12) }, + { input: u32Bits(0b11111111111111111110000000000000), expected: u32(13) }, + { input: u32Bits(0b11111111111111111100000000000000), expected: u32(14) }, + { input: u32Bits(0b11111111111111111000000000000000), expected: u32(15) }, + { input: u32Bits(0b11111111111111110000000000000000), expected: u32(16) }, + { input: u32Bits(0b11111111111111100000000000000000), expected: u32(17) }, + { input: u32Bits(0b11111111111111000000000000000000), expected: u32(18) }, + { input: u32Bits(0b11111111111110000000000000000000), expected: u32(19) }, + { input: u32Bits(0b11111111111100000000000000000000), expected: u32(20) }, + { input: u32Bits(0b11111111111000000000000000000000), expected: u32(21) }, + { input: u32Bits(0b11111111110000000000000000000000), expected: u32(22) }, + { input: u32Bits(0b11111111100000000000000000000000), expected: u32(23) }, + { input: u32Bits(0b11111111000000000000000000000000), expected: u32(24) }, + { input: u32Bits(0b11111110000000000000000000000000), expected: u32(25) }, + { input: u32Bits(0b11111100000000000000000000000000), expected: u32(26) }, + { input: u32Bits(0b11111000000000000000000000000000), expected: u32(27) }, + { input: u32Bits(0b11110000000000000000000000000000), expected: u32(28) }, + { input: u32Bits(0b11100000000000000000000000000000), expected: u32(29) }, + { input: u32Bits(0b11000000000000000000000000000000), expected: u32(30) }, + + // random before trailing 1 + { input: u32Bits(0b11110000001111111101111010001111), expected: u32(0) }, + { input: u32Bits(0b11011110111111100101110011110010), expected: u32(1) }, + { input: u32Bits(0b11110111011011111111010000111100), expected: u32(2) }, + { input: u32Bits(0b11010011011101111111010011101000), expected: u32(3) }, + { input: u32Bits(0b11010111110111110001111110110000), expected: u32(4) }, + { input: u32Bits(0b11111101111101111110101111100000), expected: u32(5) }, + { input: u32Bits(0b11111001111011111001111011000000), expected: u32(6) }, + { input: u32Bits(0b11001110110111110111111010000000), expected: u32(7) }, + { input: u32Bits(0b11101111011111101110101100000000), expected: u32(8) }, + { input: u32Bits(0b11111101111011111111111000000000), expected: u32(9) }, + { input: u32Bits(0b10011111011101110110110000000000), expected: u32(10) }, + { input: u32Bits(0b11111111101101111011100000000000), expected: u32(11) }, + { input: u32Bits(0b11111011010110111011000000000000), expected: u32(12) }, + { input: u32Bits(0b00111101010000111010000000000000), expected: u32(13) }, + { input: u32Bits(0b11111011110001101100000000000000), expected: u32(14) }, + { input: u32Bits(0b10111111010111111000000000000000), expected: u32(15) }, + { input: u32Bits(0b11011101111010110000000000000000), expected: u32(16) }, + { input: u32Bits(0b01110100110110100000000000000000), expected: u32(17) }, + { input: u32Bits(0b11100111001011000000000000000000), expected: u32(18) }, + { input: u32Bits(0b11111001110110000000000000000000), expected: u32(19) }, + { input: u32Bits(0b00110100100100000000000000000000), expected: u32(20) }, + { input: u32Bits(0b11111010011000000000000000000000), expected: u32(21) }, + { input: u32Bits(0b00000010110000000000000000000000), expected: u32(22) }, + { input: u32Bits(0b11100111100000000000000000000000), expected: u32(23) }, + { input: u32Bits(0b00101101000000000000000000000000), expected: u32(24) }, + { input: u32Bits(0b11011010000000000000000000000000), expected: u32(25) }, + { input: u32Bits(0b11010100000000000000000000000000), expected: u32(26) }, + { input: u32Bits(0b10111000000000000000000000000000), expected: u32(27) }, + { input: u32Bits(0b01110000000000000000000000000000), expected: u32(28) }, + { input: u32Bits(0b10100000000000000000000000000000), expected: u32(29) }, + ]); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + await run(t, builtin('firstTrailingBit'), [TypeI32], TypeI32, cfg, [ + // Zero + { input: i32Bits(0b00000000000000000000000000000000), expected: i32(-1) }, + + // High bit + { input: i32Bits(0b10000000000000000000000000000000), expected: i32(31) }, + + // 0's before trailing 1 + { input: i32Bits(0b00000000000000000000000000000001), expected: i32(0) }, + { input: i32Bits(0b00000000000000000000000000000010), expected: i32(1) }, + { input: i32Bits(0b00000000000000000000000000000100), expected: i32(2) }, + { input: i32Bits(0b00000000000000000000000000001000), expected: i32(3) }, + { input: i32Bits(0b00000000000000000000000000010000), expected: i32(4) }, + { input: i32Bits(0b00000000000000000000000000100000), expected: i32(5) }, + { input: i32Bits(0b00000000000000000000000001000000), expected: i32(6) }, + { input: i32Bits(0b00000000000000000000000010000000), expected: i32(7) }, + { input: i32Bits(0b00000000000000000000000100000000), expected: i32(8) }, + { input: i32Bits(0b00000000000000000000001000000000), expected: i32(9) }, + { input: i32Bits(0b00000000000000000000010000000000), expected: i32(10) }, + { input: i32Bits(0b00000000000000000000100000000000), expected: i32(11) }, + { input: i32Bits(0b00000000000000000001000000000000), expected: i32(12) }, + { input: i32Bits(0b00000000000000000010000000000000), expected: i32(13) }, + { input: i32Bits(0b00000000000000000100000000000000), expected: i32(14) }, + { input: i32Bits(0b00000000000000001000000000000000), expected: i32(15) }, + { input: i32Bits(0b00000000000000010000000000000000), expected: i32(16) }, + { input: i32Bits(0b00000000000000100000000000000000), expected: i32(17) }, + { input: i32Bits(0b00000000000001000000000000000000), expected: i32(18) }, + { input: i32Bits(0b00000000000010000000000000000000), expected: i32(19) }, + { input: i32Bits(0b00000000000100000000000000000000), expected: i32(20) }, + { input: i32Bits(0b00000000001000000000000000000000), expected: i32(21) }, + { input: i32Bits(0b00000000010000000000000000000000), expected: i32(22) }, + { input: i32Bits(0b00000000100000000000000000000000), expected: i32(23) }, + { input: i32Bits(0b00000001000000000000000000000000), expected: i32(24) }, + { input: i32Bits(0b00000010000000000000000000000000), expected: i32(25) }, + { input: i32Bits(0b00000100000000000000000000000000), expected: i32(26) }, + { input: i32Bits(0b00001000000000000000000000000000), expected: i32(27) }, + { input: i32Bits(0b00010000000000000000000000000000), expected: i32(28) }, + { input: i32Bits(0b00100000000000000000000000000000), expected: i32(29) }, + { input: i32Bits(0b01000000000000000000000000000000), expected: i32(30) }, + + // 1's before trailing 1 + { input: i32Bits(0b11111111111111111111111111111111), expected: i32(0) }, + { input: i32Bits(0b11111111111111111111111111111110), expected: i32(1) }, + { input: i32Bits(0b11111111111111111111111111111100), expected: i32(2) }, + { input: i32Bits(0b11111111111111111111111111111000), expected: i32(3) }, + { input: i32Bits(0b11111111111111111111111111110000), expected: i32(4) }, + { input: i32Bits(0b11111111111111111111111111100000), expected: i32(5) }, + { input: i32Bits(0b11111111111111111111111111000000), expected: i32(6) }, + { input: i32Bits(0b11111111111111111111111110000000), expected: i32(7) }, + { input: i32Bits(0b11111111111111111111111100000000), expected: i32(8) }, + { input: i32Bits(0b11111111111111111111111000000000), expected: i32(9) }, + { input: i32Bits(0b11111111111111111111110000000000), expected: i32(10) }, + { input: i32Bits(0b11111111111111111111100000000000), expected: i32(11) }, + { input: i32Bits(0b11111111111111111111000000000000), expected: i32(12) }, + { input: i32Bits(0b11111111111111111110000000000000), expected: i32(13) }, + { input: i32Bits(0b11111111111111111100000000000000), expected: i32(14) }, + { input: i32Bits(0b11111111111111111000000000000000), expected: i32(15) }, + { input: i32Bits(0b11111111111111110000000000000000), expected: i32(16) }, + { input: i32Bits(0b11111111111111100000000000000000), expected: i32(17) }, + { input: i32Bits(0b11111111111111000000000000000000), expected: i32(18) }, + { input: i32Bits(0b11111111111110000000000000000000), expected: i32(19) }, + { input: i32Bits(0b11111111111100000000000000000000), expected: i32(20) }, + { input: i32Bits(0b11111111111000000000000000000000), expected: i32(21) }, + { input: i32Bits(0b11111111110000000000000000000000), expected: i32(22) }, + { input: i32Bits(0b11111111100000000000000000000000), expected: i32(23) }, + { input: i32Bits(0b11111111000000000000000000000000), expected: i32(24) }, + { input: i32Bits(0b11111110000000000000000000000000), expected: i32(25) }, + { input: i32Bits(0b11111100000000000000000000000000), expected: i32(26) }, + { input: i32Bits(0b11111000000000000000000000000000), expected: i32(27) }, + { input: i32Bits(0b11110000000000000000000000000000), expected: i32(28) }, + { input: i32Bits(0b11100000000000000000000000000000), expected: i32(29) }, + { input: i32Bits(0b11000000000000000000000000000000), expected: i32(30) }, + + // random before trailing 1 + { input: i32Bits(0b11110000001111111101111010001111), expected: i32(0) }, + { input: i32Bits(0b11011110111111100101110011110010), expected: i32(1) }, + { input: i32Bits(0b11110111011011111111010000111100), expected: i32(2) }, + { input: i32Bits(0b11010011011101111111010011101000), expected: i32(3) }, + { input: i32Bits(0b11010111110111110001111110110000), expected: i32(4) }, + { input: i32Bits(0b11111101111101111110101111100000), expected: i32(5) }, + { input: i32Bits(0b11111001111011111001111011000000), expected: i32(6) }, + { input: i32Bits(0b11001110110111110111111010000000), expected: i32(7) }, + { input: i32Bits(0b11101111011111101110101100000000), expected: i32(8) }, + { input: i32Bits(0b11111101111011111111111000000000), expected: i32(9) }, + { input: i32Bits(0b10011111011101110110110000000000), expected: i32(10) }, + { input: i32Bits(0b11111111101101111011100000000000), expected: i32(11) }, + { input: i32Bits(0b11111011010110111011000000000000), expected: i32(12) }, + { input: i32Bits(0b00111101010000111010000000000000), expected: i32(13) }, + { input: i32Bits(0b11111011110001101100000000000000), expected: i32(14) }, + { input: i32Bits(0b10111111010111111000000000000000), expected: i32(15) }, + { input: i32Bits(0b11011101111010110000000000000000), expected: i32(16) }, + { input: i32Bits(0b01110100110110100000000000000000), expected: i32(17) }, + { input: i32Bits(0b11100111001011000000000000000000), expected: i32(18) }, + { input: i32Bits(0b11111001110110000000000000000000), expected: i32(19) }, + { input: i32Bits(0b00110100100100000000000000000000), expected: i32(20) }, + { input: i32Bits(0b11111010011000000000000000000000), expected: i32(21) }, + { input: i32Bits(0b00000010110000000000000000000000), expected: i32(22) }, + { input: i32Bits(0b11100111100000000000000000000000), expected: i32(23) }, + { input: i32Bits(0b00101101000000000000000000000000), expected: i32(24) }, + { input: i32Bits(0b11011010000000000000000000000000), expected: i32(25) }, + { input: i32Bits(0b11010100000000000000000000000000), expected: i32(26) }, + { input: i32Bits(0b10111000000000000000000000000000), expected: i32(27) }, + { input: i32Bits(0b01110000000000000000000000000000), expected: i32(28) }, + { input: i32Bits(0b10100000000000000000000000000000), expected: i32(29) }, + ]); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts new file mode 100644 index 0000000000..227efff7ef --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts @@ -0,0 +1,71 @@ +export const description = ` +Execution tests for the 'floor' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn floor(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { floorInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('floor', { + f32: () => { + return generateUnaryToF32IntervalCases( + [ + // Small positive numbers + 0.1, + 0.9, + 1.0, + 1.1, + 1.9, + // Small negative numbers + -0.1, + -0.9, + -1.0, + -1.1, + -1.9, + ...fullF32Range(), + ], + 'unfiltered', + floorInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('floor'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts new file mode 100644 index 0000000000..f7488eb7b0 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts @@ -0,0 +1,68 @@ +export const description = ` +Execution tests for the 'fma' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn fma(e1: T ,e2: T ,e3: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { fmaInterval } from '../../../../../util/f32_interval.js'; +import { sparseF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateTernaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('fma', { + f32_const: () => { + return generateTernaryToF32IntervalCases( + sparseF32Range(), + sparseF32Range(), + sparseF32Range(), + 'f32-only', + fmaInterval + ); + }, + f32_non_const: () => { + return generateTernaryToF32IntervalCases( + sparseF32Range(), + sparseF32Range(), + sparseF32Range(), + 'unfiltered', + fmaInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('fma'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts new file mode 100644 index 0000000000..c7d0244b96 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts @@ -0,0 +1,73 @@ +export const description = ` +Execution tests for the 'fract' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn fract(e: T ) -> T +Returns the fractional part of e, computed as e - floor(e). +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { fractInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('fract', { + f32: () => { + return generateUnaryToF32IntervalCases( + [ + 0.5, // 0.5 -> 0.5 + 0.9, // ~0.9 -> ~0.9 + 1, // 1 -> 0 + 2, // 2 -> 0 + 1.11, // ~1.11 -> ~0.11 + 10.0001, // ~10.0001 -> ~0.0001 + -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 + -10.0001, // -10.0001 -> ~0.9999 + ...fullF32Range(), + ], + 'unfiltered', + fractInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('fract'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts new file mode 100644 index 0000000000..02f301f868 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts @@ -0,0 +1,80 @@ +export const description = ` +Execution tests for the 'frexp' builtin function + +S is f32 or f16 +T is S or vecN<S> + +@const fn frexp(e: T) -> result_struct + +Splits e into a significand and exponent of the form significand * 2^exponent. +Returns the result_struct for the appropriate overload. + + +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 { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('scalar_f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +f32 tests + +struct __frexp_result { + sig : f32, // significand part + exp : i32 // exponent part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('scalar_f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +f16 tests + +struct __frexp_result_f16 { + sig : f16, // significand part + exp : i32 // exponent part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('vector_f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +vecN<f32> + +struct __frexp_result_vecN { + sig : vecN<f32>, // significand part + exp : vecN<i32> // exponent part +} +` + ) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); + +g.test('vector_f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +vecN<f16> + +struct __frexp_result_vecN_f16 { + sig : vecN<f16>, // significand part + exp : vecN<i32> // exponent part +} +` + ) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidth.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidth.spec.ts new file mode 100644 index 0000000000..7c6f0232a9 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidth.spec.ts @@ -0,0 +1,21 @@ +export const description = ` +Execution tests for the 'fwidth' builtin function + +T is f32 or vecN<f32> +fn fwidth(e:T) ->T +Returns abs(dpdx(e)) + abs(dpdy(e)). +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthCoarse.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthCoarse.spec.ts new file mode 100644 index 0000000000..9f93237934 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthCoarse.spec.ts @@ -0,0 +1,21 @@ +export const description = ` +Execution tests for the 'fwidthCoarse' builtin function + +T is f32 or vecN<f32> +fn fwidthCoarse(e:T) ->T +Returns abs(dpdxCoarse(e)) + abs(dpdyCoarse(e)). +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthFine.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthFine.spec.ts new file mode 100644 index 0000000000..b08c293228 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/fwidthFine.spec.ts @@ -0,0 +1,21 @@ +export const description = ` +Execution tests for the 'fwidthFine' builtin function + +T is f32 or vecN<f32> +fn fwidthFine(e:T) ->T +Returns abs(dpdxFine(e)) + abs(dpdyFine(e)). +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#derivative-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts new file mode 100644 index 0000000000..1068e76252 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts @@ -0,0 +1,386 @@ +export const description = ` +Execution tests for the 'insertBits' builtin function + +S is i32 or u32 +T is S or vecN<S> +@const fn insertBits(e: T, newbits:T, offset: u32, count: u32) -> T Sets bits in an integer. + +When T is a scalar type, then: + w is the bit width of T + o = min(offset,w) + c = min(count, w - o) + +The result is e if c is 0. +Otherwise, bits o..o+c-1 of the result are copied from bits 0..c-1 of newbits. +Other bits of the result are copied from e. +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { + i32Bits, + TypeI32, + u32, + TypeU32, + u32Bits, + vec2, + vec3, + vec4, + TypeVec, +} from '../../../../../util/conversion.js'; +import { allInputSources, Config, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('integer') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`integer tests`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('signed', [false, true]) + .combine('width', [1, 2, 3, 4]) + ) + .fn(async t => { + const cfg: Config = t.params; + const scalarType = t.params.signed ? TypeI32 : TypeU32; + const T = t.params.width === 1 ? scalarType : TypeVec(t.params.width, scalarType); + + const V = (x: number, y?: number, z?: number, w?: number) => { + y = y === undefined ? x : y; + z = z === undefined ? x : z; + w = w === undefined ? x : w; + + if (t.params.signed) { + switch (t.params.width) { + case 1: + return i32Bits(x); + case 2: + return vec2(i32Bits(x), i32Bits(y)); + case 3: + return vec3(i32Bits(x), i32Bits(y), i32Bits(z)); + default: + return vec4(i32Bits(x), i32Bits(y), i32Bits(z), i32Bits(w)); + } + } else { + switch (t.params.width) { + case 1: + return u32Bits(x); + case 2: + return vec2(u32Bits(x), u32Bits(y)); + case 3: + return vec3(u32Bits(x), u32Bits(y), u32Bits(z)); + default: + return vec4(u32Bits(x), u32Bits(y), u32Bits(z), u32Bits(w)); + } + } + }; + + const all_1 = V(0b11111111111111111111111111111111); + const all_0 = V(0b00000000000000000000000000000000); + const low_1 = V(0b00000000000000000000000000000001); + const low_0 = V(0b11111111111111111111111111111110); + const high_1 = V(0b10000000000000000000000000000000); + const high_0 = V(0b01111111111111111111111111111111); + const pattern = V( + 0b10001001010100100010010100100010, + 0b11001110001100111000110011100011, + 0b10101010101010101010101010101010, + 0b01010101010101010101010101010101 + ); + + const cases = [ + { input: [all_0, all_0, u32(0), u32(32)], expected: all_0 }, + { input: [all_0, all_0, u32(1), u32(10)], expected: all_0 }, + { input: [all_0, all_0, u32(2), u32(5)], expected: all_0 }, + { input: [all_0, all_0, u32(0), u32(1)], expected: all_0 }, + { input: [all_0, all_0, u32(31), u32(1)], expected: all_0 }, + + { input: [all_0, all_1, u32(0), u32(32)], expected: all_1 }, + { input: [all_1, all_0, u32(0), u32(32)], expected: all_0 }, + { input: [all_0, all_1, u32(0), u32(1)], expected: low_1 }, + { input: [all_1, all_0, u32(0), u32(1)], expected: low_0 }, + { input: [all_0, all_1, u32(31), u32(1)], expected: high_1 }, + { input: [all_1, all_0, u32(31), u32(1)], expected: high_0 }, + { input: [all_0, all_1, u32(1), u32(10)], expected: V(0b00000000000000000000011111111110) }, + { input: [all_1, all_0, u32(1), u32(10)], expected: V(0b11111111111111111111100000000001) }, + { input: [all_0, all_1, u32(2), u32(5)], expected: V(0b00000000000000000000000001111100) }, + { input: [all_1, all_0, u32(2), u32(5)], expected: V(0b11111111111111111111111110000011) }, + + // Patterns + { input: [all_0, pattern, u32(0), u32(32)], expected: pattern }, + { input: [all_1, pattern, u32(0), u32(32)], expected: pattern }, + { + input: [all_0, pattern, u32(1), u32(31)], + expected: V( + 0b00010010101001000100101001000100, + 0b10011100011001110001100111000110, + 0b01010101010101010101010101010100, + 0b10101010101010101010101010101010 + ), + }, + { + input: [all_1, pattern, u32(1), u32(31)], + expected: V( + 0b00010010101001000100101001000101, + 0b10011100011001110001100111000111, + 0b01010101010101010101010101010101, + 0b10101010101010101010101010101011 + ), + }, + { + input: [all_0, pattern, u32(14), u32(18)], + expected: V( + 0b10001001010010001000000000000000, + 0b11100011001110001100000000000000, + 0b10101010101010101000000000000000, + 0b01010101010101010100000000000000 + ), + }, + { + input: [all_1, pattern, u32(14), u32(18)], + expected: V( + 0b10001001010010001011111111111111, + 0b11100011001110001111111111111111, + 0b10101010101010101011111111111111, + 0b01010101010101010111111111111111 + ), + }, + { + input: [all_0, pattern, u32(14), u32(7)], + expected: V( + 0b00000000000010001000000000000000, + 0b00000000000110001100000000000000, + 0b00000000000010101000000000000000, + 0b00000000000101010100000000000000 + ), + }, + { + input: [all_1, pattern, u32(14), u32(7)], + expected: V( + 0b11111111111010001011111111111111, + 0b11111111111110001111111111111111, + 0b11111111111010101011111111111111, + 0b11111111111101010111111111111111 + ), + }, + { + input: [all_0, pattern, u32(14), u32(4)], + expected: V( + 0b00000000000000001000000000000000, + 0b00000000000000001100000000000000, + 0b00000000000000101000000000000000, + 0b00000000000000010100000000000000 + ), + }, + { + input: [all_1, pattern, u32(14), u32(4)], + expected: V( + 0b11111111111111001011111111111111, + 0b11111111111111001111111111111111, + 0b11111111111111101011111111111111, + 0b11111111111111010111111111111111 + ), + }, + { + input: [all_0, pattern, u32(14), u32(3)], + expected: V( + 0b00000000000000001000000000000000, + 0b00000000000000001100000000000000, + 0b00000000000000001000000000000000, + 0b00000000000000010100000000000000 + ), + }, + { + input: [all_1, pattern, u32(14), u32(3)], + expected: V( + 0b11111111111111101011111111111111, + 0b11111111111111101111111111111111, + 0b11111111111111101011111111111111, + 0b11111111111111110111111111111111 + ), + }, + { + input: [all_0, pattern, u32(18), u32(3)], + expected: V( + 0b00000000000010000000000000000000, + 0b00000000000011000000000000000000, + 0b00000000000010000000000000000000, + 0b00000000000101000000000000000000 + ), + }, + { + input: [all_1, pattern, u32(18), u32(3)], + expected: V( + 0b11111111111010111111111111111111, + 0b11111111111011111111111111111111, + 0b11111111111010111111111111111111, + 0b11111111111101111111111111111111 + ), + }, + { + input: [pattern, all_0, u32(1), u32(31)], + expected: V( + 0b00000000000000000000000000000000, + 0b00000000000000000000000000000001, + 0b00000000000000000000000000000000, + 0b00000000000000000000000000000001 + ), + }, + { + input: [pattern, all_1, u32(1), u32(31)], + expected: V( + 0b11111111111111111111111111111110, + 0b11111111111111111111111111111111, + 0b11111111111111111111111111111110, + 0b11111111111111111111111111111111 + ), + }, + { + input: [pattern, all_0, u32(14), u32(18)], + expected: V( + 0b00000000000000000010010100100010, + 0b00000000000000000000110011100011, + 0b00000000000000000010101010101010, + 0b00000000000000000001010101010101 + ), + }, + { + input: [pattern, all_1, u32(14), u32(18)], + expected: V( + 0b11111111111111111110010100100010, + 0b11111111111111111100110011100011, + 0b11111111111111111110101010101010, + 0b11111111111111111101010101010101 + ), + }, + { + input: [pattern, all_0, u32(14), u32(7)], + expected: V( + 0b10001001010000000010010100100010, + 0b11001110001000000000110011100011, + 0b10101010101000000010101010101010, + 0b01010101010000000001010101010101 + ), + }, + { + input: [pattern, all_1, u32(14), u32(7)], + expected: V( + 0b10001001010111111110010100100010, + 0b11001110001111111100110011100011, + 0b10101010101111111110101010101010, + 0b01010101010111111101010101010101 + ), + }, + { + input: [pattern, all_0, u32(14), u32(4)], + expected: V( + 0b10001001010100000010010100100010, + 0b11001110001100000000110011100011, + 0b10101010101010000010101010101010, + 0b01010101010101000001010101010101 + ), + }, + { + input: [pattern, all_1, u32(14), u32(4)], + expected: V( + 0b10001001010100111110010100100010, + 0b11001110001100111100110011100011, + 0b10101010101010111110101010101010, + 0b01010101010101111101010101010101 + ), + }, + { + input: [pattern, all_0, u32(14), u32(3)], + expected: V( + 0b10001001010100100010010100100010, + 0b11001110001100100000110011100011, + 0b10101010101010100010101010101010, + 0b01010101010101000001010101010101 + ), + }, + { + input: [pattern, all_1, u32(14), u32(3)], + expected: V( + 0b10001001010100111110010100100010, + 0b11001110001100111100110011100011, + 0b10101010101010111110101010101010, + 0b01010101010101011101010101010101 + ), + }, + { + input: [pattern, all_0, u32(18), u32(3)], + expected: V( + 0b10001001010000100010010100100010, + 0b11001110001000111000110011100011, + 0b10101010101000101010101010101010, + 0b01010101010000010101010101010101 + ), + }, + { + input: [pattern, all_1, u32(18), u32(3)], + expected: V( + 0b10001001010111100010010100100010, + 0b11001110001111111000110011100011, + 0b10101010101111101010101010101010, + 0b01010101010111010101010101010101 + ), + }, + { + input: [pattern, pattern, u32(18), u32(3)], + expected: V( + 0b10001001010010100010010100100010, + 0b11001110001011111000110011100011, + 0b10101010101010101010101010101010, + 0b01010101010101010101010101010101 + ), + }, + { + input: [pattern, pattern, u32(14), u32(7)], + expected: V( + 0b10001001010010001010010100100010, + 0b11001110001110001100110011100011, + 0b10101010101010101010101010101010, + 0b01010101010101010101010101010101 + ), + }, + + // Zero count + { input: [pattern, all_1, u32(0), u32(0)], expected: pattern }, + { input: [pattern, all_1, u32(1), u32(0)], expected: pattern }, + { input: [pattern, all_1, u32(2), u32(0)], expected: pattern }, + { input: [pattern, all_1, u32(31), u32(0)], expected: pattern }, + { input: [pattern, all_1, u32(32), u32(0)], expected: pattern }, + { input: [pattern, all_1, u32(0), u32(0)], expected: pattern }, + ]; + + if (t.params.inputSource !== 'const') { + cases.push( + ...[ + // Start overflow + { input: [all_0, pattern, u32(50), u32(3)], expected: all_0 }, + { input: [all_1, pattern, u32(50), u32(3)], expected: all_1 }, + { input: [pattern, pattern, u32(50), u32(3)], expected: pattern }, + + // End overflow + { input: [all_0, pattern, u32(0), u32(99)], expected: pattern }, + { input: [all_1, pattern, u32(0), u32(99)], expected: pattern }, + { input: [all_0, low_1, u32(31), u32(99)], expected: high_1 }, + { + input: [pattern, pattern, u32(20), u32(99)], + expected: V( + 0b01010010001000100010010100100010, + 0b11001110001100111000110011100011, + 0b10101010101010101010101010101010, + 0b01010101010101010101010101010101 + ), + }, + ] + ); + } + + await run(t, builtin('insertBits'), [T, T, TypeU32, TypeU32], T, cfg, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts new file mode 100644 index 0000000000..ec79aa3807 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts @@ -0,0 +1,63 @@ +export const description = ` +Execution tests for the 'inverseSqrt' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn inverseSqrt(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { inverseSqrtInterval } from '../../../../../util/f32_interval.js'; +import { biasedRange, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('inverseSqrt', { + f32: () => { + return generateUnaryToF32IntervalCases( + [ + // 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', + inverseSqrtInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('inverseSqrt'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts new file mode 100644 index 0000000000..b443aef4b6 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts @@ -0,0 +1,95 @@ +export const description = ` +Execution tests for the 'ldexp' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> + +K is AbstractInt, i32 +I is K or vecN<K>, where + I is a scalar if T is a scalar, or a vector when T is a vector + +@const fn ldexp(e1: T ,e2: I ) -> T +Returns e1 * 2^e2. Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { kValue } from '../../../../../util/constants.js'; +import { f32, i32, TypeF32, TypeI32 } from '../../../../../util/conversion.js'; +import { ldexpInterval } from '../../../../../util/f32_interval.js'; +import { + biasedRange, + fullF32Range, + fullI32Range, + quantizeToF32, + quantizeToI32, +} from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, Case, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +const makeCase = (e1: number, e2: number): Case => { + // Due to the heterogeneous types of the params to ldexp (f32 & i32), + // makeBinaryToF32IntervalCase cannot be used here. + e1 = quantizeToF32(e1); + e2 = quantizeToI32(e2); + const expected = ldexpInterval(e1, e2); + return { input: [f32(e1), i32(e2)], expected }; +}; + +export const d = makeCaseCache('ldexp', { + f32_non_const: () => { + const cases: Array<Case> = []; + fullF32Range().forEach(e1 => { + fullI32Range().forEach(e2 => { + cases.push(makeCase(e1, e2)); + }); + }); + return cases; + }, + f32_const: () => { + const cases: Array<Case> = []; + fullF32Range().forEach(e1 => { + biasedRange(-128, 128, 10).forEach(e2 => { + const val = e1 * Math.pow(2, e2); + if (val >= kValue.f32.negative.min && val <= kValue.f32.positive.max) { + cases.push(makeCase(e1, e2)); + } + }); + }); + return cases; + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +` + ) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('ldexp'), [TypeF32, TypeI32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts new file mode 100644 index 0000000000..1fb54f6fa0 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts @@ -0,0 +1,107 @@ +export const description = ` +Execution tests for the 'length' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn length(e: T ) -> f32 +Returns the length of e (e.g. abs(e) if T is a scalar, or sqrt(e[0]^2 + e[1]^2 + ...) if T is a vector). +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { lengthInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range, vectorF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { + allInputSources, + generateUnaryToF32IntervalCases, + generateVectorToF32IntervalCases, + run, +} from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('length', { + f32: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', lengthInterval); + }, + f32_vec2_const: () => { + return generateVectorToF32IntervalCases(vectorF32Range(2), 'f32-only', lengthInterval); + }, + f32_vec2_non_const: () => { + return generateVectorToF32IntervalCases(vectorF32Range(2), 'unfiltered', lengthInterval); + }, + f32_vec3_const: () => { + return generateVectorToF32IntervalCases(vectorF32Range(3), 'f32-only', lengthInterval); + }, + f32_vec3_non_const: () => { + return generateVectorToF32IntervalCases(vectorF32Range(3), 'unfiltered', lengthInterval); + }, + f32_vec4_const: () => { + return generateVectorToF32IntervalCases(vectorF32Range(4), 'f32-only', lengthInterval); + }, + f32_vec4_non_const: () => { + return generateVectorToF32IntervalCases(vectorF32Range(4), 'unfiltered', lengthInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('length'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f32_vec2') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec2s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' + ); + await run(t, builtin('length'), [TypeVec(2, TypeF32)], TypeF32, t.params, cases); + }); + +g.test('f32_vec3') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec3s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' + ); + await run(t, builtin('length'), [TypeVec(3, TypeF32)], TypeF32, t.params, cases); + }); + +g.test('f32_vec4') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec4s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' + ); + await run(t, builtin('length'), [TypeVec(4, TypeF32)], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts new file mode 100644 index 0000000000..6d2b16f44e --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts @@ -0,0 +1,71 @@ +export const description = ` +Execution tests for the 'log' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn log(e: T ) -> T +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 { kValue } from '../../../../../util/constants.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { logInterval } from '../../../../../util/f32_interval.js'; +import { biasedRange, fullF32Range, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +// log's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] } +const inputs = [ + ...linearRange(kValue.f32.positive.min, 0.5, 20), + ...linearRange(0.5, 2.0, 20), + ...biasedRange(2.0, 2 ** 32, 1000), + ...fullF32Range(), +]; + +export const d = makeCaseCache('log', { + f32_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'f32-only', logInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'unfiltered', logInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +f32 tests + +TODO(#792): Decide what the ground-truth is for these tests. [1] +` + ) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('log'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts new file mode 100644 index 0000000000..4425fb7b1a --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts @@ -0,0 +1,71 @@ +export const description = ` +Execution tests for the 'log2' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn log2(e: T ) -> T +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 { kValue } from '../../../../../util/constants.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { log2Interval } from '../../../../../util/f32_interval.js'; +import { biasedRange, fullF32Range, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +// log2's accuracy is defined in three regions { [0, 0.5), [0.5, 2.0], (2.0, +∞] } +const inputs = [ + ...linearRange(kValue.f32.positive.min, 0.5, 20), + ...linearRange(0.5, 2.0, 20), + ...biasedRange(2.0, 2 ** 32, 1000), + ...fullF32Range(), +]; + +export const d = makeCaseCache('log2', { + f32_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'f32-only', log2Interval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(inputs, 'unfiltered', log2Interval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +f32 tests + +TODO(#792): Decide what the ground-truth is for these tests. [1] +` + ) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('log2'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts new file mode 100644 index 0000000000..7b8446b176 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts @@ -0,0 +1,123 @@ +export const description = ` +Execution tests for the 'max' builtin function + +S is AbstractInt, i32, or u32 +T is S or vecN<S> +@const fn max(e1: T ,e2: T) -> T +Returns e2 if e1 is less than e2, and e1 otherwise. Component-wise when T is a vector. + +S is AbstractFloat, f32, f16 +T is vecN<S> +@const fn max(e1: T ,e2: T) -> T +Returns e2 if e1 is less than e2, and e1 otherwise. +If one operand is a NaN, the other is returned. +If both operands are NaNs, a NaN is returned. +Component-wise when T is a vector. + +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { i32, TypeF32, TypeI32, TypeU32, u32 } from '../../../../../util/conversion.js'; +import { maxInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, Case, generateBinaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +/** Generate set of max test cases from list of interesting values */ +function generateTestCases( + values: Array<number>, + makeCase: (x: number, y: number) => Case +): Array<Case> { + const cases = new Array<Case>(); + values.forEach(e => { + values.forEach(f => { + cases.push(makeCase(e, f)); + }); + }); + return cases; +} + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('max', { + f32: () => { + return generateBinaryToF32IntervalCases( + fullF32Range(), + fullF32Range(), + 'unfiltered', + maxInterval + ); + }, +}); + +g.test('abstract_int') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`abstract int tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const makeCase = (x: number, y: number): Case => { + return { input: [u32(x), u32(y)], expected: u32(Math.max(x, y)) }; + }; + + const test_values: Array<number> = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff]; + const cases = generateTestCases(test_values, makeCase); + + await run(t, builtin('max'), [TypeU32, TypeU32], TypeU32, t.params, cases); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const makeCase = (x: number, y: number): Case => { + return { input: [i32(x), i32(y)], expected: i32(Math.max(x, y)) }; + }; + + const test_values: Array<number> = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000]; + const cases = generateTestCases(test_values, makeCase); + + await run(t, builtin('max'), [TypeI32, TypeI32], TypeI32, t.params, cases); + }); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('max'), [TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts new file mode 100644 index 0000000000..3b3e612a66 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts @@ -0,0 +1,122 @@ +export const description = ` +Execution tests for the 'min' builtin function + +S is AbstractInt, i32, or u32 +T is S or vecN<S> +@const fn min(e1: T ,e2: T) -> T +Returns e1 if e1 is less than e2, and e2 otherwise. Component-wise when T is a vector. + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn min(e1: T ,e2: T) -> T +Returns e2 if e2 is less than e1, and e1 otherwise. +If one operand is a NaN, the other is returned. +If both operands are NaNs, a NaN is returned. +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { i32, TypeF32, TypeI32, TypeU32, u32 } from '../../../../../util/conversion.js'; +import { minInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, Case, generateBinaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('min', { + f32: () => { + return generateBinaryToF32IntervalCases( + fullF32Range(), + fullF32Range(), + 'unfiltered', + minInterval + ); + }, +}); + +/** Generate set of min test cases from list of interesting values */ +function generateTestCases( + values: Array<number>, + makeCase: (x: number, y: number) => Case +): Array<Case> { + const cases = new Array<Case>(); + values.forEach(e => { + values.forEach(f => { + cases.push(makeCase(e, f)); + }); + }); + return cases; +} + +g.test('abstract_int') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`abstract int tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const makeCase = (x: number, y: number): Case => { + return { input: [u32(x), u32(y)], expected: u32(Math.min(x, y)) }; + }; + + const test_values: Array<number> = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff]; + const cases = generateTestCases(test_values, makeCase); + + await run(t, builtin('min'), [TypeU32, TypeU32], TypeU32, t.params, cases); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const makeCase = (x: number, y: number): Case => { + return { input: [i32(x), i32(y)], expected: i32(Math.min(x, y)) }; + }; + + const test_values: Array<number> = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000]; + const cases = generateTestCases(test_values, makeCase); + + await run(t, builtin('min'), [TypeI32, TypeI32], TypeI32, t.params, cases); + }); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('min'), [TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts new file mode 100644 index 0000000000..8f0959ed08 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts @@ -0,0 +1,93 @@ +export const description = ` +Execution tests for the 'mix' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn mix(e1: T, e2: T, e3: T) -> T +Returns the linear blend of e1 and e2 (e.g. e1*(1-e3)+e2*e3). Component-wise when T is a vector. + +T is AbstractFloat, f32, or f16 +T2 is vecN<T> +@const fn mix(e1: T2, e2: T2, e3: T) -> T2 +Returns the component-wise linear blend of e1 and e2, using scalar blending factor e3 for each component. +Same as mix(e1,e2,T2(e3)). + +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { mixIntervals } from '../../../../../util/f32_interval.js'; +import { sparseF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateTernaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('mix', { + f32_const: () => { + return generateTernaryToF32IntervalCases( + sparseF32Range(), + sparseF32Range(), + sparseF32Range(), + 'f32-only', + ...mixIntervals + ); + }, + f32_non_const: () => { + return generateTernaryToF32IntervalCases( + sparseF32Range(), + sparseF32Range(), + sparseF32Range(), + 'unfiltered', + ...mixIntervals + ); + }, +}); + +g.test('matching_abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests with matching params`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('matching_f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 test with matching third param`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('mix'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('matching_f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests with matching third param`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('nonmatching_abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests with vector params and scalar third param`) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); + +g.test('nonmatching_f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests with vector params and scalar third param`) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); + +g.test('monmatching_f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests with vector params and scalar third param`) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts new file mode 100644 index 0000000000..f82a297b6a --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts @@ -0,0 +1,356 @@ +export const description = ` +Execution tests for the 'modf' builtin function + +T is f32 or f16 +@const fn modf(e:T) -> result_struct +Splits |e| into fractional and whole number parts. +The whole part is (|e| % 1.0), and the fractional part is |e| minus the whole part. +Returns the result_struct for the given type. + +S is f32 or f16 +T is vecN<S> +@const fn modf(e:T) -> result_struct +Splits the components of |e| into fractional and whole number parts. +The |i|'th component of the whole and fractional parts equal the whole and fractional parts of modf(e[i]). +Returns the result_struct for the given type. + +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { f32, toVector, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { modfInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range, quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, Case, ExpressionBuilder, run } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +/* @returns an ExpressionBuilder that evaluates modf and returns .whole from the result structure */ +function wholeBuilder(): ExpressionBuilder { + return value => `modf(${value}).whole`; +} + +/* @returns an ExpressionBuilder that evaluates modf and returns .fract from the result structure */ +function fractBuilder(): ExpressionBuilder { + return value => `modf(${value}).fract`; +} + +/* @returns a fract Case for a given vector input */ +function makeVectorCaseFract(v: number[]): Case { + v = v.map(quantizeToF32); + const fs = v.map(e => { + return modfInterval(e).fract; + }); + + return { input: toVector(v, f32), expected: fs }; +} + +/* @returns a fract Case for a given vector input */ +function makeVectorCaseWhole(v: number[]): Case { + v = v.map(quantizeToF32); + const ws = v.map(e => { + return modfInterval(e).whole; + }); + + return { input: toVector(v, f32), expected: ws }; +} + +export const d = makeCaseCache('modf', { + f32_fract: () => { + const makeCase = (n: number): Case => { + n = quantizeToF32(n); + return { input: f32(n), expected: modfInterval(n).fract }; + }; + return fullF32Range().map(makeCase); + }, + f32_whole: () => { + const makeCase = (n: number): Case => { + n = quantizeToF32(n); + return { input: f32(n), expected: modfInterval(n).whole }; + }; + return fullF32Range().map(makeCase); + }, + f32_vec2_fract: () => { + return vectorF32Range(2).map(makeVectorCaseFract); + }, + f32_vec2_whole: () => { + return vectorF32Range(2).map(makeVectorCaseWhole); + }, + f32_vec3_fract: () => { + return vectorF32Range(3).map(makeVectorCaseFract); + }, + f32_vec3_whole: () => { + return vectorF32Range(3).map(makeVectorCaseWhole); + }, + f32_vec4_fract: () => { + return vectorF32Range(4).map(makeVectorCaseFract); + }, + f32_vec4_whole: () => { + return vectorF32Range(4).map(makeVectorCaseWhole); + }, +}); + +g.test('f32_fract') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is f32 + +struct __modf_result_f32 { + fract : f32, // fractional part + whole : f32 // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get('f32_fract'); + await run(t, fractBuilder(), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f32_whole') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is f32 + +struct __modf_result_f32 { + fract : f32, // fractional part + whole : f32 // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get('f32_whole'); + await run(t, wholeBuilder(), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f32_vec2_fract') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec2<f32> + +struct __modf_result_vec2_f32 { + fract : vec2<f32>, // fractional part + whole : vec2<f32> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get('f32_vec2_fract'); + await run(t, fractBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases); + }); + +g.test('f32_vec2_whole') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec2<f32> + +struct __modf_result_vec2_f32 { + fract : vec2<f32>, // fractional part + whole : vec2<f32> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get('f32_vec2_whole'); + await run(t, wholeBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases); + }); + +g.test('f32_vec3_fract') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec3<f32> + +struct __modf_result_vec3_f32 { + fract : vec3<f32>, // fractional part + whole : vec3<f32> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get('f32_vec3_fract'); + await run(t, fractBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases); + }); + +g.test('f32_vec3_whole') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec3<f32> + +struct __modf_result_vec3_f32 { + fract : vec3<f32>, // fractional part + whole : vec3<f32> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get('f32_vec3_whole'); + await run(t, wholeBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases); + }); + +g.test('f32_vec4_fract') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec4<f32> + +struct __modf_result_vec4_f32 { + fract : vec4<f32>, // fractional part + whole : vec4<f32> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get('f32_vec4_fract'); + await run(t, fractBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases); + }); + +g.test('f32_vec4_whole') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec4<f32> + +struct __modf_result_vec4_f32 { + fract : vec4<f32>, // fractional part + whole : vec4<f32> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get('f32_vec4_whole'); + await run(t, wholeBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases); + }); + +g.test('f16_fract') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is f16 + +struct __modf_result_f16 { + fract : f16, // fractional part + whole : f16 // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('f16_whole') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is f16 + +struct __modf_result_f16 { + fract : f16, // fractional part + whole : f16 // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('f16_vec2_fract') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec2<f16> + +struct __modf_result_vec2_f16 { + fract : vec2<f16>, // fractional part + whole : vec2<f16> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('f16_vec2_whole') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec2<f16> + +struct __modf_result_vec2_f16 { + fract : vec2<f16>, // fractional part + whole : vec2<f16> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('f16_vec3_fract') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec3<f16> + +struct __modf_result_vec3_f16 { + fract : vec3<f16>, // fractional part + whole : vec3<f16> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('f16_vec3_whole') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec3<f16> + +struct __modf_result_vec3_f16 { + fract : vec3<f16>, // fractional part + whole : vec3<f16> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('f16_vec4_fract') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec4<f16> + +struct __modf_result_vec4_f16 { + fract : vec4<f16>, // fractional part + whole : vec4<f16> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); + +g.test('f16_vec4_whole') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +T is vec4<f16> + +struct __modf_result_vec4_f16 { + fract : vec4<f16>, // fractional part + whole : vec4<f16> // whole part +} +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts new file mode 100644 index 0000000000..d1900795ad --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts @@ -0,0 +1,89 @@ +export const description = ` +Execution tests for the 'normalize' builtin function + +T is AbstractFloat, f32, or f16 +@const fn normalize(e: vecN<T> ) -> vecN<T> +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, TypeVec } from '../../../../../util/conversion.js'; +import { normalizeInterval } from '../../../../../util/f32_interval.js'; +import { vectorF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateVectorToVectorCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('normalize', { + f32_vec2_const: () => { + return generateVectorToVectorCases(vectorF32Range(2), 'f32-only', normalizeInterval); + }, + f32_vec2_non_const: () => { + return generateVectorToVectorCases(vectorF32Range(2), 'unfiltered', normalizeInterval); + }, + f32_vec3_const: () => { + return generateVectorToVectorCases(vectorF32Range(3), 'f32-only', normalizeInterval); + }, + f32_vec3_non_const: () => { + return generateVectorToVectorCases(vectorF32Range(3), 'unfiltered', normalizeInterval); + }, + f32_vec4_const: () => { + return generateVectorToVectorCases(vectorF32Range(4), 'f32-only', normalizeInterval); + }, + f32_vec4_non_const: () => { + return generateVectorToVectorCases(vectorF32Range(4), 'unfiltered', normalizeInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32_vec2') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec2s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' + ); + await run(t, builtin('normalize'), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases); + }); + +g.test('f32_vec3') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec3s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' + ); + await run(t, builtin('normalize'), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases); + }); + +g.test('f32_vec4') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec4s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' + ); + await run(t, builtin('normalize'), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts new file mode 100644 index 0000000000..790e54720c --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts @@ -0,0 +1,88 @@ +export const description = ` +Converts two floating point values to half-precision floating point numbers, and then combines them into one u32 value. +Component e[i] of the input is converted to a IEEE-754 binary16 value, +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, fullF32Range, quantizeToF32 } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, Case, run } from '../../expression.js'; + +import { builtin } from './builtin.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(fullF32Range(), fullF32Range(), true); + }, + f32_non_const: () => { + return generateCases(fullF32Range(), fullF32Range(), false); + }, +}); + +g.test('pack') + .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions') + .desc( + ` +@const fn pack2x16float(e: vec2<f32>) -> u32 +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('pack2x16float'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts new file mode 100644 index 0000000000..54bb21f6c6 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts @@ -0,0 +1,55 @@ +export const description = ` +Converts two normalized floating point values to 16-bit signed integers, and then combines them into one u32 value. +Component e[i] of the input is converted to a 16-bit twos complement integer value +⌊ 0.5 + 32767 × min(1, max(-1, e[i])) ⌋ 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 { kValue } from '../../../../../util/constants.js'; +import { + f32, + pack2x16snorm, + TypeF32, + TypeU32, + TypeVec, + u32, + vec2, +} from '../../../../../util/conversion.js'; +import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; +import { allInputSources, Case, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('pack') + .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions') + .desc( + ` +@const fn pack2x16snorm(e: vec2<f32>) -> u32 +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const makeCase = (x: number, y: number): Case => { + x = quantizeToF32(x); + y = quantizeToF32(y); + return { input: [vec2(f32(x), f32(y))], expected: u32(pack2x16snorm(x, y)) }; + }; + + // Returns a value normalized to [-1, 1]. + const normalizeF32 = (n: number): number => { + return n / kValue.f32.positive.max; + }; + + const cases: Array<Case> = vectorF32Range(2).flatMap(v => { + return [ + makeCase(...(v as [number, number])), + makeCase(...(v.map(normalizeF32) as [number, number])), + ]; + }); + + await run(t, builtin('pack2x16snorm'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts new file mode 100644 index 0000000000..a875a9c7e1 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts @@ -0,0 +1,55 @@ +export const description = ` +Converts two normalized floating point values to 16-bit unsigned integers, and then combines them into one u32 value. +Component e[i] of the input is converted to a 16-bit unsigned integer value +⌊ 0.5 + 65535 × min(1, max(0, e[i])) ⌋ 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 { kValue } from '../../../../../util/constants.js'; +import { + f32, + pack2x16unorm, + TypeF32, + TypeU32, + TypeVec, + u32, + vec2, +} from '../../../../../util/conversion.js'; +import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; +import { allInputSources, Case, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('pack') + .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions') + .desc( + ` +@const fn pack2x16unorm(e: vec2<f32>) -> u32 +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const makeCase = (x: number, y: number): Case => { + x = quantizeToF32(x); + y = quantizeToF32(y); + return { input: [vec2(f32(x), f32(y))], expected: u32(pack2x16unorm(x, y)) }; + }; + + // Returns a value normalized to [0, 1]. + const normalizeF32 = (n: number): number => { + return n > 0 ? n / kValue.f32.positive.max : n / kValue.f32.negative.min; + }; + + const cases: Array<Case> = vectorF32Range(2).flatMap(v => { + return [ + makeCase(...(v as [number, number])), + makeCase(...(v.map(normalizeF32) as [number, number])), + ]; + }); + + await run(t, builtin('pack2x16unorm'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts new file mode 100644 index 0000000000..de0463e9fc --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts @@ -0,0 +1,60 @@ +export const description = ` +Converts four normalized floating point values to 8-bit signed integers, and then combines them into one u32 value. +Component e[i] of the input is converted to an 8-bit twos complement integer value +⌊ 0.5 + 127 × min(1, max(-1, e[i])) ⌋ which is then placed in +bits 8 × i through 8 × i + 7 of the result. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { kValue } from '../../../../../util/constants.js'; +import { + f32, + pack4x8snorm, + Scalar, + TypeF32, + TypeU32, + TypeVec, + u32, + vec4, +} from '../../../../../util/conversion.js'; +import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; +import { allInputSources, Case, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('pack') + .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions') + .desc( + ` +@const fn pack4x8snorm(e: vec4<f32>) -> u32 +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const makeCase = (vals: [number, number, number, number]): Case => { + const vals_f32 = new Array<Scalar>(4) as [Scalar, Scalar, Scalar, Scalar]; + for (const idx in vals) { + vals[idx] = quantizeToF32(vals[idx]); + vals_f32[idx] = f32(vals[idx]); + } + + return { input: [vec4(...vals_f32)], expected: u32(pack4x8snorm(...vals)) }; + }; + + // Returns a value normalized to [-1, 1]. + const normalizeF32 = (n: number): number => { + return n / kValue.f32.positive.max; + }; + + const cases: Array<Case> = vectorF32Range(4).flatMap(v => { + return [ + makeCase(v as [number, number, number, number]), + makeCase(v.map(normalizeF32) as [number, number, number, number]), + ]; + }); + + await run(t, builtin('pack4x8snorm'), [TypeVec(4, TypeF32)], TypeU32, t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts new file mode 100644 index 0000000000..b670e92fbb --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts @@ -0,0 +1,60 @@ +export const description = ` +Converts four normalized floating point values to 8-bit unsigned integers, and then combines them into one u32 value. +Component e[i] of the input is converted to an 8-bit unsigned integer value +⌊ 0.5 + 255 × min(1, max(0, e[i])) ⌋ which is then placed in +bits 8 × i through 8 × i + 7 of the result. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { kValue } from '../../../../../util/constants.js'; +import { + f32, + pack4x8unorm, + Scalar, + TypeF32, + TypeU32, + TypeVec, + u32, + vec4, +} from '../../../../../util/conversion.js'; +import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; +import { allInputSources, Case, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('pack') + .specURL('https://www.w3.org/TR/WGSL/#pack-builtin-functions') + .desc( + ` +@const fn pack4x8unorm(e: vec4<f32>) -> u32 +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const makeCase = (vals: [number, number, number, number]): Case => { + const vals_f32 = new Array<Scalar>(4) as [Scalar, Scalar, Scalar, Scalar]; + for (const idx in vals) { + vals[idx] = quantizeToF32(vals[idx]); + vals_f32[idx] = f32(vals[idx]); + } + + return { input: [vec4(...vals_f32)], expected: u32(pack4x8unorm(...vals)) }; + }; + + // Returns a value normalized to [0, 1]. + const normalizeF32 = (n: number): number => { + return n > 0 ? n / kValue.f32.positive.max : n / kValue.f32.negative.min; + }; + + const cases: Array<Case> = vectorF32Range(4).flatMap(v => { + return [ + makeCase(v as [number, number, number, number]), + makeCase(v.map(normalizeF32) as [number, number, number, number]), + ]; + }); + + await run(t, builtin('pack4x8unorm'), [TypeVec(4, TypeF32)], TypeU32, t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts new file mode 100644 index 0000000000..8f2b3c9a54 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts @@ -0,0 +1,66 @@ +export const description = ` +Execution tests for the 'pow' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn pow(e1: T ,e2: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { powInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateBinaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('pow', { + f32_const: () => { + return generateBinaryToF32IntervalCases( + fullF32Range(), + fullF32Range(), + 'f32-only', + powInterval + ); + }, + f32_non_const: () => { + return generateBinaryToF32IntervalCases( + fullF32Range(), + fullF32Range(), + 'unfiltered', + powInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('pow'), [TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts new file mode 100644 index 0000000000..2c07f640be --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts @@ -0,0 +1,70 @@ +export const description = ` +Execution tests for the 'quantizeToF16' builtin function + +T is f32 or vecN<f32> +@const fn quantizeToF16(e: T ) -> T +Quantizes a 32-bit floating point value e as if e were converted to a IEEE 754 +binary16 value, and then converted back to a IEEE 754 binary32 value. +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 { quantizeToF16Interval } from '../../../../../util/f32_interval.js'; +import { fullF16Range, fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('quantizeToF16', { + f32_const: () => { + return generateUnaryToF32IntervalCases( + [ + kValue.f16.negative.min, + kValue.f16.negative.max, + kValue.f16.subnormal.negative.min, + kValue.f16.subnormal.negative.max, + kValue.f16.subnormal.positive.min, + kValue.f16.subnormal.positive.max, + kValue.f16.positive.min, + kValue.f16.positive.max, + ...fullF16Range(), + ], + 'f32-only', + quantizeToF16Interval + ); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases( + [ + kValue.f16.negative.min, + kValue.f16.negative.max, + kValue.f16.subnormal.negative.min, + kValue.f16.subnormal.negative.max, + kValue.f16.subnormal.positive.min, + kValue.f16.subnormal.positive.max, + kValue.f16.positive.min, + kValue.f16.positive.max, + ...fullF32Range(), + ], + 'unfiltered', + quantizeToF16Interval + ); + }, +}); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('quantizeToF16'), [TypeF32], TypeF32, t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts new file mode 100644 index 0000000000..9b53db29b9 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts @@ -0,0 +1,54 @@ +export const description = ` +Execution tests for the 'radians' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn radians(e1: T ) -> T +Converts degrees to radians, approximating e1 * π / 180. +Component-wise when T is a vector +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { radiansInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('radians', { + f32: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', radiansInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('radians'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts new file mode 100644 index 0000000000..a2865e1a85 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts @@ -0,0 +1,137 @@ +export const description = ` +Execution tests for the 'reflect' builtin function + +T is vecN<AbstractFloat>, vecN<f32>, or vecN<f16> +@const fn reflect(e1: T, e2: T ) -> T +For the incident vector e1 and surface orientation e2, returns the reflection +direction e1-2*dot(e2,e1)*e2. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { reflectInterval } from '../../../../../util/f32_interval.js'; +import { sparseVectorF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateVectorPairToVectorCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('reflect', { + f32_vec2_const: () => { + return generateVectorPairToVectorCases( + sparseVectorF32Range(2), + sparseVectorF32Range(2), + 'f32-only', + reflectInterval + ); + }, + f32_vec2_non_const: () => { + return generateVectorPairToVectorCases( + sparseVectorF32Range(2), + sparseVectorF32Range(2), + 'unfiltered', + reflectInterval + ); + }, + f32_vec3_const: () => { + return generateVectorPairToVectorCases( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + 'f32-only', + reflectInterval + ); + }, + f32_vec3_non_const: () => { + return generateVectorPairToVectorCases( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + 'unfiltered', + reflectInterval + ); + }, + f32_vec4_const: () => { + return generateVectorPairToVectorCases( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + 'f32-only', + reflectInterval + ); + }, + f32_vec4_non_const: () => { + return generateVectorPairToVectorCases( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + 'unfiltered', + reflectInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); + +g.test('f32_vec2') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec2s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' + ); + await run( + t, + builtin('reflect'), + [TypeVec(2, TypeF32), TypeVec(2, TypeF32)], + TypeVec(2, TypeF32), + t.params, + cases + ); + }); + +g.test('f32_vec3') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec3s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' + ); + await run( + t, + builtin('reflect'), + [TypeVec(3, TypeF32), TypeVec(3, TypeF32)], + TypeVec(3, TypeF32), + t.params, + cases + ); + }); + +g.test('f32_vec4') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec4s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' + ); + await run( + t, + builtin('reflect'), + [TypeVec(4, TypeF32), TypeVec(4, TypeF32)], + TypeVec(4, TypeF32), + t.params, + cases + ); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts new file mode 100644 index 0000000000..96a0fbe02a --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts @@ -0,0 +1,196 @@ +export const description = ` +Execution tests for the 'refract' builtin function + +T is vecN<I> +I is AbstractFloat, f32, or f16 +@const fn refract(e1: T ,e2: T ,e3: I ) -> T +For the incident vector e1 and surface normal e2, and the ratio of indices of +refraction e3, let k = 1.0 -e3*e3* (1.0 - dot(e2,e1) * dot(e2,e1)). +If k < 0.0, returns the refraction vector 0.0, otherwise return the refraction +vector e3*e1- (e3* dot(e2,e1) + sqrt(k)) *e2. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { f32, TypeF32, TypeVec, Vector } from '../../../../../util/conversion.js'; +import { refractInterval } from '../../../../../util/f32_interval.js'; +import { sparseVectorF32Range, quantizeToF32, sparseF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, Case, IntervalFilter, run } from '../../expression.js'; + +import { builtin } from './builtin.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 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 makeCaseF32(i: number[], s: number[], r: number, check: IntervalFilter): Case | undefined { + i = i.map(quantizeToF32); + s = s.map(quantizeToF32); + r = quantizeToF32(r); + + const i_f32 = i.map(f32); + const s_f32 = s.map(f32); + const r_f32 = f32(r); + + const vectors = refractInterval(i, s, r); + if (check === 'f32-only' && vectors.some(e => !e.isFinite())) { + return undefined; + } + + return { + input: [new Vector(i_f32), new Vector(s_f32), r_f32], + expected: refractInterval(i, s, r), + }; +} + +/** + * @returns an array of Cases for `refract` + * @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 generateCasesF32( + param_is: number[][], + param_ss: number[][], + param_rs: 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 makeCaseF32(i, s, r, check); + }); + }); + }) + .filter((c): c is Case => c !== undefined); +} + +export const d = makeCaseCache('refract', { + f32_vec2_const: () => { + return generateCasesF32( + sparseVectorF32Range(2), + sparseVectorF32Range(2), + sparseF32Range(), + 'f32-only' + ); + }, + f32_vec2_non_const: () => { + return generateCasesF32( + sparseVectorF32Range(2), + sparseVectorF32Range(2), + sparseF32Range(), + 'unfiltered' + ); + }, + f32_vec3_const: () => { + return generateCasesF32( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + sparseF32Range(), + 'f32-only' + ); + }, + f32_vec3_non_const: () => { + return generateCasesF32( + sparseVectorF32Range(3), + sparseVectorF32Range(3), + sparseF32Range(), + 'unfiltered' + ); + }, + f32_vec4_const: () => { + return generateCasesF32( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + sparseF32Range(), + 'f32-only' + ); + }, + f32_vec4_non_const: () => { + return generateCasesF32( + sparseVectorF32Range(4), + sparseVectorF32Range(4), + sparseF32Range(), + 'unfiltered' + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); + +g.test('f32_vec2') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec2s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' + ); + await run( + t, + builtin('refract'), + [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeF32], + TypeVec(2, TypeF32), + t.params, + cases + ); + }); + +g.test('f32_vec3') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec3s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' + ); + await run( + t, + builtin('refract'), + [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeF32], + TypeVec(3, TypeF32), + t.params, + cases + ); + }); + +g.test('f32_vec4') + .specURL('https://www.w3.org/TR/WGSL/#numeric-builtin-functions') + .desc(`f32 tests using vec4s`) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get( + t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' + ); + await run( + t, + builtin('refract'), + [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeF32], + TypeVec(4, TypeF32), + t.params, + cases + ); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => u.combine('inputSource', allInputSources).combine('vectorize', [2, 3, 4] as const)) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts new file mode 100644 index 0000000000..6acb359822 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts @@ -0,0 +1,250 @@ +export const description = ` +Execution tests for the 'reversBits' builtin function + +S is i32, u32 +T is S or vecN<S> +@const fn reverseBits(e: T ) -> T +Reverses the bits in e: The bit at position k of the result equals the bit at position 31-k of e. +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeU32, u32Bits, TypeI32, i32Bits } from '../../../../../util/conversion.js'; +import { allInputSources, Config, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('u32') + .specURL('https://www.w3.org/TR/WGSL/#integer-builtin-functions') + .desc(`u32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + // prettier-ignore + await run(t, builtin('reverseBits'), [TypeU32], TypeU32, cfg, [ + // Zero + { input: u32Bits(0b00000000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000000) }, + + // One + { input: u32Bits(0b00000000000000000000000000000001), expected: u32Bits(0b10000000000000000000000000000000) }, + + // 0's after leading 1 + { input: u32Bits(0b00000000000000000000000000000010), expected: u32Bits(0b01000000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000000100), expected: u32Bits(0b00100000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000001000), expected: u32Bits(0b00010000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000010000), expected: u32Bits(0b00001000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000100000), expected: u32Bits(0b00000100000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000001000000), expected: u32Bits(0b00000010000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000010000000), expected: u32Bits(0b00000001000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000100000000), expected: u32Bits(0b00000000100000000000000000000000) }, + { input: u32Bits(0b00000000000000000000001000000000), expected: u32Bits(0b00000000010000000000000000000000) }, + { input: u32Bits(0b00000000000000000000010000000000), expected: u32Bits(0b00000000001000000000000000000000) }, + { input: u32Bits(0b00000000000000000000100000000000), expected: u32Bits(0b00000000000100000000000000000000) }, + { input: u32Bits(0b00000000000000000001000000000000), expected: u32Bits(0b00000000000010000000000000000000) }, + { input: u32Bits(0b00000000000000000010000000000000), expected: u32Bits(0b00000000000001000000000000000000) }, + { input: u32Bits(0b00000000000000000100000000000000), expected: u32Bits(0b00000000000000100000000000000000) }, + { input: u32Bits(0b00000000000000001000000000000000), expected: u32Bits(0b00000000000000010000000000000000) }, + { input: u32Bits(0b00000000000000010000000000000000), expected: u32Bits(0b00000000000000001000000000000000) }, + { input: u32Bits(0b00000000000000100000000000000000), expected: u32Bits(0b00000000000000000100000000000000) }, + { input: u32Bits(0b00000000000001000000000000000000), expected: u32Bits(0b00000000000000000010000000000000) }, + { input: u32Bits(0b00000000000010000000000000000000), expected: u32Bits(0b00000000000000000001000000000000) }, + { input: u32Bits(0b00000000000100000000000000000000), expected: u32Bits(0b00000000000000000000100000000000) }, + { input: u32Bits(0b00000000001000000000000000000000), expected: u32Bits(0b00000000000000000000010000000000) }, + { input: u32Bits(0b00000000010000000000000000000000), expected: u32Bits(0b00000000000000000000001000000000) }, + { input: u32Bits(0b00000000100000000000000000000000), expected: u32Bits(0b00000000000000000000000100000000) }, + { input: u32Bits(0b00000001000000000000000000000000), expected: u32Bits(0b00000000000000000000000010000000) }, + { input: u32Bits(0b00000010000000000000000000000000), expected: u32Bits(0b00000000000000000000000001000000) }, + { input: u32Bits(0b00000100000000000000000000000000), expected: u32Bits(0b00000000000000000000000000100000) }, + { input: u32Bits(0b00001000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000010000) }, + { input: u32Bits(0b00010000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000001000) }, + { input: u32Bits(0b00100000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000100) }, + { input: u32Bits(0b01000000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000010) }, + { input: u32Bits(0b10000000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000001) }, + + // 1's after leading 1 + { input: u32Bits(0b00000000000000000000000000000011), expected: u32Bits(0b11000000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000000111), expected: u32Bits(0b11100000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000001111), expected: u32Bits(0b11110000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000011111), expected: u32Bits(0b11111000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000111111), expected: u32Bits(0b11111100000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000001111111), expected: u32Bits(0b11111110000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000011111111), expected: u32Bits(0b11111111000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000111111111), expected: u32Bits(0b11111111100000000000000000000000) }, + { input: u32Bits(0b00000000000000000000001111111111), expected: u32Bits(0b11111111110000000000000000000000) }, + { input: u32Bits(0b00000000000000000000011111111111), expected: u32Bits(0b11111111111000000000000000000000) }, + { input: u32Bits(0b00000000000000000000111111111111), expected: u32Bits(0b11111111111100000000000000000000) }, + { input: u32Bits(0b00000000000000000001111111111111), expected: u32Bits(0b11111111111110000000000000000000) }, + { input: u32Bits(0b00000000000000000011111111111111), expected: u32Bits(0b11111111111111000000000000000000) }, + { input: u32Bits(0b00000000000000000111111111111111), expected: u32Bits(0b11111111111111100000000000000000) }, + { input: u32Bits(0b00000000000000001111111111111111), expected: u32Bits(0b11111111111111110000000000000000) }, + { input: u32Bits(0b00000000000000011111111111111111), expected: u32Bits(0b11111111111111111000000000000000) }, + { input: u32Bits(0b00000000000000111111111111111111), expected: u32Bits(0b11111111111111111100000000000000) }, + { input: u32Bits(0b00000000000001111111111111111111), expected: u32Bits(0b11111111111111111110000000000000) }, + { input: u32Bits(0b00000000000011111111111111111111), expected: u32Bits(0b11111111111111111111000000000000) }, + { input: u32Bits(0b00000000000111111111111111111111), expected: u32Bits(0b11111111111111111111100000000000) }, + { input: u32Bits(0b00000000001111111111111111111111), expected: u32Bits(0b11111111111111111111110000000000) }, + { input: u32Bits(0b00000000011111111111111111111111), expected: u32Bits(0b11111111111111111111111000000000) }, + { input: u32Bits(0b00000000111111111111111111111111), expected: u32Bits(0b11111111111111111111111100000000) }, + { input: u32Bits(0b00000001111111111111111111111111), expected: u32Bits(0b11111111111111111111111110000000) }, + { input: u32Bits(0b00000011111111111111111111111111), expected: u32Bits(0b11111111111111111111111111000000) }, + { input: u32Bits(0b00000111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111100000) }, + { input: u32Bits(0b00001111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111110000) }, + { input: u32Bits(0b00011111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111111000) }, + { input: u32Bits(0b00111111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111111100) }, + { input: u32Bits(0b01111111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111111110) }, + { input: u32Bits(0b11111111111111111111111111111111), expected: u32Bits(0b11111111111111111111111111111111) }, + + // random after leading 1 + { input: u32Bits(0b00000000000000000000000000000110), expected: u32Bits(0b01100000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000001101), expected: u32Bits(0b10110000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000011101), expected: u32Bits(0b10111000000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000000111001), expected: u32Bits(0b10011100000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000001101111), expected: u32Bits(0b11110110000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000011111111), expected: u32Bits(0b11111111000000000000000000000000) }, + { input: u32Bits(0b00000000000000000000000111101111), expected: u32Bits(0b11110111100000000000000000000000) }, + { input: u32Bits(0b00000000000000000000001111111111), expected: u32Bits(0b11111111110000000000000000000000) }, + { input: u32Bits(0b00000000000000000000011111110001), expected: u32Bits(0b10001111111000000000000000000000) }, + { input: u32Bits(0b00000000000000000000111011011101), expected: u32Bits(0b10111011011100000000000000000000) }, + { input: u32Bits(0b00000000000000000001101101111111), expected: u32Bits(0b11111110110110000000000000000000) }, + { input: u32Bits(0b00000000000000000011111111011111), expected: u32Bits(0b11111011111111000000000000000000) }, + { input: u32Bits(0b00000000000000000101111001110101), expected: u32Bits(0b10101110011110100000000000000000) }, + { input: u32Bits(0b00000000000000001101111011110111), expected: u32Bits(0b11101111011110110000000000000000) }, + { input: u32Bits(0b00000000000000011111111111110011), expected: u32Bits(0b11001111111111111000000000000000) }, + { input: u32Bits(0b00000000000000111111111110111111), expected: u32Bits(0b11111101111111111100000000000000) }, + { input: u32Bits(0b00000000000001111111011111111111), expected: u32Bits(0b11111111111011111110000000000000) }, + { input: u32Bits(0b00000000000011111111111111111111), expected: u32Bits(0b11111111111111111111000000000000) }, + { input: u32Bits(0b00000000000111110101011110111111), expected: u32Bits(0b11111101111010101111100000000000) }, + { input: u32Bits(0b00000000001111101111111111110111), expected: u32Bits(0b11101111111111110111110000000000) }, + { input: u32Bits(0b00000000011111111111010000101111), expected: u32Bits(0b11110100001011111111111000000000) }, + { input: u32Bits(0b00000000111111111111001111111011), expected: u32Bits(0b11011111110011111111111100000000) }, + { input: u32Bits(0b00000001111111011111101111111111), expected: u32Bits(0b11111111110111111011111110000000) }, + { input: u32Bits(0b00000011101011111011110111111011), expected: u32Bits(0b11011111101111011111010111000000) }, + { input: u32Bits(0b00000111111110111111111111111111), expected: u32Bits(0b11111111111111111101111111100000) }, + { input: u32Bits(0b00001111000000011011011110111111), expected: u32Bits(0b11111101111011011000000011110000) }, + { input: u32Bits(0b00011110101111011111111111111111), expected: u32Bits(0b11111111111111111011110101111000) }, + { input: u32Bits(0b00110110111111100111111110111101), expected: u32Bits(0b10111101111111100111111101101100) }, + { input: u32Bits(0b01010111111101111111011111011111), expected: u32Bits(0b11111011111011111110111111101010) }, + { input: u32Bits(0b11100010011110101101101110101111), expected: u32Bits(0b11110101110110110101111001000111) }, + ]); + }); + +g.test('i32') + .specURL('https://www.w3.org/TR/2021/WD-WGSL-20210929/#integer-builtin-functions') + .desc(`i32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cfg: Config = t.params; + // prettier-ignore + await run(t, builtin('reverseBits'), [TypeI32], TypeI32, cfg, [ + // Zero + { input: i32Bits(0b00000000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000000) }, + + // One + { input: i32Bits(0b00000000000000000000000000000001), expected: i32Bits(0b10000000000000000000000000000000) }, + + // 0's after leading 1 + { input: i32Bits(0b00000000000000000000000000000010), expected: i32Bits(0b01000000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000000100), expected: i32Bits(0b00100000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000001000), expected: i32Bits(0b00010000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000010000), expected: i32Bits(0b00001000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000100000), expected: i32Bits(0b00000100000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000001000000), expected: i32Bits(0b00000010000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000010000000), expected: i32Bits(0b00000001000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000100000000), expected: i32Bits(0b00000000100000000000000000000000) }, + { input: i32Bits(0b00000000000000000000001000000000), expected: i32Bits(0b00000000010000000000000000000000) }, + { input: i32Bits(0b00000000000000000000010000000000), expected: i32Bits(0b00000000001000000000000000000000) }, + { input: i32Bits(0b00000000000000000000100000000000), expected: i32Bits(0b00000000000100000000000000000000) }, + { input: i32Bits(0b00000000000000000001000000000000), expected: i32Bits(0b00000000000010000000000000000000) }, + { input: i32Bits(0b00000000000000000010000000000000), expected: i32Bits(0b00000000000001000000000000000000) }, + { input: i32Bits(0b00000000000000000100000000000000), expected: i32Bits(0b00000000000000100000000000000000) }, + { input: i32Bits(0b00000000000000001000000000000000), expected: i32Bits(0b00000000000000010000000000000000) }, + { input: i32Bits(0b00000000000000010000000000000000), expected: i32Bits(0b00000000000000001000000000000000) }, + { input: i32Bits(0b00000000000000100000000000000000), expected: i32Bits(0b00000000000000000100000000000000) }, + { input: i32Bits(0b00000000000001000000000000000000), expected: i32Bits(0b00000000000000000010000000000000) }, + { input: i32Bits(0b00000000000010000000000000000000), expected: i32Bits(0b00000000000000000001000000000000) }, + { input: i32Bits(0b00000000000100000000000000000000), expected: i32Bits(0b00000000000000000000100000000000) }, + { input: i32Bits(0b00000000001000000000000000000000), expected: i32Bits(0b00000000000000000000010000000000) }, + { input: i32Bits(0b00000000010000000000000000000000), expected: i32Bits(0b00000000000000000000001000000000) }, + { input: i32Bits(0b00000000100000000000000000000000), expected: i32Bits(0b00000000000000000000000100000000) }, + { input: i32Bits(0b00000001000000000000000000000000), expected: i32Bits(0b00000000000000000000000010000000) }, + { input: i32Bits(0b00000010000000000000000000000000), expected: i32Bits(0b00000000000000000000000001000000) }, + { input: i32Bits(0b00000100000000000000000000000000), expected: i32Bits(0b00000000000000000000000000100000) }, + { input: i32Bits(0b00001000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000010000) }, + { input: i32Bits(0b00010000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000001000) }, + { input: i32Bits(0b00100000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000100) }, + { input: i32Bits(0b01000000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000010) }, + { input: i32Bits(0b10000000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000001) }, + + // 1's after leading 1 + { input: i32Bits(0b00000000000000000000000000000011), expected: i32Bits(0b11000000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000000111), expected: i32Bits(0b11100000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000001111), expected: i32Bits(0b11110000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000011111), expected: i32Bits(0b11111000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000111111), expected: i32Bits(0b11111100000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000001111111), expected: i32Bits(0b11111110000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000011111111), expected: i32Bits(0b11111111000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000111111111), expected: i32Bits(0b11111111100000000000000000000000) }, + { input: i32Bits(0b00000000000000000000001111111111), expected: i32Bits(0b11111111110000000000000000000000) }, + { input: i32Bits(0b00000000000000000000011111111111), expected: i32Bits(0b11111111111000000000000000000000) }, + { input: i32Bits(0b00000000000000000000111111111111), expected: i32Bits(0b11111111111100000000000000000000) }, + { input: i32Bits(0b00000000000000000001111111111111), expected: i32Bits(0b11111111111110000000000000000000) }, + { input: i32Bits(0b00000000000000000011111111111111), expected: i32Bits(0b11111111111111000000000000000000) }, + { input: i32Bits(0b00000000000000000111111111111111), expected: i32Bits(0b11111111111111100000000000000000) }, + { input: i32Bits(0b00000000000000001111111111111111), expected: i32Bits(0b11111111111111110000000000000000) }, + { input: i32Bits(0b00000000000000011111111111111111), expected: i32Bits(0b11111111111111111000000000000000) }, + { input: i32Bits(0b00000000000000111111111111111111), expected: i32Bits(0b11111111111111111100000000000000) }, + { input: i32Bits(0b00000000000001111111111111111111), expected: i32Bits(0b11111111111111111110000000000000) }, + { input: i32Bits(0b00000000000011111111111111111111), expected: i32Bits(0b11111111111111111111000000000000) }, + { input: i32Bits(0b00000000000111111111111111111111), expected: i32Bits(0b11111111111111111111100000000000) }, + { input: i32Bits(0b00000000001111111111111111111111), expected: i32Bits(0b11111111111111111111110000000000) }, + { input: i32Bits(0b00000000011111111111111111111111), expected: i32Bits(0b11111111111111111111111000000000) }, + { input: i32Bits(0b00000000111111111111111111111111), expected: i32Bits(0b11111111111111111111111100000000) }, + { input: i32Bits(0b00000001111111111111111111111111), expected: i32Bits(0b11111111111111111111111110000000) }, + { input: i32Bits(0b00000011111111111111111111111111), expected: i32Bits(0b11111111111111111111111111000000) }, + { input: i32Bits(0b00000111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111100000) }, + { input: i32Bits(0b00001111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111110000) }, + { input: i32Bits(0b00011111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111111000) }, + { input: i32Bits(0b00111111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111111100) }, + { input: i32Bits(0b01111111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111111110) }, + { input: i32Bits(0b11111111111111111111111111111111), expected: i32Bits(0b11111111111111111111111111111111) }, + + // random after leading 1 + { input: i32Bits(0b00000000000000000000000000000110), expected: i32Bits(0b01100000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000001101), expected: i32Bits(0b10110000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000011101), expected: i32Bits(0b10111000000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000000111001), expected: i32Bits(0b10011100000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000001101111), expected: i32Bits(0b11110110000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000011111111), expected: i32Bits(0b11111111000000000000000000000000) }, + { input: i32Bits(0b00000000000000000000000111101111), expected: i32Bits(0b11110111100000000000000000000000) }, + { input: i32Bits(0b00000000000000000000001111111111), expected: i32Bits(0b11111111110000000000000000000000) }, + { input: i32Bits(0b00000000000000000000011111110001), expected: i32Bits(0b10001111111000000000000000000000) }, + { input: i32Bits(0b00000000000000000000111011011101), expected: i32Bits(0b10111011011100000000000000000000) }, + { input: i32Bits(0b00000000000000000001101101111111), expected: i32Bits(0b11111110110110000000000000000000) }, + { input: i32Bits(0b00000000000000000011111111011111), expected: i32Bits(0b11111011111111000000000000000000) }, + { input: i32Bits(0b00000000000000000101111001110101), expected: i32Bits(0b10101110011110100000000000000000) }, + { input: i32Bits(0b00000000000000001101111011110111), expected: i32Bits(0b11101111011110110000000000000000) }, + { input: i32Bits(0b00000000000000011111111111110011), expected: i32Bits(0b11001111111111111000000000000000) }, + { input: i32Bits(0b00000000000000111111111110111111), expected: i32Bits(0b11111101111111111100000000000000) }, + { input: i32Bits(0b00000000000001111111011111111111), expected: i32Bits(0b11111111111011111110000000000000) }, + { input: i32Bits(0b00000000000011111111111111111111), expected: i32Bits(0b11111111111111111111000000000000) }, + { input: i32Bits(0b00000000000111110101011110111111), expected: i32Bits(0b11111101111010101111100000000000) }, + { input: i32Bits(0b00000000001111101111111111110111), expected: i32Bits(0b11101111111111110111110000000000) }, + { input: i32Bits(0b00000000011111111111010000101111), expected: i32Bits(0b11110100001011111111111000000000) }, + { input: i32Bits(0b00000000111111111111001111111011), expected: i32Bits(0b11011111110011111111111100000000) }, + { input: i32Bits(0b00000001111111011111101111111111), expected: i32Bits(0b11111111110111111011111110000000) }, + { input: i32Bits(0b00000011101011111011110111111011), expected: i32Bits(0b11011111101111011111010111000000) }, + { input: i32Bits(0b00000111111110111111111111111111), expected: i32Bits(0b11111111111111111101111111100000) }, + { input: i32Bits(0b00001111000000011011011110111111), expected: i32Bits(0b11111101111011011000000011110000) }, + { input: i32Bits(0b00011110101111011111111111111111), expected: i32Bits(0b11111111111111111011110101111000) }, + { input: i32Bits(0b00110110111111100111111110111101), expected: i32Bits(0b10111101111111100111111101101100) }, + { input: i32Bits(0b01010111111101111111011111011111), expected: i32Bits(0b11111011111011111110111111101010) }, + { input: i32Bits(0b11100010011110101101101110101111), expected: i32Bits(0b11110101110110110101111001000111) }, + ]); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts new file mode 100644 index 0000000000..609ae629ad --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts @@ -0,0 +1,56 @@ +export const description = ` +Execution tests for the 'round' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn round(e: T) -> T +Result is the integer k nearest to e, as a floating point value. +When e lies halfway between integers k and k+1, the result is k when k is even, +and k+1 when k is odd. +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { roundInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('round', { + f32: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', roundInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('round'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts new file mode 100644 index 0000000000..d1c83bbdd4 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts @@ -0,0 +1,61 @@ +export const description = ` +Execution tests for the 'saturate' builtin function + +S is AbstractFloat, f32, or f16 +T is S or vecN<S> +@const fn saturate(e: T) -> T +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 { TypeF32 } from '../../../../../util/conversion.js'; +import { saturateInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('saturate', { + f32: () => { + return generateUnaryToF32IntervalCases( + [ + // Non-clamped values + ...linearRange(0.0, 1.0, 100), + ...fullF32Range(), + ], + 'unfiltered', + saturateInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('saturate'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts new file mode 100644 index 0000000000..4e8c4c3799 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts @@ -0,0 +1,229 @@ +export const description = ` +Execution tests for the 'select' builtin function + +T is scalar, abstract numeric type, or vector +@const fn select(f: T, t: T, cond: bool) -> T +Returns t when cond is true, and f otherwise. + +T is scalar or abstract numeric type +@const fn select(f: vecN<T>, t: vecN<T>, cond: vecN<bool>) -> vecN<T> +Component-wise selection. Result component i is evaluated as select(f[i],t[i],cond[i]). +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { + VectorType, + TypeVec, + TypeBool, + TypeF32, + TypeI32, + TypeU32, + f32, + i32, + u32, + False, + True, + bool, + vec2, + vec3, + vec4, +} from '../../../../../util/conversion.js'; +import { run, CaseList, allInputSources } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +function makeBool(n: number) { + return bool((n & 1) === 1); +} + +type scalarKind = 'b' | 'f' | 'i' | 'u'; + +const dataType = { + b: { + type: TypeBool, + constructor: makeBool, + }, + f: { + type: TypeF32, + constructor: f32, + }, + i: { + type: TypeI32, + constructor: i32, + }, + u: { + type: TypeU32, + constructor: u32, + }, +}; + +g.test('scalar') + .specURL('https://www.w3.org/TR/WGSL/#logical-builtin-functions') + .desc(`scalar tests`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('component', ['b', 'f', 'i', 'u'] as const) + .combine('overload', ['scalar', 'vec2', 'vec3', 'vec4'] as const) + ) + .fn(async t => { + const componentType = dataType[t.params.component as scalarKind].type; + const cons = dataType[t.params.component as scalarKind].constructor; + + // Create the scalar values that will be selected from, either as scalars + // or vectors. + // + // Each boolean will select between c[k] and c[k+4]. Those values must + // always compare as different. The tricky case is boolean, where the parity + // has to be different, i.e. c[k]-c[k+4] must be odd. + const c = [0, 1, 2, 3, 5, 6, 7, 8].map(i => cons(i)); + // Now form vectors that will have different components from each other. + const v2a = vec2(c[0], c[1]); + const v2b = vec2(c[4], c[5]); + const v3a = vec3(c[0], c[1], c[2]); + const v3b = vec3(c[4], c[5], c[6]); + const v4a = vec4(c[0], c[1], c[2], c[3]); + const v4b = vec4(c[4], c[5], c[6], c[7]); + + const overloads = { + scalar: { + type: componentType, + cases: [ + { input: [c[0], c[1], False], expected: c[0] }, + { input: [c[0], c[1], True], expected: c[1] }, + ], + }, + vec2: { + type: TypeVec(2, componentType), + cases: [ + { input: [v2a, v2b, False], expected: v2a }, + { input: [v2a, v2b, True], expected: v2b }, + ], + }, + vec3: { + type: TypeVec(3, componentType), + cases: [ + { input: [v3a, v3b, False], expected: v3a }, + { input: [v3a, v3b, True], expected: v3b }, + ], + }, + vec4: { + type: TypeVec(4, componentType), + cases: [ + { input: [v4a, v4b, False], expected: v4a }, + { input: [v4a, v4b, True], expected: v4b }, + ], + }, + }; + const overload = overloads[t.params.overload]; + + await run( + t, + builtin('select'), + [overload.type, overload.type, TypeBool], + overload.type, + t.params, + overload.cases + ); + }); + +g.test('vector') + .specURL('https://www.w3.org/TR/WGSL/#logical-builtin-functions') + .desc(`vector tests`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('component', ['b', 'f', 'i', 'u'] as const) + .combine('overload', ['vec2', 'vec3', 'vec4'] as const) + ) + .fn(async t => { + const componentType = dataType[t.params.component as scalarKind].type; + const cons = dataType[t.params.component as scalarKind].constructor; + + // Create the scalar values that will be selected from. + // + // Each boolean will select between c[k] and c[k+4]. Those values must + // always compare as different. The tricky case is boolean, where the parity + // has to be different, i.e. c[k]-c[k+4] must be odd. + const c = [0, 1, 2, 3, 5, 6, 7, 8].map(i => cons(i)); + const T = True; + const F = False; + + let tests: { dataType: VectorType; boolType: VectorType; cases: CaseList }; + + switch (t.params.overload) { + case 'vec2': { + const a = vec2(c[0], c[1]); + const b = vec2(c[4], c[5]); + tests = { + dataType: TypeVec(2, componentType), + boolType: TypeVec(2, TypeBool), + cases: [ + { input: [a, b, vec2(F, F)], expected: vec2(a.x, a.y) }, + { input: [a, b, vec2(F, T)], expected: vec2(a.x, b.y) }, + { input: [a, b, vec2(T, F)], expected: vec2(b.x, a.y) }, + { input: [a, b, vec2(T, T)], expected: vec2(b.x, b.y) }, + ], + }; + break; + } + case 'vec3': { + const a = vec3(c[0], c[1], c[2]); + const b = vec3(c[4], c[5], c[6]); + tests = { + dataType: TypeVec(3, componentType), + boolType: TypeVec(3, TypeBool), + cases: [ + { input: [a, b, vec3(F, F, F)], expected: vec3(a.x, a.y, a.z) }, + { input: [a, b, vec3(F, F, T)], expected: vec3(a.x, a.y, b.z) }, + { input: [a, b, vec3(F, T, F)], expected: vec3(a.x, b.y, a.z) }, + { input: [a, b, vec3(F, T, T)], expected: vec3(a.x, b.y, b.z) }, + { input: [a, b, vec3(T, F, F)], expected: vec3(b.x, a.y, a.z) }, + { input: [a, b, vec3(T, F, T)], expected: vec3(b.x, a.y, b.z) }, + { input: [a, b, vec3(T, T, F)], expected: vec3(b.x, b.y, a.z) }, + { input: [a, b, vec3(T, T, T)], expected: vec3(b.x, b.y, b.z) }, + ], + }; + break; + } + case 'vec4': { + const a = vec4(c[0], c[1], c[2], c[3]); + const b = vec4(c[4], c[5], c[6], c[7]); + tests = { + dataType: TypeVec(4, componentType), + boolType: TypeVec(4, TypeBool), + cases: [ + { input: [a, b, vec4(F, F, F, F)], expected: vec4(a.x, a.y, a.z, a.w) }, + { input: [a, b, vec4(F, F, F, T)], expected: vec4(a.x, a.y, a.z, b.w) }, + { input: [a, b, vec4(F, F, T, F)], expected: vec4(a.x, a.y, b.z, a.w) }, + { input: [a, b, vec4(F, F, T, T)], expected: vec4(a.x, a.y, b.z, b.w) }, + { input: [a, b, vec4(F, T, F, F)], expected: vec4(a.x, b.y, a.z, a.w) }, + { input: [a, b, vec4(F, T, F, T)], expected: vec4(a.x, b.y, a.z, b.w) }, + { input: [a, b, vec4(F, T, T, F)], expected: vec4(a.x, b.y, b.z, a.w) }, + { input: [a, b, vec4(F, T, T, T)], expected: vec4(a.x, b.y, b.z, b.w) }, + { input: [a, b, vec4(T, F, F, F)], expected: vec4(b.x, a.y, a.z, a.w) }, + { input: [a, b, vec4(T, F, F, T)], expected: vec4(b.x, a.y, a.z, b.w) }, + { input: [a, b, vec4(T, F, T, F)], expected: vec4(b.x, a.y, b.z, a.w) }, + { input: [a, b, vec4(T, F, T, T)], expected: vec4(b.x, a.y, b.z, b.w) }, + { input: [a, b, vec4(T, T, F, F)], expected: vec4(b.x, b.y, a.z, a.w) }, + { input: [a, b, vec4(T, T, F, T)], expected: vec4(b.x, b.y, a.z, b.w) }, + { input: [a, b, vec4(T, T, T, F)], expected: vec4(b.x, b.y, b.z, a.w) }, + { input: [a, b, vec4(T, T, T, T)], expected: vec4(b.x, b.y, b.z, b.w) }, + ], + }; + break; + } + } + + await run( + t, + builtin('select'), + [tests.dataType, tests.dataType, tests.boolType], + tests.dataType, + t.params, + tests.cases + ); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts new file mode 100644 index 0000000000..66f812dd66 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts @@ -0,0 +1,53 @@ +export const description = ` +Execution tests for the 'sign' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn sign(e: T ) -> T +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 { TypeF32 } from '../../../../../util/conversion.js'; +import { signInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('sign', { + f32: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', signInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('sign'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts new file mode 100644 index 0000000000..802977b56f --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts @@ -0,0 +1,67 @@ +export const description = ` +Execution tests for the 'sin' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn sin(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { sinInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('sin', { + f32: () => { + return generateUnaryToF32IntervalCases( + [ + // Well defined accuracy range + ...linearRange(-Math.PI, Math.PI, 1000), + ...fullF32Range(), + ], + 'unfiltered', + sinInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc( + ` +f32 tests + +TODO(#792): Decide what the ground-truth is for these tests. [1] +` + ) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('sin'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts new file mode 100644 index 0000000000..8f59ce1e4c --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts @@ -0,0 +1,56 @@ +export const description = ` +Execution tests for the 'sinh' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn sinh(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { sinhInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('sinh', { + f32_const: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'f32-only', sinhInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', sinhInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('sinh'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts new file mode 100644 index 0000000000..2874f83262 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts @@ -0,0 +1,70 @@ +export const description = ` +Execution tests for the 'smoothstep' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn smoothstep(low: T , high: T , x: T ) -> T +Returns the smooth Hermite interpolation between 0 and 1. +Component-wise when T is a vector. +For scalar T, the result is t * t * (3.0 - 2.0 * t), where t = clamp((x - low) / (high - low), 0.0, 1.0). +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { smoothStepInterval } from '../../../../../util/f32_interval.js'; +import { sparseF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateTernaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('smoothstep', { + f32_const: () => { + return generateTernaryToF32IntervalCases( + sparseF32Range(), + sparseF32Range(), + sparseF32Range(), + 'f32-only', + smoothStepInterval + ); + }, + f32_non_const: () => { + return generateTernaryToF32IntervalCases( + sparseF32Range(), + sparseF32Range(), + sparseF32Range(), + 'unfiltered', + smoothStepInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('smoothstep'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts new file mode 100644 index 0000000000..48980078a3 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts @@ -0,0 +1,56 @@ +export const description = ` +Execution tests for the 'sqrt' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn sqrt(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { sqrtInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('sqrt', { + f32_const: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'f32-only', sqrtInterval); + }, + f32_non_const: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', sqrtInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); + await run(t, builtin('sqrt'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts new file mode 100644 index 0000000000..24226c9463 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts @@ -0,0 +1,85 @@ +export const description = ` +Execution tests for the 'step' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn step(edge: T ,x: T ) -> T +Returns 1.0 if edge ≤ x, and 0.0 otherwise. Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { anyOf } from '../../../../../util/compare.js'; +import { f32, TypeF32 } from '../../../../../util/conversion.js'; +import { stepInterval, toF32Interval } from '../../../../../util/f32_interval.js'; +import { fullF32Range, quantizeToF32 } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, Case, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('step', { + f32: () => { + const zeroInterval = toF32Interval(0); + const oneInterval = toF32Interval(1); + + // stepInterval's return value isn't always interpreted as an acceptance + // interval, so makeBinaryToF32IntervalCase cannot be used here. + // See the comment block on stepInterval for more details + const makeCase = (edge: number, x: number): Case => { + edge = quantizeToF32(edge); + x = quantizeToF32(x); + const expected = stepInterval(edge, x); + + // [0, 0], [1, 1], or [-∞, +∞] cases + if (expected.isPoint() || !expected.isFinite()) { + return { input: [f32(edge), f32(x)], expected }; + } + + // [0, 1] case + return { + input: [f32(edge), f32(x)], + expected: anyOf(zeroInterval, oneInterval), + }; + }; + + const range = fullF32Range(); + const cases: Array<Case> = []; + range.forEach(edge => { + range.forEach(x => { + cases.push(makeCase(edge, x)); + }); + }); + + return cases; + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('step'), [TypeF32, TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/storageBarrier.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/storageBarrier.spec.ts new file mode 100644 index 0000000000..f376db4472 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/storageBarrier.spec.ts @@ -0,0 +1,38 @@ +export const description = ` +'storageBarrier' affects memory and atomic operations in the storage address space. + +All synchronization functions execute a control barrier with Acquire/Release memory ordering. +That is, all synchronization functions, and affected memory and atomic operations are ordered +in program order relative to the synchronization function. Additionally, the affected memory +and atomic operations program-ordered before the synchronization function must be visible to +all other threads in the workgroup before any affected memory or atomic operation program-ordered +after the synchronization function is executed by a member of the workgroup. All synchronization +functions use the Workgroup memory scope. All synchronization functions have a Workgroup +execution scope. + +All synchronization functions must only be used in the compute shader stage. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#sync-builtin-functions') + .desc( + ` +All synchronization functions must only be used in the compute shader stage. +` + ) + .params(u => u.combine('stage', ['vertex', 'fragment', 'compute'] as const)) + .unimplemented(); + +g.test('barrier') + .specURL('https://www.w3.org/TR/WGSL/#sync-builtin-functions') + .desc( + ` +fn storageBarrier() +` + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts new file mode 100644 index 0000000000..c226cdd851 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts @@ -0,0 +1,61 @@ +export const description = ` +Execution tests for the 'tan' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn tan(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { tanInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range, linearRange } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('tan', { + f32: () => { + return generateUnaryToF32IntervalCases( + [ + // Defined accuracy range + ...linearRange(-Math.PI, Math.PI, 100), + ...fullF32Range(), + ], + 'unfiltered', + tanInterval + ); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('tan'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts new file mode 100644 index 0000000000..d031331a04 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts @@ -0,0 +1,53 @@ +export const description = ` +Execution tests for the 'tanh' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn tanh(e: T ) -> T +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 } from '../../../../../util/conversion.js'; +import { tanhInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('tanh', { + f32: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', tanhInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('tanh'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts new file mode 100644 index 0000000000..0ecb9964cf --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureDimension.spec.ts @@ -0,0 +1,160 @@ +export const description = ` +Execution tests for the 'textureDimension' builtin function + +The dimensions of the texture in texels. +For textures based on cubes, the results are the dimensions of each face of the cube. +Cube faces are square, so the x and y components of the result are equal. +If level is outside the range [0, textureNumLevels(t)) then any valid value for the return type may be returned. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('sampled') + .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') + .desc( + ` +T: f32, i32, u32 + +fn textureDimensions(t: texture_1d<T>) -> u32 +fn textureDimensions(t: texture_1d<T>, level: u32) -> u32 +fn textureDimensions(t: texture_2d<T>) -> vec2<u32> +fn textureDimensions(t: texture_2d<T>, level: u32) -> vec2<u32> +fn textureDimensions(t: texture_2d_array<T>) -> vec2<u32> +fn textureDimensions(t: texture_2d_array<T>, level: u32) -> vec2<u32> +fn textureDimensions(t: texture_3d<T>) -> vec3<u32> +fn textureDimensions(t: texture_3d<T>, level: u32) -> vec3<u32> +fn textureDimensions(t: texture_cube<T>) -> vec2<u32> +fn textureDimensions(t: texture_cube<T>, level: u32) -> vec2<u32> +fn textureDimensions(t: texture_cube_array<T>) -> vec2<u32> +fn textureDimensions(t: texture_cube_array<T>, level: u32) -> vec2<u32> +fn textureDimensions(t: texture_multisampled_2d<T>)-> vec2<u32> + +Parameters: + * t: the sampled texture + * level: + - The mip level, with level 0 containing a full size version of the texture. + - If omitted, the dimensions of level 0 are returned. +` + ) + .params(u => + u + .combine('texture_type', [ + 'texture_1d', + 'texture_2d', + 'texture_2d_array', + 'texture_3d', + 'texture_cube', + 'texture_cube_array', + 'texture_multisampled_2d', + ] as const) + .beginSubcases() + .combine('sampled_type', ['f32-only', 'i32', 'u32'] as const) + .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) + ) + .unimplemented(); + +g.test('depth') + .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') + .desc( + ` +fn textureDimensions(t: texture_depth_2d) -> vec2<u32> +fn textureDimensions(t: texture_depth_2d, level: u32) -> vec2<u32> +fn textureDimensions(t: texture_depth_2d_array) -> vec2<u32> +fn textureDimensions(t: texture_depth_2d_array, level: u32) -> vec2<u32> +fn textureDimensions(t: texture_depth_cube) -> vec2<u32> +fn textureDimensions(t: texture_depth_cube, level: u32) -> vec2<u32> +fn textureDimensions(t: texture_depth_cube_array) -> vec2<u32> +fn textureDimensions(t: texture_depth_cube_array, level: u32) -> vec2<u32> +fn textureDimensions(t: texture_depth_multisampled_2d)-> vec2<u32> + +Parameters: + * t: the depth or multisampled texture + * level: + - The mip level, with level 0 containing a full size version of the texture. + - If omitted, the dimensions of level 0 are returned. +` + ) + .params(u => + u + .combine('texture_type', [ + 'texture_depth_2d', + 'texture_depth_2d_array', + 'texture_depth_cube', + 'texture_depth_cube_array', + 'texture_depth_multisampled_2d', + ]) + .beginSubcases() + .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) + ) + .unimplemented(); + +g.test('storage') + .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') + .desc( + ` +F: rgba8unorm + rgba8snorm + rgba8uint + rgba8sint + rgba16uint + rgba16sint + rgba16float + r32uint + r32sint + r32float + rg32uint + rg32sint + rg32float + rgba32uint + rgba32sint + rgba32float +A: read, write, read_write + +fn textureDimensions(t: texture_storage_1d<F,A>) -> u32 +fn textureDimensions(t: texture_storage_2d<F,A>) -> vec2<u32> +fn textureDimensions(t: texture_storage_2d_array<F,A>) -> vec2<u32> +fn textureDimensions(t: texture_storage_3d<F,A>) -> vec3<u32> + +Parameters: + * t: the storage texture +` + ) + .params(u => + u + .combine('texel_format', [ + 'rgba8unorm', + 'rgba8snorm', + 'rgba8uint', + 'rgba8sint', + 'rgba16uint', + 'rgba16sint', + 'rgba16float', + 'r32uint', + 'r32sint', + 'r32float', + 'rg32uint', + 'rg32sint', + 'rg32float', + 'rgba32uint', + 'rgba32sint', + 'rgba32float', + ] as const) + .beginSubcases() + .combine('access_mode', ['read', 'write', 'read_write'] as const) + ) + .unimplemented(); + +g.test('external') + .specURL('https://www.w3.org/TR/WGSL/#texturedimensions') + .desc( + ` +fn textureDimensions(t: texture_external) -> vec2<u32> + +Parameters: + * t: the external texture +` + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts new file mode 100644 index 0000000000..40b331efab --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGather.spec.ts @@ -0,0 +1,270 @@ +export const description = ` +Execution tests for the 'textureGather' builtin function + +A texture gather operation reads from a 2D, 2D array, cube, or cube array texture, computing a four-component vector as follows: + * Find the four texels that would be used in a sampling operation with linear filtering, from mip level 0: + - Use the specified coordinate, array index (when present), and offset (when present). + - The texels are adjacent, forming a square, when considering their texture space coordinates (u,v). + - Selected texels at the texture edge, cube face edge, or cube corners are handled as in ordinary texture sampling. + * For each texel, read one channel and convert it into a scalar value. + - For non-depth textures, a zero-based component parameter specifies the channel to use. + * If the texture format supports the specified channel, i.e. has more than component channels: + - Yield scalar value v[component] when the texel value is v. + * Otherwise: + - Yield 0.0 when component is 1 or 2. + - Yield 1.0 when component is 3 (the alpha channel). + - For depth textures, yield the texel value. (Depth textures only have one channel.) + * Yield the four-component vector, arranging scalars produced by the previous step into components according to the relative coordinates of the texels, as follows: + - Result component Relative texel coordinate + x (umin,vmax) + y (umax,vmax) + z (umax,vmin) + w (umin,vmin) +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +import { generateCoordBoundaries, generateOffsets } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('sampled_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegather') + .desc( + ` +C: i32, u32 +T: i32, u32, f32 + +fn textureGather(component: C, t: texture_2d<T>, s: sampler, coords: vec2<f32>) -> vec4<T> +fn textureGather(component: C, t: texture_2d<T>, s: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<T> + +Parameters: + * component: + - The index of the channel to read from the selected texels. + - When provided, the component expression must a creation-time expression (e.g. 1). + - Its value must be at least 0 and at most 3. Values outside of this range will result in a shader-creation error. + * t: The sampled texture to read from + * s: The sampler type + * coords: The texture coordinates + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('T', ['f32-only', 'i32', 'u32'] as const) + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('sampled_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegather') + .desc( + ` +C: i32, u32 +T: i32, u32, f32 + +fn textureGather(component: C, t: texture_cube<T>, s: sampler, coords: vec3<f32>) -> vec4<T> + +Parameters: + * component: + - The index of the channel to read from the selected texels. + - When provided, the component expression must a creation-time expression (e.g. 1). + - Its value must be at least 0 and at most 3. Values outside of this range will result in a shader-creation error. + * t: The sampled texture to read from + * s: The sampler type + * coords: The texture coordinates +` + ) + .paramsSubcasesOnly(u => + u + .combine('T', ['f32-only', 'i32', 'u32'] as const) + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(3)) + ) + .unimplemented(); + +g.test('sampled_array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegather') + .desc( + ` +C: i32, u32 +T: i32, u32, f32 + +fn textureGather(component: C, t: texture_2d_array<T>, s: sampler, coords: vec2<f32>, array_index: C) -> vec4<T> +fn textureGather(component: C, t: texture_2d_array<T>, s: sampler, coords: vec2<f32>, array_index: C, offset: vec2<i32>) -> vec4<T> + +Parameters: + * component: + - The index of the channel to read from the selected texels. + - When provided, the component expression must a creation-time expression (e.g. 1). + - Its value must be at least 0 and at most 3. Values outside of this range will result in a shader-creation error. + * t: The sampled texture to read from + * s: The sampler type + * coords: The texture coordinates + * array_index: The 0-based texture array index + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('T', ['f32-only', 'i32', 'u32'] as const) + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(2)) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('sampled_array_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegather') + .desc( + ` +C: i32, u32 +T: i32, u32, f32 + +fn textureGather(component: C, t: texture_cube_array<T>, s: sampler, coords: vec3<f32>, array_index: C) -> vec4<T> + +Parameters: + * component: + - The index of the channel to read from the selected texels. + - When provided, the component expression must a creation-time expression (e.g. 1). + - Its value must be at least 0 and at most 3. Values outside of this range will result in a shader-creation error. + * t: The sampled texture to read from + * s: The sampler type + * coords: The texture coordinates + * array_index: The 0-based texture array index +` + ) + .paramsSubcasesOnly( + u => + u + .combine('T', ['f32-only', 'i32', 'u32'] as const) + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(3)) + /* array_index not param'd as out-of-bounds is implementation specific */ + ) + .unimplemented(); + +g.test('depth_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegather') + .desc( + ` +fn textureGather(t: texture_depth_2d, s: sampler, coords: vec2<f32>) -> vec4<f32> +fn textureGather(t: texture_depth_2d, s: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t: The depth texture to read from + * s: The sampler type + * coords: The texture coordinates + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('depth_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegather') + .desc( + ` +fn textureGather(t: texture_depth_cube, s: sampler, coords: vec3<f32>) -> vec4<f32> + +Parameters: + * t: The depth texture to read from + * s: The sampler type + * coords: The texture coordinates +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + ) + .unimplemented(); + +g.test('depth_array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegather') + .desc( + ` +C: i32, u32 + +fn textureGather(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C) -> vec4<f32> +fn textureGather(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t: The depth texture to read from + * s: The sampler type + * coords: The texture coordinates + * array_index: The 0-based texture array index + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('coords', generateCoordBoundaries(2)) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('depth_array_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegather') + .desc( + ` +C: i32, u32 + +fn textureGather(t: texture_depth_cube_array, s: sampler, coords: vec3<f32>, array_index: C) -> vec4<f32> + +Parameters: + * t: The depth texture to read from + * s: The sampler type + * coords: The texture coordinates + * array_index: The 0-based texture array index +` + ) + .paramsSubcasesOnly( + u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('coords', generateCoordBoundaries(3)) + /* array_index not param'd as out-of-bounds is implementation specific */ + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts new file mode 100644 index 0000000000..c743883ce8 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureGatherCompare.spec.ts @@ -0,0 +1,134 @@ +export const description = ` +Execution tests for the 'textureGatherCompare' builtin function + +A texture gather compare operation performs a depth comparison on four texels in a depth texture and collects the results into a single vector, as follows: + * Find the four texels that would be used in a depth sampling operation with linear filtering, from mip level 0: + - Use the specified coordinate, array index (when present), and offset (when present). + - The texels are adjacent, forming a square, when considering their texture space coordinates (u,v). + - Selected texels at the texture edge, cube face edge, or cube corners are handled as in ordinary texture sampling. + * For each texel, perform a comparison against the depth reference value, yielding a 0.0 or 1.0 value, as controlled by the comparison sampler parameters. + * Yield the four-component vector where the components are the comparison results with the texels with relative texel coordinates as follows: + + Result component Relative texel coordinate + x (umin,vmax) + y (umax,vmax) + z (umax,vmin) + w (umin,vmin) +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +import { generateCoordBoundaries, generateOffsets } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare') + .desc( + ` +C: i32, u32 + +fn textureGatherCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32) -> vec4<f32> +fn textureGatherCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t: The depth texture to read from + * s: The sampler_comparison + * coords: The texture coordinates + * array_index: The 0-based array index. + * depth_ref: The reference value to compare the sampled depth value against + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4]) + .combine('coords', generateCoordBoundaries(2)) + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('array_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare') + .desc( + ` +C: i32, u32 + +fn textureGatherCompare(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3<f32>, array_index: C, depth_ref: f32) -> vec4<f32> + +Parameters: + * t: The depth texture to read from + * s: The sampler_comparison + * coords: The texture coordinates + * array_index: The 0-based array index. + * depth_ref: The reference value to compare the sampled depth value against +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4]) + .combine('coords', generateCoordBoundaries(3)) + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + ) + .unimplemented(); + +g.test('sampled_array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare') + .desc( + ` +fn textureGatherCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> vec4<f32> +fn textureGatherCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t: The depth texture to read from + * s: The sampler_comparison + * coords: The texture coordinates + * depth_ref: The reference value to compare the sampled depth value against + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('sampled_array_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare') + .desc( + ` +fn textureGatherCompare(t: texture_depth_cube, s: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> vec4<f32> + +Parameters: + * t: The depth texture to read from + * s: The sampler_comparison + * coords: The texture coordinates + * depth_ref: The reference value to compare the sampled depth value against +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts new file mode 100644 index 0000000000..30cc4fff52 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureLoad.spec.ts @@ -0,0 +1,185 @@ +export const description = ` +Execution tests for the 'textureLoad' builtin function + +Reads a single texel from a texture without sampling or filtering. + +Returns the unfiltered texel data. + +An out of bounds access occurs if: + * any element of coords is outside the range [0, textureDimensions(t, level)) for the corresponding element, or + * array_index is outside the range [0, textureNumLayers(t)), or + * level is outside the range [0, textureNumLevels(t)) + +If an out of bounds access occurs, the built-in function returns one of: + * The data for some texel within bounds of the texture + * A vector (0,0,0,0) or (0,0,0,1) of the appropriate type for non-depth textures + * 0.0 for depth textures +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +import { generateCoordBoundaries } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('sampled_1d') + .specURL('https://www.w3.org/TR/WGSL/#textureload') + .desc( + ` +C is i32 or u32 + +fn textureLoad(t: texture_1d<T>, coords: C, level: C) -> vec4<T> + +Parameters: + * t: The sampled texture to read from + * coords: The 0-based texel coordinate + * level: The mip level, with level 0 containing a full size version of the texture +` + ) + .params(u => + u + .combine('C', ['i32', 'u32'] as const) + .combine('coords', generateCoordBoundaries(1)) + .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const) + ) + .unimplemented(); + +g.test('sampled_2d') + .specURL('https://www.w3.org/TR/WGSL/#textureload') + .desc( + ` +C is i32 or u32 + +fn textureLoad(t: texture_2d<T>, coords: vec2<C>, level: C) -> vec4<T> + +Parameters: + * t: The sampled texture to read from + * coords: The 0-based texel coordinate + * level: The mip level, with level 0 containing a full size version of the texture +` + ) + .params(u => + u + .combine('C', ['i32', 'u32'] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const) + ) + .unimplemented(); + +g.test('sampled_3d') + .specURL('https://www.w3.org/TR/WGSL/#textureload') + .desc( + ` +C is i32 or u32 + +fn textureLoad(t: texture_3d<T>, coords: vec3<C>, level: C) -> vec4<T> + +Parameters: + * t: The sampled texture to read from + * coords: The 0-based texel coordinate + * level: The mip level, with level 0 containing a full size version of the texture +` + ) + .params(u => + u + .combine('C', ['i32', 'u32'] as const) + .combine('coords', generateCoordBoundaries(3)) + .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const) + ) + .unimplemented(); + +g.test('multisampled') + .specURL('https://www.w3.org/TR/WGSL/#textureload') + .desc( + ` +C is i32 or u32 + +fn textureLoad(t: texture_multisampled_2d<T>, coords: vec2<C>, sample_index: C)-> vec4<T> +fn textureLoad(t: texture_depth_multisampled_2d, coords: vec2<C>, sample_index: C)-> f32 + +Parameters: + * t: The sampled texture to read from + * coords: The 0-based texel coordinate + * sample_index: The 0-based sample index of the multisampled texture +` + ) + .params(u => + u + .combine('texture_type', [ + 'texture_multisampled_2d', + 'texture_depth_multisampled_2d', + ] as const) + .beginSubcases() + .combine('C', ['i32', 'u32'] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('sample_index', [-1, 0, `sampleCount-1`, `sampleCount`] as const) + ) + .unimplemented(); + +g.test('depth') + .specURL('https://www.w3.org/TR/WGSL/#textureload') + .desc( + ` +C is i32 or u32 + +fn textureLoad(t: texture_depth_2d, coords: vec2<C>, level: C) -> f32 + +Parameters: + * t: The sampled texture to read from + * coords: The 0-based texel coordinate + * level: The mip level, with level 0 containing a full size version of the texture +` + ) + .paramsSubcasesOnly(u => + u + .combine('C', ['i32', 'u32'] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const) + ) + .unimplemented(); + +g.test('external') + .specURL('https://www.w3.org/TR/WGSL/#textureload') + .desc( + ` +C is i32 or u32 + +fn textureLoad(t: texture_external, coords: vec2<C>) -> vec4<f32> + +Parameters: + * t: The sampled texture to read from + * coords: The 0-based texel coordinate +` + ) + .paramsSubcasesOnly(u => + u.combine('C', ['i32', 'u32'] as const).combine('coords', generateCoordBoundaries(2)) + ) + .unimplemented(); + +g.test('arrayed') + .specURL('https://www.w3.org/TR/WGSL/#textureload') + .desc( + ` +C is i32 or u32 + +fn textureLoad(t: texture_2d_array<T>, coords: vec2<C>, array_index: C, level: C) -> vec4<T> +fn textureLoad(t: texture_depth_2d_array, coords: vec2<C>, array_index: C, level: C) -> f32 + +Parameters: + * t: The sampled texture to read from + * coords: The 0-based texel coordinate + * array_index: The 0-based texture array index + * level: The mip level, with level 0 containing a full size version of the texture +` + ) + .params(u => + u + .combine('texture_type', ['texture_2d_array', 'texture_depth_2d_array'] as const) + .beginSubcases() + .combine('C', ['i32', 'u32'] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('array_index', [-1, 0, `numlayers-1`, `numlayers`] as const) + .combine('level', [-1, 0, `numlevels-1`, `numlevels`] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLayers.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLayers.spec.ts new file mode 100644 index 0000000000..b845301161 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLayers.spec.ts @@ -0,0 +1,100 @@ +export const description = ` +Execution tests for the 'textureNumLayers' builtin function + +Returns the number of layers (elements) of an array texture. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('sampled') + .specURL('https://www.w3.org/TR/WGSL/#texturenumlayers') + .desc( + ` +T, a sampled type. + +fn textureNumLayers(t: texture_2d_array<T>) -> u32 +fn textureNumLayers(t: texture_cube_array<T>) -> u32 + +Parameters + * t The sampled array texture. +` + ) + .params(u => + u + .combine('texture_type', ['texture_2d_array', 'texture_cube_array'] as const) + .beginSubcases() + .combine('sampled_type', ['f32-only', 'i32', 'u32'] as const) + ) + .unimplemented(); + +g.test('arrayed') + .specURL('https://www.w3.org/TR/WGSL/#texturenumlayers') + .desc( + ` +fn textureNumLayers(t: texture_depth_2d_array) -> u32 +fn textureNumLayers(t: texture_depth_cube_array) -> u32 + +Parameters + * t The depth array texture. +` + ) + .params(u => + u.combine('texture_type', ['texture_depth_2d_array', 'texture_depth_cube_array'] as const) + ) + .unimplemented(); + +g.test('storage') + .specURL('https://www.w3.org/TR/WGSL/#texturenumlayers') + .desc( + ` +F: rgba8unorm + rgba8snorm + rgba8uint + rgba8sint + rgba16uint + rgba16sint + rgba16float + r32uint + r32sint + r32float + rg32uint + rg32sint + rg32float + rgba32uint + rgba32sint + rgba32float +A: read, write, read_write + +fn textureNumLayers(t: texture_storage_2d_array<F,A>) -> u32 + +Parameters + * t The sampled storage array texture. +` + ) + .params(u => + u + .beginSubcases() + .combine('texel_format', [ + 'rgba8unorm', + 'rgba8snorm', + 'rgba8uint', + 'rgba8sint', + 'rgba16uint', + 'rgba16sint', + 'rgba16float', + 'r32uint', + 'r32sint', + 'r32float', + 'rg32uint', + 'rg32sint', + 'rg32float', + 'rgba32uint', + 'rgba32sint', + 'rgba32float', + ] as const) + .combine('access_mode', ['read', 'write', 'read_write'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLevels.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLevels.spec.ts new file mode 100644 index 0000000000..4204397b23 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumLevels.spec.ts @@ -0,0 +1,65 @@ +export const description = ` +Execution tests for the 'textureNumLevels' builtin function + +Returns the number of mip levels of a texture. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('sampled') + .specURL('https://www.w3.org/TR/WGSL/#texturenumlevels') + .desc( + ` +T, a sampled type. + +fn textureNumLevels(t: texture_1d<T>) -> u32 +fn textureNumLevels(t: texture_2d<T>) -> u32 +fn textureNumLevels(t: texture_2d_array<T>) -> u32 +fn textureNumLevels(t: texture_3d<T>) -> u32 +fn textureNumLevels(t: texture_cube<T>) -> u32 +fn textureNumLevels(t: texture_cube_array<T>) -> u32 + +Parameters + * t The sampled array texture. +` + ) + .params(u => + u + .combine('texture_type', [ + 'texture_1d', + 'texture_2d', + 'texture_2d_array', + 'texture_3d', + 'texture_cube', + 'texture_cube_array`', + ] as const) + .beginSubcases() + .combine('sampled_type', ['f32-only', 'i32', 'u32'] as const) + ) + .unimplemented(); + +g.test('depth') + .specURL('https://www.w3.org/TR/WGSL/#texturenumlevels') + .desc( + ` +fn textureNumLevels(t: texture_depth_2d) -> u32 +fn textureNumLevels(t: texture_depth_2d_array) -> u32 +fn textureNumLevels(t: texture_depth_cube) -> u32 +fn textureNumLevels(t: texture_depth_cube_array) -> u32 + +Parameters + * t The depth array texture. +` + ) + .params(u => + u.combine('texture_type', [ + 'texture_depth_2d', + 'texture_depth_2d_array', + 'texture_depth_cube', + 'texture_depth_cube_array', + ] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumSamples.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumSamples.spec.ts new file mode 100644 index 0000000000..26bda6cd48 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureNumSamples.spec.ts @@ -0,0 +1,37 @@ +export const description = ` +Execution tests for the 'textureNumSamples' builtin function + +Returns the number samples per texel in a multisampled texture. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('sampled') + .specURL('https://www.w3.org/TR/WGSL/#texturenumsamples') + .desc( + ` +T, a sampled type. + +fn textureNumSamples(t: texture_multisampled_2d<T>) -> u32 + +Parameters + * t The multisampled texture. +` + ) + .params(u => u.beginSubcases().combine('sampled_type', ['f32-only', 'i32', 'u32'] as const)) + .unimplemented(); + +g.test('depth') + .specURL('https://www.w3.org/TR/WGSL/#texturenumsamples') + .desc( + ` +fn textureNumSamples(t: texture_depth_multisampled_2d) -> u32 + +Parameters + * t The multisampled texture. +` + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts new file mode 100644 index 0000000000..f5b01dfc63 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSample.spec.ts @@ -0,0 +1,273 @@ +export const description = ` +Samples a texture. + +Must only be used in a fragment shader stage. +Must only be invoked in uniform control flow. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +import { generateCoordBoundaries, generateOffsets } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +Tests that 'textureSample' can only be called in 'fragment' shaders. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('control_flow') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +Tests that 'textureSample' can only be called in uniform control flow. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('sampled_1d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +fn textureSample(t: texture_1d<f32>, s: sampler, coords: f32) -> vec4<f32> + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('coords', generateCoordBoundaries(1)) + ) + .unimplemented(); + +g.test('sampled_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +fn textureSample(t: texture_2d<f32>, s: sampler, coords: vec2<f32>) -> vec4<f32> +fn textureSample(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('sampled_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +fn textureSample(t: texture_3d<f32>, s: sampler, coords: vec3<f32>) -> vec4<f32> +fn textureSample(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, offset: vec3<i32>) -> vec4<f32> +fn textureSample(t: texture_cube<f32>, s: sampler, coords: vec3<f32>) -> vec4<f32> + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .params(u => + u + .combine('texture_type', ['texture_3d', 'texture_cube'] as const) + .beginSubcases() + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('coords', generateCoordBoundaries(3)) + .combine('offset', generateOffsets(3)) + ) + .unimplemented(); + +g.test('depth_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +fn textureSample(t: texture_depth_2d, s: sampler, coords: vec2<f32>) -> f32 +fn textureSample(t: texture_depth_2d, s: sampler, coords: vec2<f32>, offset: vec2<i32>) -> f32 + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('sampled_array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +C is i32 or u32 + +fn textureSample(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C) -> vec4<f32> +fn textureSample(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * array_index The 0-based texture array index to sample. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('coords', generateCoordBoundaries(2)) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('sampled_array_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +C is i32 or u32 + +fn textureSample(t: texture_cube_array<f32>, s: sampler, coords: vec3<f32>, array_index: C) -> vec4<f32> + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * array_index The 0-based texture array index to sample. +` + ) + .paramsSubcasesOnly( + u => + u + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('coords', generateCoordBoundaries(3)) + /* array_index not param'd as out-of-bounds is implementation specific */ + ) + .unimplemented(); + +g.test('depth_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +fn textureSample(t: texture_depth_cube, s: sampler, coords: vec3<f32>) -> f32 + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('coords', generateCoordBoundaries(3)) + ) + .unimplemented(); + +g.test('depth_array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +C is i32 or u32 + +fn textureSample(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C) -> f32 +fn textureSample(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C, offset: vec2<i32>) -> f32 + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * array_index The 0-based texture array index to sample. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('coords', generateCoordBoundaries(2)) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('depth_array_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesample') + .desc( + ` +C is i32 or u32 + +fn textureSample(t: texture_depth_cube_array, s: sampler, coords: vec3<f32>, array_index: C) -> f32 + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * array_index The 0-based texture array index to sample. +` + ) + .paramsSubcasesOnly( + u => + u + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat'] as const) + .combine('coords', generateCoordBoundaries(3)) + /* array_index not param'd as out-of-bounds is implementation specific */ + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts new file mode 100644 index 0000000000..786bce4830 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleBias.spec.ts @@ -0,0 +1,163 @@ +export const description = ` +Execution tests for the 'textureSampleBias' builtin function + +Samples a texture with a bias to the mip level. +Must only be used in a fragment shader stage. +Must only be invoked in uniform control flow. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +import { generateCoordBoundaries, generateOffsets } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias') + .desc( + ` +Tests that 'textureSampleBias' can only be called in 'fragment' shaders. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('control_flow') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias') + .desc( + ` +Tests that 'textureSampleBias' can only be called in uniform control flow. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('sampled_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias') + .desc( + ` +fn textureSampleBias(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, bias: f32) -> vec4<f32> +fn textureSampleBias(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, bias: f32, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t: The sampled texture to read from + * s: The sampler type + * coords: The texture coordinates + * bias: The bias to apply to the mip level before sampling. bias must be between -16.0 and 15.99. + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('bias', [-16.1, -16, 0, 1, 15.99, 16] as const) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('sampled_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias') + .desc( + ` +fn textureSampleBias(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32> +fn textureSampleBias(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, bias: f32, offset: vec3<i32>) -> vec4<f32> +fn textureSampleBias(t: texture_cube<f32>, s: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32> + +Parameters: + * t: The sampled texture to read from + * s: The sampler type + * coords: The texture coordinates + * bias: The bias to apply to the mip level before sampling. bias must be between -16.0 and 15.99. + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .params(u => + u + .combine('texture_type', ['texture_3d', 'texture_cube'] as const) + .beginSubcases() + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + .combine('bias', [-16.1, -16, 0, 1, 15.99, 16] as const) + .combine('offset', generateOffsets(3)) + ) + .unimplemented(); + +g.test('arrayed_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias') + .desc( + ` +C: i32, u32 + +fn textureSampleBias(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, bias: f32) -> vec4<f32> +fn textureSampleBias(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, bias: f32, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t: The sampled texture to read from + * s: The sampler type + * coords: The texture coordinates + * array_index: The 0-based texture array index to sample. + * bias: The bias to apply to the mip level before sampling. bias must be between -16.0 and 15.99. + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('bias', [-16.1, -16, 0, 1, 15.99, 16] as const) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('arrayed_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplebias') + .desc( + ` +C: i32, u32 + +fn textureSampleBias(t: texture_cube_array<f32>, s: sampler, coords: vec3<f32>, array_index: C, bias: f32) -> vec4<f32> + +Parameters: + * t: The sampled texture to read from + * s: The sampler type + * coords: The texture coordinates + * array_index: The 0-based texture array index to sample. + * bias: The bias to apply to the mip level before sampling. bias must be between -16.0 and 15.99. + * offset: + - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + This offset is applied before applying any texture wrapping modes. + - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + - Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('bias', [-16.1, -16, 0, 1, 15.99, 16] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts new file mode 100644 index 0000000000..9f723fac2e --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompare.spec.ts @@ -0,0 +1,145 @@ +export const description = ` +Samples a depth texture and compares the sampled depth values against a reference value. + +Must only be used in a fragment shader stage. +Must only be invoked in uniform control flow. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +import { generateCoordBoundaries, generateOffsets } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') + .desc( + ` +Tests that 'textureSampleCompare' can only be called in 'fragment' shaders. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('control_flow') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') + .desc( + ` +Tests that 'textureSampleCompare' can only be called in uniform control flow. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') + .desc( + ` +fn textureSampleCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32 +fn textureSampleCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> f32 + +Parameters: + * t The depth texture to sample. + * s The sampler_comparision type. + * coords The texture coordinates used for sampling. + * depth_ref The reference value to compare the sampled depth value against. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') + .desc( + ` +fn textureSampleCompare(t: texture_depth_cube, s: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32 + +Parameters: + * t The depth texture to sample. + * s The sampler_comparision type. + * coords The texture coordinates used for sampling. + * depth_ref The reference value to compare the sampled depth value against. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + ) + .unimplemented(); + +g.test('arrayed_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') + .desc( + ` +C is i32 or u32 + +fn textureSampleCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32) -> f32 +fn textureSampleCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32, offset: vec2<i32>) -> f32 + +Parameters: + * t The depth texture to sample. + * s The sampler_comparision type. + * coords The texture coordinates used for sampling. + * array_index: The 0-based texture array index to sample. + * depth_ref The reference value to compare the sampled depth value against. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('arrayed_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecompare') + .desc( + ` +C is i32 or u32 + +fn textureSampleCompare(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3<f32>, array_index: C, depth_ref: f32) -> f32 + +Parameters: + * t The depth texture to sample. + * s The sampler_comparision type. + * coords The texture coordinates used for sampling. + * array_index: The 0-based texture array index to sample. + * depth_ref The reference value to compare the sampled depth value against. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts new file mode 100644 index 0000000000..500df8a6ec --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel.spec.ts @@ -0,0 +1,149 @@ +export const description = ` +Samples a depth texture and compares the sampled depth values against a reference value. + +The textureSampleCompareLevel function is the same as textureSampleCompare, except that: + + * textureSampleCompareLevel always samples texels from mip level 0. + * The function does not compute derivatives. + * There is no requirement for textureSampleCompareLevel to be invoked in uniform control flow. + * textureSampleCompareLevel may be invoked in any shader stage. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +import { generateCoordBoundaries, generateOffsets } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') + .desc( + ` +Tests that 'textureSampleCompareLevel' maybe called in any shader stage. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('control_flow') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') + .desc( + ` +Tests that 'textureSampleCompareLevel' maybe called in non-uniform control flow. +` + ) + .params(u => u.combine('stage', ['fragment', 'vertex', 'compute'] as const)) + .unimplemented(); + +g.test('2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') + .desc( + ` +fn textureSampleCompareLevel(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32 +fn textureSampleCompareLevel(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> f32 + +Parameters: + * t The depth texture to sample. + * s The sampler_comparision type. + * coords The texture coordinates used for sampling. + * depth_ref The reference value to compare the sampled depth value against. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') + .desc( + ` +fn textureSampleCompareLevel(t: texture_depth_cube, s: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32 + +Parameters: + * t The depth texture to sample. + * s The sampler_comparision type. + * coords The texture coordinates used for sampling. + * depth_ref The reference value to compare the sampled depth value against. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + ) + .unimplemented(); + +g.test('arrayed_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') + .desc( + ` +C is i32 or u32 + +fn textureSampleCompareLevel(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32) -> f32 +fn textureSampleCompareLevel(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: C, depth_ref: f32, offset: vec2<i32>) -> f32 + +Parameters: + * t The depth texture to sample. + * s The sampler_comparision type. + * coords The texture coordinates used for sampling. + * array_index: The 0-based texture array index to sample. + * depth_ref The reference value to compare the sampled depth value against. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('arrayed_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplecomparelevel') + .desc( + ` +C is i32 or u32 + +fn textureSampleCompareLevel(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3<f32>, array_index: C, depth_ref: f32) -> f32 + +Parameters: + * t The depth texture to sample. + * s The sampler_comparision type. + * coords The texture coordinates used for sampling. + * array_index: The 0-based texture array index to sample. + * depth_ref The reference value to compare the sampled depth value against. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('depth_ref', [-1 /* smaller ref */, 0 /* equal ref */, 1 /* larger ref */] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts new file mode 100644 index 0000000000..e0d754ece3 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleGrad.spec.ts @@ -0,0 +1,136 @@ +export const description = ` +Samples a texture using explicit gradients. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +import { generateCoordBoundaries, generateOffsets } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('sampled_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplegrad') + .desc( + ` +fn textureSampleGrad(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32> +fn textureSampleGrad(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t The sampled texture. + * s The sampler type. + * coords The texture coordinates used for sampling. + * ddx The x direction derivative vector used to compute the sampling locations + * ddy The y direction derivative vector used to compute the sampling locations + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('sampled_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplegrad') + .desc( + ` +fn textureSampleGrad(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32> +fn textureSampleGrad(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, ddx: vec3<f32>, ddy: vec3<f32>, offset: vec3<i32>) -> vec4<f32> +fn textureSampleGrad(t: texture_cube<f32>, s: sampler, coords: vec3<f32>, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32> + +Parameters: + * t The sampled texture. + * s The sampler type. + * ddx The x direction derivative vector used to compute the sampling locations + * ddy The y direction derivative vector used to compute the sampling locations + * coords The texture coordinates used for sampling. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + .combine('offset', generateOffsets(3)) + ) + .unimplemented(); + +g.test('sampled_array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplegrad') + .desc( + ` +C is i32 or u32 + +fn textureSampleGrad(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32> +fn textureSampleGrad(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, ddx: vec2<f32>, ddy: vec2<f32>, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t The sampled texture. + * s The sampler type. + * coords The texture coordinates used for sampling. + * array_index The 0-based texture array index to sample. + * ddx The x direction derivative vector used to compute the sampling locations + * ddy The y direction derivative vector used to compute the sampling locations + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(2)) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('offset', generateOffsets(2)) + ) + .unimplemented(); + +g.test('sampled_array_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplegrad') + .desc( + ` +C is i32 or u32 + +fn textureSampleGrad(t: texture_cube_array<f32>, s: sampler, coords: vec3<f32>, array_index: C, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32> + +Parameters: + * t The sampled texture. + * s The sampler type. + * coords The texture coordinates used for sampling. + * array_index The 0-based texture array index to sample. + * ddx The x direction derivative vector used to compute the sampling locations + * ddy The y direction derivative vector used to compute the sampling locations + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(3)) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts new file mode 100644 index 0000000000..f8073c65d6 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureSampleLevel.spec.ts @@ -0,0 +1,274 @@ +export const description = ` +Samples a texture. + +Must only be used in a fragment shader stage. +Must only be invoked in uniform control flow. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +import { generateCoordBoundaries, generateOffsets } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('sampled_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel') + .desc( + ` +fn textureSampleLevel(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, level: f32) -> vec4<f32> +fn textureSampleLevel(t: texture_2d<f32>, s: sampler, coords: vec2<f32>, level: f32, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t The sampled or depth texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * level + * The mip level, with level 0 containing a full size version of the texture. + * For the functions where level is a f32, fractional values may interpolate between + two levels if the format is filterable according to the Texture Format Capabilities. + * When not specified, mip level 0 is sampled. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(2)) + .combine('offset', generateOffsets(2)) + .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) + ) + .unimplemented(); + +g.test('sampled_array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel') + .desc( + ` +C is i32 or u32 + +fn textureSampleLevel(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, level: f32) -> vec4<f32> +fn textureSampleLevel(t: texture_2d_array<f32>, s: sampler, coords: vec2<f32>, array_index: C, level: f32, offset: vec2<i32>) -> vec4<f32> + +Parameters: + * t The sampled or depth texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * array_index The 0-based texture array index to sample. + * level + * The mip level, with level 0 containing a full size version of the texture. + * For the functions where level is a f32, fractional values may interpolate between + two levels if the format is filterable according to the Texture Format Capabilities. + * When not specified, mip level 0 is sampled. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('offset', generateOffsets(2)) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) + ) + .unimplemented(); + +g.test('sampled_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel') + .desc( + ` +fn textureSampleLevel(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, level: f32) -> vec4<f32> +fn textureSampleLevel(t: texture_3d<f32>, s: sampler, coords: vec3<f32>, level: f32, offset: vec3<i32>) -> vec4<f32> +fn textureSampleLevel(t: texture_cube<f32>, s: sampler, coords: vec3<f32>, level: f32) -> vec4<f32> + +Parameters: + * t The sampled or depth texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * level + * The mip level, with level 0 containing a full size version of the texture. + * For the functions where level is a f32, fractional values may interpolate between + two levels if the format is filterable according to the Texture Format Capabilities. + * When not specified, mip level 0 is sampled. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .params(u => + u + .combine('texture_type', ['texture_3d', 'texture_cube'] as const) + .beginSubcases() + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('coords', generateCoordBoundaries(3)) + .combine('offset', generateOffsets(3)) + .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) + ) + .unimplemented(); + +g.test('sampled_array_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel') + .desc( + ` +C is i32 or u32 + +fn textureSampleLevel(t: texture_cube_array<f32>, s: sampler, coords: vec3<f32>, array_index: C, level: f32) -> vec4<f32> + +Parameters: + * t The sampled or depth texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * array_index The 0-based texture array index to sample. + * level + * The mip level, with level 0 containing a full size version of the texture. + * For the functions where level is a f32, fractional values may interpolate between + two levels if the format is filterable according to the Texture Format Capabilities. + * When not specified, mip level 0 is sampled. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(3)) + .combine('offset', generateOffsets(3)) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) + ) + .unimplemented(); + +g.test('depth_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel') + .desc( + ` +C is i32 or u32 + +fn textureSampleLevel(t: texture_depth_2d, s: sampler, coords: vec2<f32>, level: C) -> f32 +fn textureSampleLevel(t: texture_depth_2d, s: sampler, coords: vec2<f32>, level: C, offset: vec2<i32>) -> f32 + +Parameters: + * t The sampled or depth texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * level + * The mip level, with level 0 containing a full size version of the texture. + * For the functions where level is a f32, fractional values may interpolate between + two levels if the format is filterable according to the Texture Format Capabilities. + * When not specified, mip level 0 is sampled. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('offset', generateOffsets(2)) + .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) + ) + .unimplemented(); + +g.test('depth_array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel') + .desc( + ` +C is i32 or u32 + +fn textureSampleLevel(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C, level: C) -> f32 +fn textureSampleLevel(t: texture_depth_2d_array, s: sampler, coords: vec2<f32>, array_index: C, level: C, offset: vec2<i32>) -> f32 + +Parameters: + * t The sampled or depth texture to sample. + * s The sampler type. + * array_index The 0-based texture array index to sample. + * coords The texture coordinates used for sampling. + * level + * The mip level, with level 0 containing a full size version of the texture. + * For the functions where level is a f32, fractional values may interpolate between + two levels if the format is filterable according to the Texture Format Capabilities. + * When not specified, mip level 0 is sampled. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .paramsSubcasesOnly(u => + u + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(2)) + .combine('offset', generateOffsets(2)) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) + ) + .unimplemented(); + +g.test('depth_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturesamplelevel') + .desc( + ` +C is i32 or u32 + +fn textureSampleLevel(t: texture_depth_cube, s: sampler, coords: vec3<f32>, level: C) -> f32 +fn textureSampleLevel(t: texture_depth_cube_array, s: sampler, coords: vec3<f32>, array_index: C, level: C) -> f32 + +Parameters: + * t The sampled or depth texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * level + * The mip level, with level 0 containing a full size version of the texture. + * For the functions where level is a f32, fractional values may interpolate between + two levels if the format is filterable according to the Texture Format Capabilities. + * When not specified, mip level 0 is sampled. + * offset + * The optional texel offset applied to the unnormalized texture coordinate before sampling the texture. + * This offset is applied before applying any texture wrapping modes. + * The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)). + * Each offset component must be at least -8 and at most 7. + Values outside of this range will result in a shader-creation error. +` + ) + .params(u => + u + .combine('texture_type', ['texture_depth_cube', 'texture_depth_cube_array'] as const) + .beginSubcases() + .combine('S', ['clamp-to-edge', 'repeat', 'mirror-repeat']) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + .combine('coords', generateCoordBoundaries(3)) + /* array_index not param'd as out-of-bounds is implementation specific */ + .combine('level', [undefined, 0, 1, 'textureNumLevels', 'textureNumLevels+1'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureStore.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureStore.spec.ts new file mode 100644 index 0000000000..efef971e24 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/textureStore.spec.ts @@ -0,0 +1,122 @@ +export const description = ` +Writes a single texel to a texture. + +The channel format T depends on the storage texel format F. +See the texel format table for the mapping of texel format to channel format. + +Note: An out-of-bounds access occurs if: + * any element of coords is outside the range [0, textureDimensions(t)) for the corresponding element, or + * array_index is outside the range of [0, textureNumLayers(t)) + +If an out-of-bounds access occurs, the built-in function may do any of the following: + * not be executed + * store value to some in bounds texel +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TexelFormats } from '../../../../types.js'; + +import { generateCoordBoundaries } from './utils.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('store_1d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturestore') + .desc( + ` +C is i32 or u32 + +fn textureStore(t: texture_storage_1d<F,write>, coords: C, value: vec4<T>) + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * value The new texel value +` + ) + .params(u => + u + .combineWithParams(TexelFormats) + .beginSubcases() + .combine('coords', generateCoordBoundaries(1)) + .combine('C', ['i32', 'u32'] as const) + ) + .unimplemented(); + +g.test('store_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturestore') + .desc( + ` +C is i32 or u32 + +fn textureStore(t: texture_storage_2d<F,write>, coords: vec2<C>, value: vec4<T>) + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * value The new texel value +` + ) + .params(u => + u + .combineWithParams(TexelFormats) + .beginSubcases() + .combine('coords', generateCoordBoundaries(2)) + .combine('C', ['i32', 'u32'] as const) + ) + .unimplemented(); + +g.test('store_array_2d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturestore') + .desc( + ` +C is i32 or u32 + +fn textureStore(t: texture_storage_2d_array<F,write>, coords: vec2<C>, array_index: C, value: vec4<T>) + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * array_index The 0-based texture array index + * coords The texture coordinates used for sampling. + * value The new texel value +` + ) + .params( + u => + u + .combineWithParams(TexelFormats) + .beginSubcases() + .combine('coords', generateCoordBoundaries(2)) + .combine('C', ['i32', 'u32'] as const) + .combine('C_value', [-1, 0, 1, 2, 3, 4] as const) + /* array_index not param'd as out-of-bounds is implementation specific */ + ) + .unimplemented(); + +g.test('store_3d_coords') + .specURL('https://www.w3.org/TR/WGSL/#texturestore') + .desc( + ` +C is i32 or u32 + +fn textureStore(t: texture_storage_3d<F,write>, coords: vec3<C>, value: vec4<T>) + +Parameters: + * t The sampled, depth, or external texture to sample. + * s The sampler type. + * coords The texture coordinates used for sampling. + * value The new texel value +` + ) + .params(u => + u + .combineWithParams(TexelFormats) + .beginSubcases() + .combine('coords', generateCoordBoundaries(3)) + .combine('C', ['i32', 'u32'] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts new file mode 100644 index 0000000000..707b74a38b --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts @@ -0,0 +1,46 @@ +export const description = ` +Execution tests for the 'transpose' builtin function + +T is AbstractFloat, f32, or f16 +@const transpose(e: matRxC<T> ) -> matCxR<T> +Returns the transpose of e. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { allInputSources } from '../../expression.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('rows', [2, 3, 4] as const) + .combine('cols', [2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions') + .desc(`f32 tests`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('rows', [2, 3, 4] as const) + .combine('cols', [2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions') + .desc(`f16 tests`) + .params(u => + u + .combine('inputSource', allInputSources) + .combine('rows', [2, 3, 4] as const) + .combine('cols', [2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts new file mode 100644 index 0000000000..c743af3230 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts @@ -0,0 +1,54 @@ +export const description = ` +Execution tests for the 'trunc' builtin function + +S is AbstractFloat, f32, f16 +T is S or vecN<S> +@const fn trunc(e: T ) -> T +Returns the nearest whole number whose absolute value is less than or equal to e. +Component-wise when T is a vector. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; +import { TypeF32 } from '../../../../../util/conversion.js'; +import { truncInterval } from '../../../../../util/f32_interval.js'; +import { fullF32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateUnaryToF32IntervalCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('trunc', { + f32: () => { + return generateUnaryToF32IntervalCases(fullF32Range(), 'unfiltered', truncInterval); + }, +}); + +g.test('abstract_float') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`abstract float tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); + +g.test('f32') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f32 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .fn(async t => { + const cases = await d.get('f32'); + await run(t, builtin('trunc'), [TypeF32], TypeF32, t.params, cases); + }); + +g.test('f16') + .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') + .desc(`f16 tests`) + .params(u => + u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) + ) + .unimplemented(); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts new file mode 100644 index 0000000000..990b957aaf --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts @@ -0,0 +1,40 @@ +export const description = ` +Decomposes a 32-bit value into two 16-bit chunks, and reinterpets each chunk as +a floating point value. +Component i of the result is the f32 representation of v, where v is the +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 { unpack2x16floatInterval } from '../../../../../util/f32_interval.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('unpack2x16float', { + u32_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack2x16floatInterval); + }, + u32_non_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack2x16floatInterval); + }, +}); + +g.test('unpack') + .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') + .desc( + ` +@const fn unpack2x16float(e: u32) -> vec2<f32> +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); + await run(t, builtin('unpack2x16float'), [TypeU32], TypeVec(2, TypeF32), t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts new file mode 100644 index 0000000000..6b003ad5d2 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts @@ -0,0 +1,40 @@ +export const description = ` +Decomposes a 32-bit value into two 16-bit chunks, then reinterprets each chunk +as a signed normalized floating point value. +Component i of the result is max(v ÷ 32767, -1), where v is the interpretation +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 { unpack2x16snormInterval } from '../../../../../util/f32_interval.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('unpack2x16snorm', { + u32_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack2x16snormInterval); + }, + u32_non_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack2x16snormInterval); + }, +}); + +g.test('unpack') + .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') + .desc( + ` +@const fn unpack2x16snorm(e: u32) -> vec2<f32> +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); + await run(t, builtin('unpack2x16snorm'), [TypeU32], TypeVec(2, TypeF32), t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts new file mode 100644 index 0000000000..f79047668e --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts @@ -0,0 +1,40 @@ +export const description = ` +Decomposes a 32-bit value into two 16-bit chunks, then reinterprets each chunk +as an unsigned normalized floating point value. +Component i of the result is v ÷ 65535, where v is the interpretation of bits +16×i through 16×i+15 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 { unpack2x16unormInterval } from '../../../../../util/f32_interval.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('unpack2x16unorm', { + u32_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack2x16unormInterval); + }, + u32_non_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack2x16unormInterval); + }, +}); + +g.test('unpack') + .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') + .desc( + ` +@const fn unpack2x16unorm(e: u32) -> vec2<f32> +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); + await run(t, builtin('unpack2x16unorm'), [TypeU32], TypeVec(2, TypeF32), t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts new file mode 100644 index 0000000000..8f425a46f4 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts @@ -0,0 +1,40 @@ +export const description = ` +Decomposes a 32-bit value into four 8-bit chunks, then reinterprets each chunk +as a signed normalized floating point value. +Component i of the result is max(v ÷ 127, -1), where v is the interpretation of +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 { unpack4x8snormInterval } from '../../../../../util/f32_interval.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('unpack4x8snorm', { + u32_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack4x8snormInterval); + }, + u32_non_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack4x8snormInterval); + }, +}); + +g.test('unpack') + .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') + .desc( + ` +@const fn unpack4x8snorm(e: u32) -> vec4<f32> +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); + await run(t, builtin('unpack4x8snorm'), [TypeU32], TypeVec(4, TypeF32), t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts new file mode 100644 index 0000000000..01d3db47c9 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts @@ -0,0 +1,40 @@ +export const description = ` +Decomposes a 32-bit value into four 8-bit chunks, then reinterprets each chunk +as an unsigned normalized floating point value. +Component i of the result is v ÷ 255, where v is the interpretation of bits 8×i +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 { unpack4x8unormInterval } from '../../../../../util/f32_interval.js'; +import { fullU32Range } from '../../../../../util/math.js'; +import { makeCaseCache } from '../../case_cache.js'; +import { allInputSources, generateU32ToVectorCases, run } from '../../expression.js'; + +import { builtin } from './builtin.js'; + +export const g = makeTestGroup(GPUTest); + +export const d = makeCaseCache('unpack4x8unorm', { + u32_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'f32-only', unpack4x8unormInterval); + }, + u32_non_const: () => { + return generateU32ToVectorCases(fullU32Range(), 'unfiltered', unpack4x8unormInterval); + }, +}); + +g.test('unpack') + .specURL('https://www.w3.org/TR/WGSL/#unpack-builtin-functions') + .desc( + ` +@const fn unpack4x8unorm(e: u32) -> vec4<f32> +` + ) + .params(u => u.combine('inputSource', allInputSources)) + .fn(async t => { + const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); + await run(t, builtin('unpack4x8unorm'), [TypeU32], TypeVec(4, TypeF32), t.params, cases); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/utils.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/utils.ts new file mode 100644 index 0000000000..9cbee00939 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/utils.ts @@ -0,0 +1,45 @@ +/** + * Generates the boundary entries for the given number of dimensions + * + * @param numDimensions: The number of dimensions to generate for + * @returns an array of generated coord boundaries + */ +export function generateCoordBoundaries(numDimensions: number) { + const ret = ['in-bounds']; + + if (numDimensions < 1 || numDimensions > 3) { + throw new Error(`invalid numDimensions: ${numDimensions}`); + } + + const name = 'xyz'; + for (let i = 0; i < numDimensions; ++i) { + for (const j of ['min', 'max']) { + for (const k of ['wrap', 'boundary']) { + ret.push(`${name[i]}-${j}-${k}`); + } + } + } + + return ret; +} + +/** + * Generates a set of offset values to attempt in the range [-9, 8]. + * + * @param numDimensions: The number of dimensions to generate for + * @return an array of generated offset values + */ +export function generateOffsets(numDimensions: number) { + if (numDimensions < 2 || numDimensions > 3) { + throw new Error(`generateOffsets: invalid numDimensions: ${numDimensions}`); + } + const ret: Array<undefined | Array<number>> = [undefined]; + for (const val of [-9, -8, 0, 1, 7, 8]) { + const v = []; + for (let i = 0; i < numDimensions; ++i) { + v.push(val); + } + ret.push(v); + } + return ret; +} diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupBarrier.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupBarrier.spec.ts new file mode 100644 index 0000000000..74e0f12325 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/call/builtin/workgroupBarrier.spec.ts @@ -0,0 +1,38 @@ +export const description = ` +'workgroupBarrier' affects memory and atomic operations in the workgroup address space. + +All synchronization functions execute a control barrier with Acquire/Release memory ordering. +That is, all synchronization functions, and affected memory and atomic operations are ordered +in program order relative to the synchronization function. Additionally, the affected memory +and atomic operations program-ordered before the synchronization function must be visible to +all other threads in the workgroup before any affected memory or atomic operation program-ordered +after the synchronization function is executed by a member of the workgroup. All synchronization +functions use the Workgroup memory scope. All synchronization functions have a Workgroup +execution scope. + +All synchronization functions must only be used in the compute shader stage. +`; + +import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('stage') + .specURL('https://www.w3.org/TR/WGSL/#sync-builtin-functions') + .desc( + ` +All synchronization functions must only be used in the compute shader stage. +` + ) + .params(u => u.combine('stage', ['vertex', 'fragment', 'compute'] as const)) + .unimplemented(); + +g.test('barrier') + .specURL('https://www.w3.org/TR/WGSL/#sync-builtin-functions') + .desc( + ` +fn workgroupBarrier() +` + ) + .unimplemented(); |