From ea0efb11192c4aae7bb3e1371a15d3afa28e217a Mon Sep 17 00:00:00 2001 From: "Yan, Shaobo" Date: Tue, 5 Mar 2024 06:47:28 +0800 Subject: [PATCH 01/13] GPUExternalTexture: Add cts to cover non-YUV format video frame (#3451) The cts lacks coverage for import non-YUV format video frame. This PR construct such frame using webcodec VideoFrame and import it in WebGPU. --- src/webgpu/listing_meta.json | 1 + .../external_texture/video.spec.ts | 133 +++++++++++++++++- 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index f0585f3365e3..a8ecd45ed3de 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -2193,6 +2193,7 @@ "webgpu:web_platform,copyToTexture,video:copy_from_video:*": { "subcaseMS": 25.101 }, "webgpu:web_platform,external_texture,video:importExternalTexture,compute:*": { "subcaseMS": 36.270 }, "webgpu:web_platform,external_texture,video:importExternalTexture,sample:*": { "subcaseMS": 34.968 }, + "webgpu:web_platform,external_texture,video:importExternalTexture,sample_non_YUV_video_frame:*": { "subcaseMS": 36.270 }, "webgpu:web_platform,external_texture,video:importExternalTexture,sampleWithVideoFrameWithVisibleRectParam:*": { "subcaseMS": 29.160 }, "webgpu:web_platform,worker,worker:dedicated_worker:*": { "subcaseMS": 245.901 }, "webgpu:web_platform,worker,worker:shared_worker:*": { "subcaseMS": 26.801 }, diff --git a/src/webgpu/web_platform/external_texture/video.spec.ts b/src/webgpu/web_platform/external_texture/video.spec.ts index 2b5e0bdb40a9..8e812ccd2a34 100644 --- a/src/webgpu/web_platform/external_texture/video.spec.ts +++ b/src/webgpu/web_platform/external_texture/video.spec.ts @@ -10,6 +10,7 @@ TODO(#3193): Test video in BT.2020 color space import { makeTestGroup } from '../../../common/framework/test_group.js'; import { GPUTest, TextureTestMixin } from '../../gpu_test.js'; +import { createCanvas } from '../../util/create_elements.js'; import { startPlayingAndWaitForVideo, getVideoFrameFromVideoElement, @@ -27,7 +28,10 @@ const kFormat = 'rgba8unorm'; export const g = makeTestGroup(TextureTestMixin(GPUTest)); -function createExternalTextureSamplingTestPipeline(t: GPUTest): GPURenderPipeline { +function createExternalTextureSamplingTestPipeline( + t: GPUTest, + colorAttachmentFormat: GPUTextureFormat = kFormat +): GPURenderPipeline { const pipeline = t.device.createRenderPipeline({ layout: 'auto', vertex: { @@ -63,7 +67,7 @@ function createExternalTextureSamplingTestPipeline(t: GPUTest): GPURenderPipelin entryPoint: 'main', targets: [ { - format: kFormat, + format: colorAttachmentFormat, }, ], }, @@ -228,6 +232,131 @@ for several combinations of video format, video color spaces and dst color space }); }); +g.test('importExternalTexture,sample_non_YUV_video_frame') + .desc( + ` +Tests that we can import an VideoFrame with non-YUV pixel format into a GPUExternalTexture and sample it. +` + ) + .params(u => + u // + .combine('videoFrameFormat', ['RGBA', 'RGBX', 'BGRA', 'BGRX'] as const) + ) + .fn(t => { + const { videoFrameFormat } = t.params; + + if (typeof VideoFrame === 'undefined') { + t.skip('WebCodec is not supported'); + } + + const canvas = createCanvas(t, 'onscreen', kWidth, kHeight); + + const canvasContext = canvas.getContext('2d'); + + if (canvasContext === null) { + t.skip(' onscreen canvas 2d context not available'); + } + + const ctx = canvasContext as CanvasRenderingContext2D; + + const rectWidth = Math.floor(kWidth / 2); + const rectHeight = Math.floor(kHeight / 2); + + // Red + ctx.fillStyle = `rgba(255, 0, 0, 1.0)`; + ctx.fillRect(0, 0, rectWidth, rectHeight); + // Lime + ctx.fillStyle = `rgba(0, 255, 0, 1.0)`; + ctx.fillRect(rectWidth, 0, kWidth - rectWidth, rectHeight); + // Blue + ctx.fillStyle = `rgba(0, 0, 255, 1.0)`; + ctx.fillRect(0, rectHeight, rectWidth, kHeight - rectHeight); + // Fuchsia + ctx.fillStyle = `rgba(255, 0, 255, 1.0)`; + ctx.fillRect(rectWidth, rectHeight, kWidth - rectWidth, kHeight - rectHeight); + + const imageData = ctx.getImageData(0, 0, kWidth, kHeight); + + // Create video frame with default color space 'srgb' + const frameInit: VideoFrameBufferInit = { + format: videoFrameFormat, + codedWidth: kWidth, + codedHeight: kHeight, + timestamp: 0, + }; + + const frame = new VideoFrame(imageData.data.buffer, frameInit); + let textureFormat: GPUTextureFormat = 'rgba8unorm'; + + if (videoFrameFormat === 'BGRA' || videoFrameFormat === 'BGRX') { + textureFormat = 'bgra8unorm'; + } + + const colorAttachment = t.device.createTexture({ + format: textureFormat, + size: { width: kWidth, height: kHeight, depthOrArrayLayers: 1 }, + usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT, + }); + + const pipeline = createExternalTextureSamplingTestPipeline(t, textureFormat); + const bindGroup = createExternalTextureSamplingTestBindGroup( + t, + undefined /* checkNonStandardIsZeroCopy */, + frame, + pipeline, + 'srgb' + ); + + const commandEncoder = t.device.createCommandEncoder(); + const passEncoder = commandEncoder.beginRenderPass({ + colorAttachments: [ + { + view: colorAttachment.createView(), + clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }, + loadOp: 'clear', + storeOp: 'store', + }, + ], + }); + passEncoder.setPipeline(pipeline); + passEncoder.setBindGroup(0, bindGroup); + passEncoder.draw(6); + passEncoder.end(); + t.device.queue.submit([commandEncoder.finish()]); + + const expected = { + topLeft: new Uint8Array([255, 0, 0, 255]), + topRight: new Uint8Array([0, 255, 0, 255]), + bottomLeft: new Uint8Array([0, 0, 255, 255]), + bottomRight: new Uint8Array([255, 0, 255, 255]), + }; + + // For validation, we sample a few pixels away from the edges to avoid compression + // artifacts. + t.expectSinglePixelComparisonsAreOkInTexture({ texture: colorAttachment }, [ + // Top-left. + { + coord: { x: kWidth * 0.25, y: kHeight * 0.25 }, + exp: expected.topLeft, + }, + // Top-right. + { + coord: { x: kWidth * 0.75, y: kHeight * 0.25 }, + exp: expected.topRight, + }, + // Bottom-left. + { + coord: { x: kWidth * 0.25, y: kHeight * 0.75 }, + exp: expected.bottomLeft, + }, + // Bottom-right. + { + coord: { x: kWidth * 0.75, y: kHeight * 0.75 }, + exp: expected.bottomRight, + }, + ]); + }); + g.test('importExternalTexture,sampleWithVideoFrameWithVisibleRectParam') .desc( ` From 1f4b242a9c2baabf6eab2df6449d0c125097f070 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Mon, 4 Mar 2024 17:51:08 -0500 Subject: [PATCH 02/13] Tests for scoping diagnostics (#3450) --- src/webgpu/listing_meta.json | 1 + .../validation/parse/diagnostic.spec.ts | 239 ++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index a8ecd45ed3de..d779fa7b05ee 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1976,6 +1976,7 @@ "webgpu:shader,validation,parse,diagnostic:conflicting_attribute_different_location:*": { "subcaseMS": 2.257 }, "webgpu:shader,validation,parse,diagnostic:conflicting_attribute_same_location:*": { "subcaseMS": 1.400 }, "webgpu:shader,validation,parse,diagnostic:conflicting_directive:*": { "subcaseMS": 1.244 }, + "webgpu:shader,validation,parse,diagnostic:diagnostic_scoping:*": { "subcaseMS": 1.244 }, "webgpu:shader,validation,parse,diagnostic:invalid_locations:*": { "subcaseMS": 1.930 }, "webgpu:shader,validation,parse,diagnostic:invalid_severity:*": { "subcaseMS": 1.361 }, "webgpu:shader,validation,parse,diagnostic:valid_locations:*": { "subcaseMS": 1.368 }, diff --git a/src/webgpu/shader/validation/parse/diagnostic.spec.ts b/src/webgpu/shader/validation/parse/diagnostic.spec.ts index 757cf50cbba4..701ae46f3a0d 100644 --- a/src/webgpu/shader/validation/parse/diagnostic.spec.ts +++ b/src/webgpu/shader/validation/parse/diagnostic.spec.ts @@ -220,3 +220,242 @@ g.test('after_other_directives') code += generateDiagnostic('directive', 'info', 'derivative_uniformity') + ';'; t.expectCompileResult(true, code); }); + +interface ScopeCase { + code: string; + result: boolean | 'warn'; +} + +function scopeCode(body: string): string { + return ` +@group(0) @binding(0) var t : texture_1d; +@group(0) @binding(1) var s : sampler; +var non_uniform_cond : bool; +var non_uniform_coord : f32; +var non_uniform_val : u32; +@fragment fn main() { + ${body} +} +`; +} + +const kScopeCases: Record = { + override_global_off: { + code: ` + ${generateDiagnostic('directive', 'error', 'derivative_uniformity')}; + ${scopeCode(` + ${generateDiagnostic('', 'off', 'derivative_uniformity')} + if non_uniform_cond { + _ = textureSample(t,s,0.0); + }`)}; + `, + result: true, + }, + override_global_on: { + code: ` + ${generateDiagnostic('directive', 'off', 'derivative_uniformity')}; + ${scopeCode(` + ${generateDiagnostic('', 'error', 'derivative_uniformity')} + if non_uniform_cond { + _ = textureSample(t,s,0.0); + }`)} + `, + result: false, + }, + override_global_warn: { + code: ` + ${generateDiagnostic('directive', 'error', 'derivative_uniformity')}; + ${scopeCode(` + ${generateDiagnostic('', 'warning', 'derivative_uniformity')} + if non_uniform_cond { + _ = textureSample(t,s,0.0); + }`)} + `, + result: 'warn', + }, + global_if_nothing_else_warn: { + code: ` + ${generateDiagnostic('directive', 'warning', 'derivative_uniformity')}; + ${scopeCode(` + if non_uniform_cond { + _ = textureSample(t,s,0.0); + }`)} + `, + result: 'warn', + }, + deepest_nesting_warn: { + code: scopeCode(` + ${generateDiagnostic('', 'error', 'derivative_uniformity')} + if non_uniform_cond { + ${generateDiagnostic('', 'warning', 'derivative_uniformity')} + if non_uniform_cond { + _ = textureSample(t,s,0.0); + } + }`), + result: 'warn', + }, + deepest_nesting_off: { + code: scopeCode(` + ${generateDiagnostic('', 'error', 'derivative_uniformity')} + if non_uniform_cond { + ${generateDiagnostic('', 'off', 'derivative_uniformity')} + if non_uniform_cond { + _ = textureSample(t,s,0.0); + } + }`), + result: true, + }, + deepest_nesting_error: { + code: scopeCode(` + ${generateDiagnostic('', 'off', 'derivative_uniformity')} + if non_uniform_cond { + ${generateDiagnostic('', 'error', 'derivative_uniformity')} + if non_uniform_cond { + _ = textureSample(t,s,0.0); + } + }`), + result: false, + }, + other_nest_unaffected: { + code: ` + ${generateDiagnostic('directive', 'warning', 'derivative_uniformity')}; + ${scopeCode(` + ${generateDiagnostic('', 'off', 'derivative_uniformity')} + if non_uniform_cond { + _ = textureSample(t,s,0.0); + } + if non_uniform_cond { + _ = textureSample(t,s,0.0); + }`)} + `, + result: 'warn', + }, + deeper_nest_no_effect: { + code: ` + ${generateDiagnostic('directive', 'error', 'derivative_uniformity')}; + ${scopeCode(` + if non_uniform_cond { + ${generateDiagnostic('', 'off', 'derivative_uniformity')} + if non_uniform_cond { + } + _ = textureSample(t,s,0.0); + }`)} + `, + result: false, + }, + call_unaffected_error: { + code: ` + ${generateDiagnostic('directive', 'error', 'derivative_uniformity')}; + fn foo() { _ = textureSample(t,s,0.0); } + ${scopeCode(` + ${generateDiagnostic('', 'off', 'derivative_uniformity')} + if non_uniform_cond { + foo(); + }`)} + `, + result: false, + }, + call_unaffected_warn: { + code: ` + ${generateDiagnostic('directive', 'warning', 'derivative_uniformity')}; + fn foo() { _ = textureSample(t,s,0.0); } + ${scopeCode(` + ${generateDiagnostic('', 'off', 'derivative_uniformity')} + if non_uniform_cond { + foo(); + }`)} + `, + result: 'warn', + }, + call_unaffected_off: { + code: ` + ${generateDiagnostic('directive', 'off', 'derivative_uniformity')}; + fn foo() { _ = textureSample(t,s,0.0); } + ${scopeCode(` + ${generateDiagnostic('', 'error', 'derivative_uniformity')} + if non_uniform_cond { + foo(); + }`)} + `, + result: true, + }, + if_condition_error: { + code: scopeCode(` + if (non_uniform_cond) { + ${generateDiagnostic('', 'error', 'derivative_uniformity')} + if textureSample(t,s,non_uniform_coord).x > 0.0 + ${generateDiagnostic('', 'off', 'derivative_uniformity')} { + } + }`), + result: false, + }, + if_condition_warn: { + code: scopeCode(` + if non_uniform_cond { + ${generateDiagnostic('', 'warning', 'derivative_uniformity')} + if textureSample(t,s,non_uniform_coord).x > 0.0 + ${generateDiagnostic('', 'error', 'derivative_uniformity')} { + } + }`), + result: 'warn', + }, + if_condition_off: { + code: scopeCode(` + if non_uniform_cond { + ${generateDiagnostic('', 'off', 'derivative_uniformity')} + if textureSample(t,s,non_uniform_coord).x > 0.0 + ${generateDiagnostic('', 'error', 'derivative_uniformity')} { + } + }`), + result: true, + }, + switch_error: { + code: scopeCode(` + ${generateDiagnostic('', 'error', 'derivative_uniformity')} + switch non_uniform_val { + case 0 ${generateDiagnostic('', 'off', 'derivative_uniformity')} { + } + default { + _ = textureSample(t,s,0.0); + } + }`), + result: false, + }, + switch_warn: { + code: scopeCode(` + ${generateDiagnostic('', 'warning', 'derivative_uniformity')} + switch non_uniform_val { + case 0 ${generateDiagnostic('', 'off', 'derivative_uniformity')} { + } + default { + _ = textureSample(t,s,0.0); + } + }`), + result: 'warn', + }, + switch_off: { + code: scopeCode(` + ${generateDiagnostic('', 'off', 'derivative_uniformity')} + switch non_uniform_val { + case 0 ${generateDiagnostic('', 'error', 'derivative_uniformity')}{ + } + default { + _ = textureSample(t,s,0.0); + } + }`), + result: true, + }, +}; + +g.test('diagnostic_scoping') + .specURL('https://gpuweb.github.io/gpuweb/wgsl/#diagnostics') + .desc('Tests that innermost scope controls the diagnostic') + .params(u => u.combine('case', keysOf(kScopeCases))) + .fn(t => { + const testcase = kScopeCases[t.params.case]; + if (testcase.result === 'warn') { + t.expectCompileWarning(true, testcase.code); + } else { + t.expectCompileResult(testcase.result as boolean, testcase.code); + } + }); From 70ac878b39ada939f225f7dd0d01134e1d7de691 Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Tue, 5 Mar 2024 18:31:28 +0100 Subject: [PATCH 03/13] WGSL: Add validation tests for readonly_and_readwrite_storage_textures. (#3453) --- src/webgpu/listing_meta.json | 4 +- ...nly_and_readwrite_storage_textures.spec.ts | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/webgpu/shader/validation/extension/readonly_and_readwrite_storage_textures.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index d779fa7b05ee..b3081b5763dd 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1922,6 +1922,8 @@ "webgpu:shader,validation,expression,unary,address_of_and_indirection:invalid:*": { "subcaseMS": 0.000 }, "webgpu:shader,validation,extension,pointer_composite_access:deref:*": { "subcaseMS": 0.000 }, "webgpu:shader,validation,extension,pointer_composite_access:pointer:*": { "subcaseMS": 0.000 }, + "webgpu:shader,validation,extension,readonly_and_readwrite_storage_textures:textureBarrier:*": { "subcaseMS": 1.141 }, + "webgpu:shader,validation,extension,readonly_and_readwrite_storage_textures:var_decl:*": { "subcaseMS": 42.299 }, "webgpu:shader,validation,functions,alias_analysis:aliasing_inside_function:*": { "subcaseMS": 1.200 }, "webgpu:shader,validation,functions,alias_analysis:member_accessors:*": { "subcaseMS": 1.656 }, "webgpu:shader,validation,functions,alias_analysis:one_atomic_pointer_one_module_scope:*": { "subcaseMS": 0.000 }, @@ -2194,8 +2196,8 @@ "webgpu:web_platform,copyToTexture,video:copy_from_video:*": { "subcaseMS": 25.101 }, "webgpu:web_platform,external_texture,video:importExternalTexture,compute:*": { "subcaseMS": 36.270 }, "webgpu:web_platform,external_texture,video:importExternalTexture,sample:*": { "subcaseMS": 34.968 }, - "webgpu:web_platform,external_texture,video:importExternalTexture,sample_non_YUV_video_frame:*": { "subcaseMS": 36.270 }, "webgpu:web_platform,external_texture,video:importExternalTexture,sampleWithVideoFrameWithVisibleRectParam:*": { "subcaseMS": 29.160 }, + "webgpu:web_platform,external_texture,video:importExternalTexture,sample_non_YUV_video_frame:*": { "subcaseMS": 36.270 }, "webgpu:web_platform,worker,worker:dedicated_worker:*": { "subcaseMS": 245.901 }, "webgpu:web_platform,worker,worker:shared_worker:*": { "subcaseMS": 26.801 }, "_end": "" diff --git a/src/webgpu/shader/validation/extension/readonly_and_readwrite_storage_textures.spec.ts b/src/webgpu/shader/validation/extension/readonly_and_readwrite_storage_textures.spec.ts new file mode 100644 index 000000000000..c74694d4b551 --- /dev/null +++ b/src/webgpu/shader/validation/extension/readonly_and_readwrite_storage_textures.spec.ts @@ -0,0 +1,48 @@ +export const description = ` +Validation tests for the readonly_and_readwrite_storage_textures language feature +`; + +import { makeTestGroup } from '../../../../common/framework/test_group.js'; +import { TexelFormats } from '../../types.js'; +import { ShaderValidationTest } from '../shader_validation_test.js'; + +export const g = makeTestGroup(ShaderValidationTest); + +const kFeatureName = 'readonly_and_readwrite_storage_textures'; + +g.test('var_decl') + .desc( + `Checks that the read and read_write access modes are only allowed with the language feature present` + ) + .paramsSubcasesOnly(u => + u + .combine('type', [ + 'texture_storage_1d', + 'texture_storage_2d', + 'texture_storage_2d_array', + 'texture_storage_3d', + ]) + .combine('format', TexelFormats) + .combine('access', ['read', 'write', 'read_write']) + ) + .fn(t => { + const { type, format, access } = t.params; + const source = `@group(0) @binding(0) var t : ${type}<${format.format}, ${access}>;`; + const requiresFeature = access !== 'write'; + t.expectCompileResult(t.hasLanguageFeature(kFeatureName) || !requiresFeature, source); + }); + +g.test('textureBarrier') + .desc( + `Checks that the textureBarrier() builtin is only allowed with the language feature present` + ) + .fn(t => { + t.expectCompileResult( + t.hasLanguageFeature(kFeatureName), + ` + @workgroup_size(1) @compute fn main() { + textureBarrier(); + } + ` + ); + }); From 3dcdba7fe0dced1f7332387627345cb5a7e311c4 Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Wed, 6 Mar 2024 13:40:23 -0500 Subject: [PATCH 04/13] Add `abs` parameter validation. (#3455) This CL adds validation for invalid `abs` parameters. --- src/webgpu/listing_meta.json | 1 + .../expression/call/builtin/abs.spec.ts | 104 ++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index b3081b5763dd..95851c200be1 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1807,6 +1807,7 @@ "webgpu:shader,validation,expression,binary,bitwise_shift:shift_left_vec_size_mismatch:*": { "subcaseMS": 1.367 }, "webgpu:shader,validation,expression,binary,bitwise_shift:shift_right_concrete:*": { "subcaseMS": 1.237 }, "webgpu:shader,validation,expression,binary,bitwise_shift:shift_right_vec_size_mismatch:*": { "subcaseMS": 1.334 }, + "webgpu:shader,validation,expression,call,builtin,abs:parameters:*": { "subcaseMS": 10.133 }, "webgpu:shader,validation,expression,call,builtin,abs:values:*": { "subcaseMS": 0.391 }, "webgpu:shader,validation,expression,call,builtin,acos:integer_argument:*": { "subcaseMS": 1.512 }, "webgpu:shader,validation,expression,call,builtin,acos:values:*": { "subcaseMS": 0.342 }, diff --git a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts index f09b47264f33..b51fc02ade0d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts @@ -56,3 +56,107 @@ Validates that constant evaluation and override evaluation of ${builtin}() never t.params.stage ); }); + +const kTests = { + valid: { + src: `_ = abs(1);`, + pass: true, + }, + alias: { + src: `_ = abs(i32_alias(1));`, + pass: true, + }, + + bool: { + src: `_ = abs(false);`, + pass: false, + }, + vec_bool: { + src: `_ = abs(vec2(false, true));`, + pass: false, + }, + matrix: { + src: `_ = abs(mat2x2(1, 1, 1, 1));`, + pass: false, + }, + atomic: { + src: ` _ = abs(a);`, + pass: false, + }, + array: { + src: `var a: array; + _ = abs(a);`, + pass: false, + }, + array_runtime: { + src: `_ = abs(k.arry);`, + pass: false, + }, + struct: { + src: `var a: A; + _ = abs(a);`, + pass: false, + }, + enumerant: { + src: `_ = abs(read_write);`, + pass: false, + }, + ptr: { + src: `var a = 1u; + let p: ptr = &a; + _ = abs(p);`, + pass: false, + }, + ptr_deref: { + src: `var a = 1u; + let p: ptr = &a; + _ = abs(*p);`, + pass: true, + }, + sampler: { + src: `_ = abs(s);`, + pass: false, + }, + texture: { + src: `_ = abs(t);`, + pass: false, + }, + no_params: { + src: `_ = abs();`, + pass: false, + }, + too_many_params: { + src: `_ = abs(1, 2);`, + pass: false, + }, +}; + +g.test('parameters') + .desc(`Test that ${builtin} is validated correctly.`) + .params(u => u.combine('test', keysOf(kTests))) + .fn(t => { + const src = kTests[t.params.test].src; + const code = ` +alias i32_alias = i32; + +@group(0) @binding(0) var s: sampler; +@group(0) @binding(1) var t: texture_2d; + +var a: atomic; + +struct A { + i: u32, +} +struct B { + arry: array, +} +@group(0) @binding(3) var k: B; + + +@vertex +fn main() -> @builtin(position) vec4 { + ${src} + return vec4(.4, .2, .3, .1); +}`; + t.expectCompileResult(kTests[t.params.test].pass, code); + }); From a93708e6e14220fde775404348ac7c13f17d5e92 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Wed, 6 Mar 2024 14:16:04 -0500 Subject: [PATCH 05/13] wgsl: Implement AbstractFloat matrix multiplcation tests (#3446) Adds in a hash based filtering utility for reducing number of test cases being generated. Issue #1626 --- src/resources/cache/hashes.json | 203 +++++++++--------- .../af_matrix_matrix_multiplication.bin | Bin 0 -> 1617464 bytes .../af_matrix_scalar_multiplication.bin | Bin 0 -> 963656 bytes .../af_matrix_vector_multiplication.bin | Bin 0 -> 577904 bytes src/unittests/floating_point.spec.ts | 26 ++- src/webgpu/listing_meta.json | 5 + .../af_matrix_matrix_multiplication.cache.ts | 28 +++ .../af_matrix_matrix_multiplication.spec.ts | 48 +++++ .../af_matrix_scalar_multiplication.cache.ts | 49 +++++ .../af_matrix_scalar_multiplication.spec.ts | 69 ++++++ .../af_matrix_vector_multiplication.cache.ts | 49 +++++ .../af_matrix_vector_multiplication.spec.ts | 69 ++++++ .../shader/execution/expression/case.ts | 49 ++++- src/webgpu/util/floating_point.ts | 30 +-- 14 files changed, 496 insertions(+), 129 deletions(-) create mode 100644 src/resources/cache/webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin create mode 100644 src/resources/cache/webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin create mode 100644 src/resources/cache/webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin create mode 100644 src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.cache.ts create mode 100644 src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts create mode 100644 src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.cache.ts create mode 100644 src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts create mode 100644 src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.cache.ts create mode 100644 src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 9476d8431f41..48c819594290 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,107 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "38b261fa", - "webgpu/shader/execution/binary/af_logical.bin": "a483b968", - "webgpu/shader/execution/binary/af_division.bin": "ec39b0da", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "ca1373a8", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "406d99af", - "webgpu/shader/execution/binary/af_multiplication.bin": "2eb6d50d", - "webgpu/shader/execution/binary/af_remainder.bin": "e2b6b21", - "webgpu/shader/execution/binary/af_subtraction.bin": "84794350", - "webgpu/shader/execution/binary/f16_addition.bin": "19e8823d", - "webgpu/shader/execution/binary/f16_logical.bin": "b89ca9b9", - "webgpu/shader/execution/binary/f16_division.bin": "6dc4b748", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "7533842", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "2d799920", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "44e3b295", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "412f0911", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "a231201b", - "webgpu/shader/execution/binary/f16_multiplication.bin": "94b11030", - "webgpu/shader/execution/binary/f16_remainder.bin": "de68a200", - "webgpu/shader/execution/binary/f16_subtraction.bin": "f308327a", - "webgpu/shader/execution/binary/f32_addition.bin": "c87c8c08", - "webgpu/shader/execution/binary/f32_logical.bin": "c7370c09", - "webgpu/shader/execution/binary/f32_division.bin": "34ce65ae", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "f3808d0c", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "e33e7fe5", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "41091ebf", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "37ccb101", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "4d67866", - "webgpu/shader/execution/binary/f32_multiplication.bin": "5d85a36c", - "webgpu/shader/execution/binary/f32_remainder.bin": "62f591b2", - "webgpu/shader/execution/binary/f32_subtraction.bin": "60fc275a", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "a0b0a016", - "webgpu/shader/execution/binary/i32_comparison.bin": "f3d9b3f9", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "16e32fd", - "webgpu/shader/execution/binary/u32_comparison.bin": "da33cc5d", - "webgpu/shader/execution/abs.bin": "1ead834c", - "webgpu/shader/execution/acos.bin": "e25802ba", - "webgpu/shader/execution/acosh.bin": "2321726f", - "webgpu/shader/execution/asin.bin": "d554a73b", - "webgpu/shader/execution/asinh.bin": "d2bdb21b", - "webgpu/shader/execution/atan.bin": "eb6476f3", - "webgpu/shader/execution/atan2.bin": "cf15e7fa", - "webgpu/shader/execution/atanh.bin": "6c57cc3", - "webgpu/shader/execution/bitcast.bin": "a25e9714", - "webgpu/shader/execution/ceil.bin": "8d120ea3", - "webgpu/shader/execution/clamp.bin": "a762ef58", - "webgpu/shader/execution/cos.bin": "a859da89", - "webgpu/shader/execution/cosh.bin": "86abdd85", - "webgpu/shader/execution/cross.bin": "e4556729", - "webgpu/shader/execution/degrees.bin": "1fa19a41", - "webgpu/shader/execution/determinant.bin": "108c3d65", - "webgpu/shader/execution/distance.bin": "77a1baa6", - "webgpu/shader/execution/dot.bin": "d4ac2e8a", - "webgpu/shader/execution/exp.bin": "15539afd", - "webgpu/shader/execution/exp2.bin": "7f6a8523", - "webgpu/shader/execution/faceForward.bin": "e7b35f43", - "webgpu/shader/execution/floor.bin": "b26656ca", - "webgpu/shader/execution/fma.bin": "5a70c683", - "webgpu/shader/execution/fract.bin": "23c0d5ec", - "webgpu/shader/execution/frexp.bin": "d28e66be", - "webgpu/shader/execution/inverseSqrt.bin": "9f297854", - "webgpu/shader/execution/ldexp.bin": "638db0c7", - "webgpu/shader/execution/length.bin": "7d237c62", - "webgpu/shader/execution/log.bin": "70720bf0", - "webgpu/shader/execution/log2.bin": "93a309be", - "webgpu/shader/execution/max.bin": "36eb4779", - "webgpu/shader/execution/min.bin": "ca772bf1", - "webgpu/shader/execution/mix.bin": "ecbf61ae", - "webgpu/shader/execution/modf.bin": "6ddea900", - "webgpu/shader/execution/normalize.bin": "d3e47c61", - "webgpu/shader/execution/pack2x16float.bin": "e6859c1a", - "webgpu/shader/execution/pow.bin": "a58be71c", - "webgpu/shader/execution/quantizeToF16.bin": "eca85bca", - "webgpu/shader/execution/radians.bin": "a216c9aa", - "webgpu/shader/execution/reflect.bin": "ebce9830", - "webgpu/shader/execution/refract.bin": "59d1e5d6", - "webgpu/shader/execution/round.bin": "9389a090", - "webgpu/shader/execution/saturate.bin": "7ca4b681", - "webgpu/shader/execution/sign.bin": "1f4eeb34", - "webgpu/shader/execution/sin.bin": "a1e234b4", - "webgpu/shader/execution/sinh.bin": "1a62054b", - "webgpu/shader/execution/smoothstep.bin": "d5824fd6", - "webgpu/shader/execution/sqrt.bin": "66f21d02", - "webgpu/shader/execution/step.bin": "310cb6c7", - "webgpu/shader/execution/tan.bin": "1e26f533", - "webgpu/shader/execution/tanh.bin": "4c546d1c", - "webgpu/shader/execution/transpose.bin": "7bef2494", - "webgpu/shader/execution/trunc.bin": "e72535eb", - "webgpu/shader/execution/unpack2x16float.bin": "593d88c6", - "webgpu/shader/execution/unpack2x16snorm.bin": "9ebd3e40", - "webgpu/shader/execution/unpack2x16unorm.bin": "83a36fa9", - "webgpu/shader/execution/unpack4x8snorm.bin": "41b12606", - "webgpu/shader/execution/unpack4x8unorm.bin": "96f1850b", - "webgpu/shader/execution/unary/af_arithmetic.bin": "6fa1d84a", - "webgpu/shader/execution/unary/af_assignment.bin": "98c8f82a", + "webgpu/shader/execution/binary/af_addition.bin": "de575056", + "webgpu/shader/execution/binary/af_logical.bin": "dc2105f8", + "webgpu/shader/execution/binary/af_division.bin": "d7e6d98f", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "c215cf6d", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "57892276", + "webgpu/shader/execution/binary/af_multiplication.bin": "4c282ac2", + "webgpu/shader/execution/binary/af_remainder.bin": "9fdddf97", + "webgpu/shader/execution/binary/af_subtraction.bin": "a27de3c1", + "webgpu/shader/execution/binary/f16_addition.bin": "ecc2aa17", + "webgpu/shader/execution/binary/f16_logical.bin": "1851f647", + "webgpu/shader/execution/binary/f16_division.bin": "2cfec6de", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "f5b6ef4f", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "c47070a0", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "30b9d67c", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "46b631ba", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "19e6a937", + "webgpu/shader/execution/binary/f16_multiplication.bin": "8fbfc97c", + "webgpu/shader/execution/binary/f16_remainder.bin": "66cd384c", + "webgpu/shader/execution/binary/f16_subtraction.bin": "8b5fed3d", + "webgpu/shader/execution/binary/f32_addition.bin": "2ef1211a", + "webgpu/shader/execution/binary/f32_logical.bin": "3c97c69d", + "webgpu/shader/execution/binary/f32_division.bin": "2867ef0a", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "da9390d1", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "2d67296e", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "c79709f5", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "38b7c05f", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "f9b675d7", + "webgpu/shader/execution/binary/f32_multiplication.bin": "bb7ee512", + "webgpu/shader/execution/binary/f32_remainder.bin": "e0d16b8f", + "webgpu/shader/execution/binary/f32_subtraction.bin": "755fc63", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "3c1d6f0f", + "webgpu/shader/execution/binary/i32_comparison.bin": "4759dfea", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "6bf6989d", + "webgpu/shader/execution/binary/u32_comparison.bin": "ca8b140b", + "webgpu/shader/execution/abs.bin": "a79b85f3", + "webgpu/shader/execution/acos.bin": "eed1c72", + "webgpu/shader/execution/acosh.bin": "a1b7dc12", + "webgpu/shader/execution/asin.bin": "fbf69cb0", + "webgpu/shader/execution/asinh.bin": "7b8f7a8", + "webgpu/shader/execution/atan.bin": "250334d8", + "webgpu/shader/execution/atan2.bin": "9df3f787", + "webgpu/shader/execution/atanh.bin": "5c79c30d", + "webgpu/shader/execution/bitcast.bin": "964fdecd", + "webgpu/shader/execution/ceil.bin": "246bf087", + "webgpu/shader/execution/clamp.bin": "3a299eaf", + "webgpu/shader/execution/cos.bin": "d3efc52b", + "webgpu/shader/execution/cosh.bin": "867cbf85", + "webgpu/shader/execution/cross.bin": "a1089567", + "webgpu/shader/execution/degrees.bin": "d1cfaeac", + "webgpu/shader/execution/determinant.bin": "44faf0f8", + "webgpu/shader/execution/distance.bin": "e1191c92", + "webgpu/shader/execution/dot.bin": "242201b", + "webgpu/shader/execution/exp.bin": "e5f97f39", + "webgpu/shader/execution/exp2.bin": "65bd37ec", + "webgpu/shader/execution/faceForward.bin": "ebb6017a", + "webgpu/shader/execution/floor.bin": "a24e0ff8", + "webgpu/shader/execution/fma.bin": "87615a5f", + "webgpu/shader/execution/fract.bin": "eab1b9fa", + "webgpu/shader/execution/frexp.bin": "7dd8033", + "webgpu/shader/execution/inverseSqrt.bin": "356b47c5", + "webgpu/shader/execution/ldexp.bin": "788fdf3e", + "webgpu/shader/execution/length.bin": "69f13c20", + "webgpu/shader/execution/log.bin": "dc9c311c", + "webgpu/shader/execution/log2.bin": "d1a49443", + "webgpu/shader/execution/max.bin": "6750f2eb", + "webgpu/shader/execution/min.bin": "c8200395", + "webgpu/shader/execution/mix.bin": "86c40712", + "webgpu/shader/execution/modf.bin": "50483a83", + "webgpu/shader/execution/normalize.bin": "244a8e05", + "webgpu/shader/execution/pack2x16float.bin": "dcd8656d", + "webgpu/shader/execution/pow.bin": "633c917a", + "webgpu/shader/execution/quantizeToF16.bin": "f6044bd2", + "webgpu/shader/execution/radians.bin": "a90b21ea", + "webgpu/shader/execution/reflect.bin": "670fbba2", + "webgpu/shader/execution/refract.bin": "63b06feb", + "webgpu/shader/execution/round.bin": "d4c09bde", + "webgpu/shader/execution/saturate.bin": "d4f8a4d0", + "webgpu/shader/execution/sign.bin": "57c988b9", + "webgpu/shader/execution/sin.bin": "59aab9f5", + "webgpu/shader/execution/sinh.bin": "3890a90c", + "webgpu/shader/execution/smoothstep.bin": "b695fd45", + "webgpu/shader/execution/sqrt.bin": "9524c93", + "webgpu/shader/execution/step.bin": "b9cc90a4", + "webgpu/shader/execution/tan.bin": "e5792957", + "webgpu/shader/execution/tanh.bin": "ba99c688", + "webgpu/shader/execution/transpose.bin": "83588805", + "webgpu/shader/execution/trunc.bin": "aad5d037", + "webgpu/shader/execution/unpack2x16float.bin": "493cbe7b", + "webgpu/shader/execution/unpack2x16snorm.bin": "fd0b5eb9", + "webgpu/shader/execution/unpack2x16unorm.bin": "f7436a6c", + "webgpu/shader/execution/unpack4x8snorm.bin": "eca842d9", + "webgpu/shader/execution/unpack4x8unorm.bin": "8654f67e", + "webgpu/shader/execution/unary/af_arithmetic.bin": "e05d3c45", + "webgpu/shader/execution/unary/af_assignment.bin": "45da8cfe", "webgpu/shader/execution/unary/bool_conversion.bin": "dd71f171", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "2f2d38fc", - "webgpu/shader/execution/unary/f16_conversion.bin": "70c94538", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "db90c01c", - "webgpu/shader/execution/unary/f32_conversion.bin": "81912140", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "9c17fdca", + "webgpu/shader/execution/unary/f16_conversion.bin": "c02b6c8", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "feff26f7", + "webgpu/shader/execution/unary/f32_conversion.bin": "f2639f4c", "webgpu/shader/execution/unary/i32_arithmetic.bin": "c69716e2", "webgpu/shader/execution/unary/i32_conversion.bin": "83218e69", "webgpu/shader/execution/unary/u32_conversion.bin": "8f5bad00", "webgpu/shader/execution/unary/ai_assignment.bin": "c7e6ac33", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "81c11ec2", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "3d27dc97" + "webgpu/shader/execution/binary/ai_arithmetic.bin": "dfcd593a", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "3d27dc97", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "7e551ea1", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "fe7ea65b", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "2a98deaa" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin new file mode 100644 index 0000000000000000000000000000000000000000..5f8636b77c7d167a1e25bcbb4f8f05e07926429a GIT binary patch literal 1617464 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∈vB_cu?ep9Q*( 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 z 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 literal 0 HcmV?d00001 diff --git a/src/resources/cache/webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin b/src/resources/cache/webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin new file mode 100644 index 0000000000000000000000000000000000000000..0a61c01c1a46f2f819829a14c1e5f2a8c3c7cba3 GIT binary patch literal 577904 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 u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .combineWithParams([ // Only testing that different shapes of matrices are handled correctly @@ -7768,12 +7768,26 @@ const kMultiplicationMatrixScalarIntervalCases = { ], }, ] as MatrixScalarToMatrixCase[], + abstract: [ + // From https://github.com/gpuweb/cts/issues/3044 + { + matrix: [ + [kValue.f64.negative.min, 0], + [0, 0], + ], + scalar: kValue.f64.negative.subnormal.min, + expected: [ + [[0, reinterpretU64AsF64(0x400ffffffffffffdn)], 0], // [[0, 3.9999995...], 0], + [0, 0], + ], + }, + ] as MatrixScalarToMatrixCase[], } as const; g.test('multiplicationMatrixScalarInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .expandWithParams(p => { const trait = FP[p.trait]; @@ -7945,7 +7959,7 @@ interface MatrixVectorToVectorCase { g.test('multiplicationMatrixVectorInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .combineWithParams([ // Only testing that different shapes of matrices are handled correctly @@ -8062,7 +8076,7 @@ interface VectorMatrixToVectorCase { g.test('multiplicationVectorMatrixInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .combineWithParams([ // Only testing that different shapes of matrices are handled correctly @@ -8070,8 +8084,8 @@ g.test('multiplicationVectorMatrixInterval') // multiplicationVectorMatrixInterval uses DotIntervalOp for calculating // intervals, so the testing for dotInterval covers the actual interval // calculations. - // Keep all expected result integer no larger than 2047 to ensure that all result is exactly - // represeantable in both f32 and f16. + // Keep all expected result integer no larger than 2047 to ensure that + // all result is exactly representable in both f32 and f16. { vector: [1, 2], matrix: [ diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 95851c200be1..a046cec8e64f 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -914,7 +914,12 @@ "webgpu:shader,execution,expression,binary,af_division:vector:*": { "subcaseMS": 237.134 }, "webgpu:shader,execution,expression,binary,af_division:vector_scalar:*": { "subcaseMS": 580.000 }, "webgpu:shader,execution,expression,binary,af_matrix_addition:matrix:*": { "subcaseMS": 11169.534 }, + "webgpu:shader,execution,expression,binary,af_matrix_matrix_multiplication:matrix_matrix:*": { "subcaseMS": 0.000 }, + "webgpu:shader,execution,expression,binary,af_matrix_scalar_multiplication:matrix_scalar:*": { "subcaseMS": 0.000 }, + "webgpu:shader,execution,expression,binary,af_matrix_scalar_multiplication:scalar_matrix:*": { "subcaseMS": 0.000 }, "webgpu:shader,execution,expression,binary,af_matrix_subtraction:matrix:*": { "subcaseMS": 14060.956 }, + "webgpu:shader,execution,expression,binary,af_matrix_vector_multiplication:matrix_vector:*": { "subcaseMS": 0.000 }, + "webgpu:shader,execution,expression,binary,af_matrix_vector_multiplication:vector_matrix:*": { "subcaseMS": 0.000 }, "webgpu:shader,execution,expression,binary,af_multiplication:scalar:*": { "subcaseMS": 777.901 }, "webgpu:shader,execution,expression,binary,af_multiplication:scalar_vector:*": { "subcaseMS": 2025.534 }, "webgpu:shader,execution,expression,binary,af_multiplication:vector:*": { "subcaseMS": 710.667 }, diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.cache.ts new file mode 100644 index 000000000000..c7540083d6ff --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.cache.ts @@ -0,0 +1,28 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF64Range } from '../../../../util/math.js'; +import { selectNCases } from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matKxR_matCxK +const mat_mat_cases = ([2, 3, 4] as const) + .flatMap(k => + ([2, 3, 4] as const).flatMap(cols => + ([2, 3, 4] as const).map(rows => ({ + [`mat${k}x${rows}_mat${cols}x${k}`]: () => { + return selectNCases( + 'binary/af_matrix_matrix_multiplication', + 50, + FP.abstract.generateMatrixPairToMatrixCases( + sparseMatrixF64Range(k, rows), + sparseMatrixF64Range(cols, k), + 'finite', + FP.abstract.multiplicationMatrixMatrixInterval + ) + ); + }, + })) + ) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_matrix_matrix_multiplication', mat_mat_cases); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts new file mode 100644 index 000000000000..bb7fd668c296 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts @@ -0,0 +1,48 @@ +export const description = ` +Execution Tests for matrix-matrix AbstractFloat multiplication expression +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../gpu_test.js'; +import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { onlyConstInputSource, run } from '../expression.js'; + +import { d } from './af_matrix_matrix_multiplication.cache.js'; +import { abstractFloatBinary } from './binary.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('matrix_matrix') + .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') + .desc( + ` +Expression: x * y, where x is a matrix and y is a matrix +Accuracy: Correctly rounded +` + ) + .params(u => + u + .combine('inputSource', onlyConstInputSource) + .combine('common_dim', [2, 3, 4] as const) + .combine('x_rows', [2, 3, 4] as const) + .combine('y_cols', [2, 3, 4] as const) + ) + .beforeAllSubcases(t => { + t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] }); + }) + .fn(async t => { + const x_cols = t.params.common_dim; + const x_rows = t.params.x_rows; + const y_cols = t.params.y_cols; + const y_rows = t.params.common_dim; + + const cases = await d.get(`mat${x_cols}x${x_rows}_mat${y_cols}x${y_rows}`); + await run( + t, + abstractFloatBinary('*'), + [TypeMat(x_cols, x_rows, TypeAbstractFloat), TypeMat(y_cols, y_rows, TypeAbstractFloat)], + TypeMat(y_cols, x_rows, TypeAbstractFloat), + t.params, + cases + ); + }); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.cache.ts new file mode 100644 index 000000000000..910636297022 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.cache.ts @@ -0,0 +1,49 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF64Range, sparseScalarF64Range } from '../../../../util/math.js'; +import { selectNCases } from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_scalar +const mat_scalar_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).map(rows => ({ + [`mat${cols}x${rows}_scalar`]: () => { + return selectNCases( + 'binary/af_matrix_scalar_multiplication_mat_scalar', + 50, + FP.abstract.generateMatrixScalarToMatrixCases( + sparseMatrixF64Range(cols, rows), + sparseScalarF64Range(), + 'finite', + FP.abstract.multiplicationMatrixScalarInterval + ) + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: scalar_matCxR +const scalar_mat_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).map(rows => ({ + [`scalar_mat${cols}x${rows}`]: () => { + return selectNCases( + 'binary/af_matrix_scalar_multiplication_scalar_mat', + 50, + FP.abstract.generateScalarMatrixToMatrixCases( + sparseScalarF64Range(), + sparseMatrixF64Range(cols, rows), + 'finite', + FP.abstract.multiplicationScalarMatrixInterval + ) + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_matrix_scalar_multiplication', { + ...mat_scalar_cases, + ...scalar_mat_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts new file mode 100644 index 000000000000..05afa31275d6 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts @@ -0,0 +1,69 @@ +export const description = ` +Execution Tests for matrix-scalar and scalar-matrix AbstractFloat multiplication expression +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../gpu_test.js'; +import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { onlyConstInputSource, run } from '../expression.js'; + +import { d } from './af_matrix_scalar_multiplication.cache.js'; +import { abstractFloatBinary } from './binary.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('matrix_scalar') + .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') + .desc( + ` +Expression: x * y, where x is a matrix and y is a scalar +Accuracy: Correctly rounded +` + ) + .params(u => + u + .combine('inputSource', onlyConstInputSource) + .combine('cols', [2, 3, 4] as const) + .combine('rows', [2, 3, 4] as const) + ) + .fn(async t => { + const cols = t.params.cols; + const rows = t.params.rows; + const cases = await d.get(`mat${cols}x${rows}_scalar`); + await run( + t, + abstractFloatBinary('*'), + [TypeMat(cols, rows, TypeAbstractFloat), TypeAbstractFloat], + TypeMat(cols, rows, TypeAbstractFloat), + t.params, + cases + ); + }); + +g.test('scalar_matrix') + .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') + .desc( + ` +Expression: x * y, where x is a scalar and y is a matrix +Accuracy: Correctly rounded +` + ) + .params(u => + u + .combine('inputSource', onlyConstInputSource) + .combine('cols', [2, 3, 4] as const) + .combine('rows', [2, 3, 4] as const) + ) + .fn(async t => { + const cols = t.params.cols; + const rows = t.params.rows; + const cases = await d.get(`scalar_mat${cols}x${rows}`); + await run( + t, + abstractFloatBinary('*'), + [TypeAbstractFloat, TypeMat(cols, rows, TypeAbstractFloat)], + TypeMat(cols, rows, TypeAbstractFloat), + t.params, + cases + ); + }); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.cache.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.cache.ts new file mode 100644 index 000000000000..1401428d3da0 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.cache.ts @@ -0,0 +1,49 @@ +import { FP } from '../../../../util/floating_point.js'; +import { sparseMatrixF64Range, sparseVectorF64Range } from '../../../../util/math.js'; +import { selectNCases } from '../case.js'; +import { makeCaseCache } from '../case_cache.js'; + +// Cases: matCxR_vecC +const mat_vec_cases = ([2, 3, 4] as const) + .flatMap(cols => + ([2, 3, 4] as const).map(rows => ({ + [`mat${cols}x${rows}_vec${cols}`]: () => { + return selectNCases( + 'binary/af_matrix_vector_multiplication_mat_vec', + 50, + FP.abstract.generateMatrixVectorToVectorCases( + sparseMatrixF64Range(cols, rows), + sparseVectorF64Range(cols), + 'finite', + FP.abstract.multiplicationMatrixVectorInterval + ) + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +// Cases: vecR_matCxR +const vec_mat_cases = ([2, 3, 4] as const) + .flatMap(rows => + ([2, 3, 4] as const).map(cols => ({ + [`vec${rows}_mat${cols}x${rows}`]: () => { + return selectNCases( + 'binary/af_matrix_vector_multiplication_vec_mat', + 50, + FP.abstract.generateVectorMatrixToVectorCases( + sparseVectorF64Range(rows), + sparseMatrixF64Range(cols, rows), + 'finite', + FP.abstract.multiplicationVectorMatrixInterval + ) + ); + }, + })) + ) + .reduce((a, b) => ({ ...a, ...b }), {}); + +export const d = makeCaseCache('binary/af_matrix_vector_multiplication', { + ...mat_vec_cases, + ...vec_mat_cases, +}); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts new file mode 100644 index 000000000000..c464c442aa85 --- /dev/null +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts @@ -0,0 +1,69 @@ +export const description = ` +Execution Tests for matrix-vector and vector-matrix AbstractFloat multiplication expression +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { GPUTest } from '../../../../gpu_test.js'; +import { TypeAbstractFloat, TypeMat, TypeVec } from '../../../../util/conversion.js'; +import { onlyConstInputSource, run } from '../expression.js'; + +import { d } from './af_matrix_vector_multiplication.cache.js'; +import { abstractFloatBinary } from './binary.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('matrix_vector') + .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') + .desc( + ` +Expression: x * y, where x is a matrix and y is a vector +Accuracy: Correctly rounded +` + ) + .params(u => + u + .combine('inputSource', onlyConstInputSource) + .combine('cols', [2, 3, 4] as const) + .combine('rows', [2, 3, 4] as const) + ) + .fn(async t => { + const cols = t.params.cols; + const rows = t.params.rows; + const cases = await d.get(`mat${cols}x${rows}_vec${cols}`); + await run( + t, + abstractFloatBinary('*'), + [TypeMat(cols, rows, TypeAbstractFloat), TypeVec(cols, TypeAbstractFloat)], + TypeVec(rows, TypeAbstractFloat), + t.params, + cases + ); + }); + +g.test('vector_matrix') + .specURL('https://www.w3.org/TR/WGSL/#floating-point-evaluation') + .desc( + ` +Expression: x * y, where x is a vector and y is is a matrix +Accuracy: Correctly rounded +` + ) + .params(u => + u + .combine('inputSource', onlyConstInputSource) + .combine('cols', [2, 3, 4] as const) + .combine('rows', [2, 3, 4] as const) + ) + .fn(async t => { + const cols = t.params.cols; + const rows = t.params.rows; + const cases = await d.get(`vec${rows}_mat${cols}x${rows}`); + await run( + t, + abstractFloatBinary('*'), + [TypeVec(rows, TypeAbstractFloat), TypeMat(cols, rows, TypeAbstractFloat)], + TypeVec(cols, TypeAbstractFloat), + t.params, + cases + ); + }); diff --git a/src/webgpu/shader/execution/expression/case.ts b/src/webgpu/shader/execution/expression/case.ts index 1cbde7be754f..37e1d2a8f190 100644 --- a/src/webgpu/shader/execution/expression/case.ts +++ b/src/webgpu/shader/execution/expression/case.ts @@ -1,11 +1,13 @@ +import { crc32 } from '../../../../common/util/crc32.js'; import { ROArrayArray } from '../../../../common/util/types.js'; -import { ScalarBuilder, Value, Vector, i32, u32, abstractInt } from '../../../util/conversion.js'; +import { assert } from '../../../../common/util/util.js'; +import { abstractInt, i32, ScalarBuilder, u32, Value, Vector } from '../../../util/conversion.js'; import { - QuantizeFunc, cartesianProduct, + QuantizeFunc, quantizeToI32, - quantizeToU32, quantizeToI64, + quantizeToU32, } from '../../../util/math.js'; import { Expectation } from './expectation.js'; @@ -25,6 +27,47 @@ export type Case = { /** CaseList is a list of Cases */ export type CaseList = Array; +/** + * Filters a given set of Cases down to a target number of cases by + * randomly selecting which Cases to return. + * + * The selection algorithm is deterministic and stable for a case's + * inputs. + * + * This means that if a specific case is selected is not affected by the + * presence of other cases in the list, so in theory it is possible to create a + * pathological set of cases such that all or not of the cases are selected + * in spite of the target number. + * + * This is a trade-off from guaranteeing stability of the selected cases over + * small changes, so the target number of cases is more of a suggestion. It is + * still guaranteed that if you set n0 < n1, then the invocation with n0 will + * return at most the number of cases that n1 does, it just isn't guaranteed to + * be less. + * + * @param dis is a string provided for additional hashing information to avoid + * systemic bias in the selection process across different test + * suites. Specifically every Case with the same input values being + * included or skipped regardless of the operation that they are + * testing. This string should be something like the name of the case + * cache the values are for or the operation under test. + * @param n number of cases targeted be returned. Expected to be a positive + * integer. If equal or greater than the number of cases, then all the + * cases are returned. 0 is not allowed, since it is likely a + * programming error, because if the caller intentionally wants 0 + * items, they can just use []. + * @param cases list of Cases to be selected from. + */ +export function selectNCases(dis: string, n: number, cases: CaseList): CaseList { + assert(n > 0 && Math.round(n) === n, `n ${n} is expected to be a positive integer`); + const count = cases.length; + if (n >= count) { + return cases; + } + const dis_crc32 = crc32(dis); + return cases.filter(c => n * (0xffff_ffff / count) > (crc32(c.input.toString()) ^ dis_crc32)); +} + /** * A function that performs a binary operation on x and y, and returns the * expected result. diff --git a/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index 31b90081bfe6..ea7bb838d4ca 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -5164,26 +5164,16 @@ class FPAbstractTraits extends FPTraits { public readonly mixIntervals = [this.mixImpreciseInterval, this.mixPreciseInterval]; public readonly modfInterval = this.modfIntervalImpl.bind(this); public readonly multiplicationInterval = this.multiplicationIntervalImpl.bind(this); - public readonly multiplicationMatrixMatrixInterval = this.unimplementedMatrixPairToMatrix.bind( - this, - 'multiplicationMatrixMatrixInterval' - ); - public readonly multiplicationMatrixScalarInterval = this.unimplementedMatrixScalarToMatrix.bind( - this, - 'multiplicationMatrixScalarInterval' - ); - public readonly multiplicationScalarMatrixInterval = this.unimplementedScalarMatrixToMatrix.bind( - this, - 'multiplicationScalarMatrixInterval' - ); - public readonly multiplicationMatrixVectorInterval = this.unimplementedMatrixVectorToVector.bind( - this, - 'multiplicationMatrixVectorInterval' - ); - public readonly multiplicationVectorMatrixInterval = this.unimplementedVectorMatrixToVector.bind( - this, - 'multiplicationVectorMatrixInterval' - ); + public readonly multiplicationMatrixMatrixInterval = + this.multiplicationMatrixMatrixIntervalImpl.bind(this); + public readonly multiplicationMatrixScalarInterval = + this.multiplicationMatrixScalarIntervalImpl.bind(this); + public readonly multiplicationScalarMatrixInterval = + this.multiplicationScalarMatrixIntervalImpl.bind(this); + public readonly multiplicationMatrixVectorInterval = + this.multiplicationMatrixVectorIntervalImpl.bind(this); + public readonly multiplicationVectorMatrixInterval = + this.multiplicationVectorMatrixIntervalImpl.bind(this); public readonly negationInterval = this.negationIntervalImpl.bind(this); public readonly normalizeInterval = this.unimplementedVectorToVector.bind( this, From 561ab2a7cb4a9013d48d086515b882eda2bd14f5 Mon Sep 17 00:00:00 2001 From: alan-baker Date: Wed, 6 Mar 2024 18:19:19 -0500 Subject: [PATCH 06/13] User IO passthrough shader tests (#3454) * User-defined IO passthrough execution tests * Tests i32, u32, f32, and f16 * scalar, vec2, and vec4 * Passes values from vertex input -> vertex output -> fragment input -> fragment output * always uses uints at vertex input and fragment output for simplicity --- src/webgpu/listing_meta.json | 1 + .../execution/shader_io/user_io.spec.ts | 213 ++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 src/webgpu/shader/execution/shader_io/user_io.spec.ts diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index a046cec8e64f..065f3f8e9fda 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -1743,6 +1743,7 @@ "webgpu:shader,execution,shader_io,shared_structs:shared_between_stages:*": { "subcaseMS": 9.601 }, "webgpu:shader,execution,shader_io,shared_structs:shared_with_buffer:*": { "subcaseMS": 20.701 }, "webgpu:shader,execution,shader_io,shared_structs:shared_with_non_entry_point_function:*": { "subcaseMS": 6.801 }, + "webgpu:shader,execution,shader_io,user_io:passthrough:*": { "subcaseMS": 373.385 }, "webgpu:shader,execution,shader_io,workgroup_size:workgroup_size:*": { "subcaseMS": 0.000 }, "webgpu:shader,execution,shadow:builtin:*": { "subcaseMS": 4.700 }, "webgpu:shader,execution,shadow:declaration:*": { "subcaseMS": 9.700 }, diff --git a/src/webgpu/shader/execution/shader_io/user_io.spec.ts b/src/webgpu/shader/execution/shader_io/user_io.spec.ts new file mode 100644 index 000000000000..0c26f89872dc --- /dev/null +++ b/src/webgpu/shader/execution/shader_io/user_io.spec.ts @@ -0,0 +1,213 @@ +export const description = ` +Test for user-defined shader I/O. + +passthrough: + * Data passed into vertex shader as uints and converted to test type + * Passed from vertex to fragment as test type + * Output from fragment shader as uint +`; + +import { makeTestGroup } from '../../../../common/framework/test_group.js'; +import { range } from '../../../../common/util/util.js'; +import { GPUTest } from '../../../gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +function generateInterstagePassthroughCode(type: string): string { + return ` +${type === 'f16' ? 'enable f16;' : ''} +struct IOData { + @builtin(position) pos : vec4f, + @location(0) @interpolate(flat) user0 : ${type}, + @location(1) @interpolate(flat) user1 : vec2<${type}>, + @location(2) @interpolate(flat) user2 : vec4<${type}>, +} + +struct VertexInput { + @builtin(vertex_index) idx : u32, + @location(0) in0 : u32, + @location(1) in1 : vec2u, + @location(2) in2 : vec4u, +} + +@vertex +fn vsMain(input : VertexInput) -> IOData { + const vertices = array( + vec4f(-1, -1, 0, 1), + vec4f(-1, 1, 0, 1), + vec4f( 1, -1, 0, 1), + ); + var data : IOData; + data.pos = vertices[input.idx]; + data.user0 = ${type}(input.in0); + data.user1 = vec2<${type}>(input.in1); + data.user2 = vec4<${type}>(input.in2); + return data; +} + +struct FragOutput { + @location(0) out0 : u32, + @location(1) out1 : vec2u, + @location(2) out2 : vec4u, +} + +@fragment +fn fsMain(input : IOData) -> FragOutput { + var out : FragOutput; + out.out0 = u32(input.user0); + out.out1 = vec2u(input.user1); + out.out2 = vec4u(input.user2); + return out; +} +`; +} + +function drawPassthrough(t: GPUTest, code: string) { + // Default limit is 32 bytes of color attachments. + // These attachments use 28 bytes (which is why vec3 is skipped). + const formats: GPUTextureFormat[] = ['r32uint', 'rg32uint', 'rgba32uint']; + const components = [1, 2, 4]; + const pipeline = t.device.createRenderPipeline({ + layout: 'auto', + vertex: { + module: t.device.createShaderModule({ code }), + entryPoint: 'vsMain', + buffers: [ + { + arrayStride: 4, + attributes: [ + { + format: 'uint32', + offset: 0, + shaderLocation: 0, + }, + ], + }, + { + arrayStride: 8, + attributes: [ + { + format: 'uint32x2', + offset: 0, + shaderLocation: 1, + }, + ], + }, + { + arrayStride: 16, + attributes: [ + { + format: 'uint32x4', + offset: 0, + shaderLocation: 2, + }, + ], + }, + ], + }, + fragment: { + module: t.device.createShaderModule({ code }), + entryPoint: 'fsMain', + targets: formats.map(x => { + return { format: x }; + }), + }, + primitive: { + topology: 'triangle-list', + }, + }); + + const vertexBuffer = t.makeBufferWithContents( + new Uint32Array([ + // scalar: offset 0 + 1, 1, 1, 0, + // vec2: offset 16 + 2, 2, 2, 2, 2, 2, 0, 0, + // vec4: offset 48 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + ]), + GPUBufferUsage.COPY_SRC | GPUBufferUsage.VERTEX + ); + + const bytesPerComponent = 4; + // 256 is the minimum bytes per row for texture to buffer copies. + const width = 256 / bytesPerComponent; + const height = 2; + const copyWidth = 4; + const outputTextures = range(3, i => { + const texture = t.device.createTexture({ + size: [width, height], + usage: + GPUTextureUsage.COPY_SRC | + GPUTextureUsage.RENDER_ATTACHMENT | + GPUTextureUsage.TEXTURE_BINDING, + format: formats[i], + }); + t.trackForCleanup(texture); + return texture; + }); + + let bufferSize = 1; + for (const comp of components) { + bufferSize *= comp; + } + bufferSize *= outputTextures.length * bytesPerComponent * copyWidth; + const outputBuffer = t.device.createBuffer({ + size: bufferSize, + usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST, + }); + t.trackForCleanup(outputBuffer); + + const encoder = t.device.createCommandEncoder(); + const pass = encoder.beginRenderPass({ + colorAttachments: outputTextures.map(t => ({ + view: t.createView(), + loadOp: 'clear', + storeOp: 'store', + })), + }); + pass.setPipeline(pipeline); + pass.setVertexBuffer(0, vertexBuffer, 0, 12); + pass.setVertexBuffer(1, vertexBuffer, 16, 24); + pass.setVertexBuffer(2, vertexBuffer, 48, 48); + pass.draw(3); + pass.end(); + + // Copy 'copyWidth' samples from each attachment into a buffer to check the results. + let offset = 0; + let expectArray: number[] = []; + for (let i = 0; i < outputTextures.length; i++) { + encoder.copyTextureToBuffer( + { texture: outputTextures[i] }, + { + buffer: outputBuffer, + offset, + bytesPerRow: bytesPerComponent * components[i] * width, + rowsPerImage: height, + }, + { width: copyWidth, height: 1 } + ); + offset += components[i] * bytesPerComponent * copyWidth; + for (let j = 0; j < components[i]; j++) { + const value = i + 1; + expectArray = expectArray.concat([value, value, value, value]); + } + } + t.queue.submit([encoder.finish()]); + + const expect = new Uint32Array(expectArray); + t.expectGPUBufferValuesEqual(outputBuffer, expect); +} + +g.test('passthrough') + .desc('Tests passing user-defined data from vertex input through fragment output') + .params(u => u.combine('type', ['f32', 'f16', 'i32', 'u32'] as const)) + .beforeAllSubcases(t => { + if (t.params.type === 'f16') { + t.selectDeviceOrSkipTestCase('shader-f16'); + } + }) + .fn(t => { + const code = generateInterstagePassthroughCode(t.params.type); + drawPassthrough(t, code); + }); From ee19c8b9903e7cbed2b9648ab7d5c8978041e497 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Thu, 7 Mar 2024 15:19:56 -0500 Subject: [PATCH 07/13] wgsl: Replace CaseList with Case[] (#3463) Fixes #3458 --- src/resources/cache/hashes.json | 216 +++++++++--------- .../expression/binary/bitwise_shift.spec.ts | 14 +- .../expression/call/builtin/select.spec.ts | 4 +- .../shader/execution/expression/case.ts | 5 +- .../shader/execution/expression/case_cache.ts | 22 +- .../shader/execution/expression/expression.ts | 30 +-- 6 files changed, 144 insertions(+), 147 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 48c819594290..87d6fb0b51da 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "de575056", - "webgpu/shader/execution/binary/af_logical.bin": "dc2105f8", - "webgpu/shader/execution/binary/af_division.bin": "d7e6d98f", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "c215cf6d", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "57892276", - "webgpu/shader/execution/binary/af_multiplication.bin": "4c282ac2", - "webgpu/shader/execution/binary/af_remainder.bin": "9fdddf97", - "webgpu/shader/execution/binary/af_subtraction.bin": "a27de3c1", - "webgpu/shader/execution/binary/f16_addition.bin": "ecc2aa17", - "webgpu/shader/execution/binary/f16_logical.bin": "1851f647", - "webgpu/shader/execution/binary/f16_division.bin": "2cfec6de", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "f5b6ef4f", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "c47070a0", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "30b9d67c", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "46b631ba", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "19e6a937", - "webgpu/shader/execution/binary/f16_multiplication.bin": "8fbfc97c", - "webgpu/shader/execution/binary/f16_remainder.bin": "66cd384c", - "webgpu/shader/execution/binary/f16_subtraction.bin": "8b5fed3d", - "webgpu/shader/execution/binary/f32_addition.bin": "2ef1211a", - "webgpu/shader/execution/binary/f32_logical.bin": "3c97c69d", - "webgpu/shader/execution/binary/f32_division.bin": "2867ef0a", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "da9390d1", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "2d67296e", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "c79709f5", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "38b7c05f", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "f9b675d7", - "webgpu/shader/execution/binary/f32_multiplication.bin": "bb7ee512", - "webgpu/shader/execution/binary/f32_remainder.bin": "e0d16b8f", - "webgpu/shader/execution/binary/f32_subtraction.bin": "755fc63", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "3c1d6f0f", - "webgpu/shader/execution/binary/i32_comparison.bin": "4759dfea", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "6bf6989d", - "webgpu/shader/execution/binary/u32_comparison.bin": "ca8b140b", - "webgpu/shader/execution/abs.bin": "a79b85f3", - "webgpu/shader/execution/acos.bin": "eed1c72", - "webgpu/shader/execution/acosh.bin": "a1b7dc12", - "webgpu/shader/execution/asin.bin": "fbf69cb0", - "webgpu/shader/execution/asinh.bin": "7b8f7a8", - "webgpu/shader/execution/atan.bin": "250334d8", - "webgpu/shader/execution/atan2.bin": "9df3f787", - "webgpu/shader/execution/atanh.bin": "5c79c30d", - "webgpu/shader/execution/bitcast.bin": "964fdecd", - "webgpu/shader/execution/ceil.bin": "246bf087", - "webgpu/shader/execution/clamp.bin": "3a299eaf", - "webgpu/shader/execution/cos.bin": "d3efc52b", - "webgpu/shader/execution/cosh.bin": "867cbf85", - "webgpu/shader/execution/cross.bin": "a1089567", - "webgpu/shader/execution/degrees.bin": "d1cfaeac", - "webgpu/shader/execution/determinant.bin": "44faf0f8", - "webgpu/shader/execution/distance.bin": "e1191c92", - "webgpu/shader/execution/dot.bin": "242201b", - "webgpu/shader/execution/exp.bin": "e5f97f39", - "webgpu/shader/execution/exp2.bin": "65bd37ec", - "webgpu/shader/execution/faceForward.bin": "ebb6017a", - "webgpu/shader/execution/floor.bin": "a24e0ff8", - "webgpu/shader/execution/fma.bin": "87615a5f", - "webgpu/shader/execution/fract.bin": "eab1b9fa", - "webgpu/shader/execution/frexp.bin": "7dd8033", - "webgpu/shader/execution/inverseSqrt.bin": "356b47c5", - "webgpu/shader/execution/ldexp.bin": "788fdf3e", - "webgpu/shader/execution/length.bin": "69f13c20", - "webgpu/shader/execution/log.bin": "dc9c311c", - "webgpu/shader/execution/log2.bin": "d1a49443", - "webgpu/shader/execution/max.bin": "6750f2eb", - "webgpu/shader/execution/min.bin": "c8200395", - "webgpu/shader/execution/mix.bin": "86c40712", - "webgpu/shader/execution/modf.bin": "50483a83", - "webgpu/shader/execution/normalize.bin": "244a8e05", - "webgpu/shader/execution/pack2x16float.bin": "dcd8656d", - "webgpu/shader/execution/pow.bin": "633c917a", - "webgpu/shader/execution/quantizeToF16.bin": "f6044bd2", - "webgpu/shader/execution/radians.bin": "a90b21ea", - "webgpu/shader/execution/reflect.bin": "670fbba2", - "webgpu/shader/execution/refract.bin": "63b06feb", - "webgpu/shader/execution/round.bin": "d4c09bde", - "webgpu/shader/execution/saturate.bin": "d4f8a4d0", - "webgpu/shader/execution/sign.bin": "57c988b9", - "webgpu/shader/execution/sin.bin": "59aab9f5", - "webgpu/shader/execution/sinh.bin": "3890a90c", - "webgpu/shader/execution/smoothstep.bin": "b695fd45", - "webgpu/shader/execution/sqrt.bin": "9524c93", - "webgpu/shader/execution/step.bin": "b9cc90a4", - "webgpu/shader/execution/tan.bin": "e5792957", - "webgpu/shader/execution/tanh.bin": "ba99c688", - "webgpu/shader/execution/transpose.bin": "83588805", - "webgpu/shader/execution/trunc.bin": "aad5d037", - "webgpu/shader/execution/unpack2x16float.bin": "493cbe7b", - "webgpu/shader/execution/unpack2x16snorm.bin": "fd0b5eb9", - "webgpu/shader/execution/unpack2x16unorm.bin": "f7436a6c", - "webgpu/shader/execution/unpack4x8snorm.bin": "eca842d9", - "webgpu/shader/execution/unpack4x8unorm.bin": "8654f67e", - "webgpu/shader/execution/unary/af_arithmetic.bin": "e05d3c45", - "webgpu/shader/execution/unary/af_assignment.bin": "45da8cfe", - "webgpu/shader/execution/unary/bool_conversion.bin": "dd71f171", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "9c17fdca", - "webgpu/shader/execution/unary/f16_conversion.bin": "c02b6c8", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "feff26f7", - "webgpu/shader/execution/unary/f32_conversion.bin": "f2639f4c", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "c69716e2", - "webgpu/shader/execution/unary/i32_conversion.bin": "83218e69", - "webgpu/shader/execution/unary/u32_conversion.bin": "8f5bad00", - "webgpu/shader/execution/unary/ai_assignment.bin": "c7e6ac33", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "dfcd593a", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "3d27dc97", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "7e551ea1", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "fe7ea65b", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "2a98deaa" + "webgpu/shader/execution/binary/af_addition.bin": "4440d4b4", + "webgpu/shader/execution/binary/af_logical.bin": "c54d7fff", + "webgpu/shader/execution/binary/af_division.bin": "d17fa436", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "7a788e99", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "f84dd903", + "webgpu/shader/execution/binary/af_multiplication.bin": "5334f9e1", + "webgpu/shader/execution/binary/af_remainder.bin": "470a66d7", + "webgpu/shader/execution/binary/af_subtraction.bin": "2c78e197", + "webgpu/shader/execution/binary/f16_addition.bin": "a579fc47", + "webgpu/shader/execution/binary/f16_logical.bin": "1f3267c4", + "webgpu/shader/execution/binary/f16_division.bin": "dfb9f322", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "ca9128f5", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "88155047", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "15278661", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "aaa23b6", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "7d9e4d5c", + "webgpu/shader/execution/binary/f16_multiplication.bin": "f9d4994", + "webgpu/shader/execution/binary/f16_remainder.bin": "244c4ad6", + "webgpu/shader/execution/binary/f16_subtraction.bin": "ed388083", + "webgpu/shader/execution/binary/f32_addition.bin": "22c323e7", + "webgpu/shader/execution/binary/f32_logical.bin": "b5a1fcd6", + "webgpu/shader/execution/binary/f32_division.bin": "af0c6a91", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "cea1c11f", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "4ca215e2", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "e0ecc16f", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "cd1f8542", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "d4d592a1", + "webgpu/shader/execution/binary/f32_multiplication.bin": "bad73246", + "webgpu/shader/execution/binary/f32_remainder.bin": "fd8b81ca", + "webgpu/shader/execution/binary/f32_subtraction.bin": "ab7a62cd", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "390c929d", + "webgpu/shader/execution/binary/i32_comparison.bin": "21a872e9", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "b6785d3e", + "webgpu/shader/execution/binary/u32_comparison.bin": "20e75aaf", + "webgpu/shader/execution/abs.bin": "7a1a8101", + "webgpu/shader/execution/acos.bin": "4f35a566", + "webgpu/shader/execution/acosh.bin": "1ddcdd7f", + "webgpu/shader/execution/asin.bin": "8c73cd", + "webgpu/shader/execution/asinh.bin": "9737e4c9", + "webgpu/shader/execution/atan.bin": "1274adbb", + "webgpu/shader/execution/atan2.bin": "6d414c8c", + "webgpu/shader/execution/atanh.bin": "ae463ca8", + "webgpu/shader/execution/bitcast.bin": "6acaafbe", + "webgpu/shader/execution/ceil.bin": "891015dc", + "webgpu/shader/execution/clamp.bin": "b1a757bd", + "webgpu/shader/execution/cos.bin": "1754b8bf", + "webgpu/shader/execution/cosh.bin": "db77b7bb", + "webgpu/shader/execution/cross.bin": "b4242751", + "webgpu/shader/execution/degrees.bin": "c19fa30", + "webgpu/shader/execution/determinant.bin": "28eea1a1", + "webgpu/shader/execution/distance.bin": "286532ef", + "webgpu/shader/execution/dot.bin": "68354d0d", + "webgpu/shader/execution/exp.bin": "8df9e76d", + "webgpu/shader/execution/exp2.bin": "494e5007", + "webgpu/shader/execution/faceForward.bin": "944e22", + "webgpu/shader/execution/floor.bin": "24434e6e", + "webgpu/shader/execution/fma.bin": "14d92aba", + "webgpu/shader/execution/fract.bin": "7bc1425a", + "webgpu/shader/execution/frexp.bin": "f6631fec", + "webgpu/shader/execution/inverseSqrt.bin": "511e859", + "webgpu/shader/execution/ldexp.bin": "f5d530d6", + "webgpu/shader/execution/length.bin": "b3dac96e", + "webgpu/shader/execution/log.bin": "34156eea", + "webgpu/shader/execution/log2.bin": "877d274b", + "webgpu/shader/execution/max.bin": "42174209", + "webgpu/shader/execution/min.bin": "e3ebfbb8", + "webgpu/shader/execution/mix.bin": "fd942c9a", + "webgpu/shader/execution/modf.bin": "1679c273", + "webgpu/shader/execution/normalize.bin": "be31d825", + "webgpu/shader/execution/pack2x16float.bin": "7e3e86c3", + "webgpu/shader/execution/pow.bin": "bf4ec2d7", + "webgpu/shader/execution/quantizeToF16.bin": "5d067d96", + "webgpu/shader/execution/radians.bin": "ebe086b6", + "webgpu/shader/execution/reflect.bin": "700f38a0", + "webgpu/shader/execution/refract.bin": "c859267f", + "webgpu/shader/execution/round.bin": "88045cd4", + "webgpu/shader/execution/saturate.bin": "dae68a14", + "webgpu/shader/execution/sign.bin": "91b4c481", + "webgpu/shader/execution/sin.bin": "1300bbbf", + "webgpu/shader/execution/sinh.bin": "27ab484c", + "webgpu/shader/execution/smoothstep.bin": "cfcaa136", + "webgpu/shader/execution/sqrt.bin": "4ae8ec04", + "webgpu/shader/execution/step.bin": "a83d7ab5", + "webgpu/shader/execution/tan.bin": "68b7a197", + "webgpu/shader/execution/tanh.bin": "32474b6e", + "webgpu/shader/execution/transpose.bin": "84e5ec15", + "webgpu/shader/execution/trunc.bin": "2fb9f2b", + "webgpu/shader/execution/unpack2x16float.bin": "4eef83ca", + "webgpu/shader/execution/unpack2x16snorm.bin": "5a8de9dc", + "webgpu/shader/execution/unpack2x16unorm.bin": "24b228cc", + "webgpu/shader/execution/unpack4x8snorm.bin": "7d98eec7", + "webgpu/shader/execution/unpack4x8unorm.bin": "546459f9", + "webgpu/shader/execution/unary/af_arithmetic.bin": "e31cea5d", + "webgpu/shader/execution/unary/af_assignment.bin": "ce59880f", + "webgpu/shader/execution/unary/bool_conversion.bin": "1bae513f", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "9276efd8", + "webgpu/shader/execution/unary/f16_conversion.bin": "254f60da", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "ed5d236e", + "webgpu/shader/execution/unary/f32_conversion.bin": "c570be4c", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "cc46fe", + "webgpu/shader/execution/unary/i32_conversion.bin": "457ade75", + "webgpu/shader/execution/unary/u32_conversion.bin": "4900fd1c", + "webgpu/shader/execution/unary/ai_assignment.bin": "1bdfc2f", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "80388ed7", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "fb7c8c8b", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "64af08ac", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "58e9a3ca", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "77ca543f" } \ No newline at end of file diff --git a/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts b/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts index b47d66a47964..cea6b86c602a 100644 --- a/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts @@ -5,7 +5,7 @@ Execution Tests for the bitwise shift binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; import { i32, scalarType, ScalarType, TypeU32, u32 } from '../../../../util/conversion.js'; -import { CaseList } from '../case.js'; +import { Case } from '../case.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -66,9 +66,9 @@ function is_valid_const_shift_right(e1: number, e1Type: string, e2: number) { // Returns all cases of shifting e1 left by [0,63]. If `is_const` is true, cases that are // invalid for const eval are not returned. -function generate_shift_left_cases(e1: number, e1Type: string, is_const: boolean): CaseList { +function generate_shift_left_cases(e1: number, e1Type: string, is_const: boolean): Case[] { const V = e1Type === 'i32' ? i32 : u32; - const cases: CaseList = []; + const cases: Case[] = []; for (let shift = 0; shift < 64; ++shift) { const e2 = shift; if (is_const && !is_valid_const_shift_left(e1, e1Type, e2)) { @@ -82,9 +82,9 @@ function generate_shift_left_cases(e1: number, e1Type: string, is_const: boolean // Returns all cases of shifting e1 right by [0,63]. If `is_const` is true, cases that are // invalid for const eval are not returned. -function generate_shift_right_cases(e1: number, e1Type: string, is_const: boolean): CaseList { +function generate_shift_right_cases(e1: number, e1Type: string, is_const: boolean): Case[] { const V = e1Type === 'i32' ? i32 : u32; - const cases: CaseList = []; + const cases: Case[] = []; for (let shift = 0; shift < 64; ++shift) { const e2 = shift; if (is_const && !is_valid_const_shift_right(e1, e1Type, e2)) { @@ -108,7 +108,7 @@ function makeShiftLeftConcreteCases(inputType: string, inputSource: string, type const V = inputType === 'i32' ? i32 : u32; const is_const = inputSource === 'const'; - const cases: CaseList = [ + const cases: Case[] = [ { input: /* */ [V(0b00000000000000000000000000000001), u32(1)], expected: /**/ V(0b00000000000000000000000000000010), @@ -222,7 +222,7 @@ function makeShiftRightConcreteCases(inputType: string, inputSource: string, typ const V = inputType === 'i32' ? i32 : u32; const is_const = inputSource === 'const'; - const cases: CaseList = [ + const cases: Case[] = [ { input: /* */ [V(0b00000000000000000000000000000001), u32(1)], expected: /**/ V(0b00000000000000000000000000000000), diff --git a/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts index 757783f77da0..aa5ee45e84e2 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts @@ -36,7 +36,7 @@ import { abstractInt, Scalar, } from '../../../../../util/conversion.js'; -import { CaseList } from '../../case.js'; +import { Case } from '../../case.js'; import { run, allInputSources } from '../../expression.js'; import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js'; @@ -198,7 +198,7 @@ g.test('vector') const T = True; const F = False; - let tests: { dataType: VectorType; boolType: VectorType; cases: CaseList }; + let tests: { dataType: VectorType; boolType: VectorType; cases: Case[] }; switch (t.params.overload) { case 'vec2': { diff --git a/src/webgpu/shader/execution/expression/case.ts b/src/webgpu/shader/execution/expression/case.ts index 37e1d2a8f190..436f0a6964ad 100644 --- a/src/webgpu/shader/execution/expression/case.ts +++ b/src/webgpu/shader/execution/expression/case.ts @@ -24,9 +24,6 @@ export type Case = { expected: Expectation; }; -/** CaseList is a list of Cases */ -export type CaseList = Array; - /** * Filters a given set of Cases down to a target number of cases by * randomly selecting which Cases to return. @@ -58,7 +55,7 @@ export type CaseList = Array; * items, they can just use []. * @param cases list of Cases to be selected from. */ -export function selectNCases(dis: string, n: number, cases: CaseList): CaseList { +export function selectNCases(dis: string, n: number, cases: Case[]): Case[] { assert(n > 0 && Math.round(n) === n, `n ${n} is expected to be a positive integer`); const count = cases.length; if (n >= count) { diff --git a/src/webgpu/shader/execution/expression/case_cache.ts b/src/webgpu/shader/execution/expression/case_cache.ts index 1580cf3298ca..1991c78217e9 100644 --- a/src/webgpu/shader/execution/expression/case_cache.ts +++ b/src/webgpu/shader/execution/expression/case_cache.ts @@ -17,7 +17,7 @@ import { } from '../../../util/floating_point.js'; import { flatten2DArray, unflatten2DArray } from '../../../util/math.js'; -import { Case, CaseList } from './case.js'; +import { Case } from './case.js'; import { Expectation, isComparator } from './expectation.js'; enum SerializedExpectationKind { @@ -123,15 +123,15 @@ export function deserializeCase(s: BinaryStream): Case { return { input, expected }; } -/** CaseListBuilder is a function that builds a CaseList */ -export type CaseListBuilder = () => CaseList; +/** CaseListBuilder is a function that builds a list of cases, Case[] */ +export type CaseListBuilder = () => Case[]; /** - * CaseCache is a cache of CaseList. + * CaseCache is a cache of Case[]. * CaseCache implements the Cacheable interface, so the cases can be pre-built * and stored in the data cache, reducing computation costs at CTS runtime. */ -export class CaseCache implements Cacheable> { +export class CaseCache implements Cacheable> { /** * Constructor * @param name the name of the cache. This must be globally unique. @@ -143,7 +143,7 @@ export class CaseCache implements Cacheable> { } /** get() returns the list of cases with the given name */ - public async get(name: string): Promise { + public async get(name: string): Promise { const data = await dataCache.fetch(this); return data[name]; } @@ -152,8 +152,8 @@ export class CaseCache implements Cacheable> { * build() implements the Cacheable.build interface. * @returns the data. */ - build(): Promise> { - const built: Record = {}; + build(): Promise> { + const built: Record = {}; for (const name in this.builders) { const cases = this.builders[name](); built[name] = cases; @@ -165,7 +165,7 @@ export class CaseCache implements Cacheable> { * serialize() implements the Cacheable.serialize interface. * @returns the serialized data. */ - serialize(data: Record): Uint8Array { + serialize(data: Record): Uint8Array { const maxSize = 32 << 20; // 32MB - max size for a file const stream = new BinaryStream(new ArrayBuffer(maxSize)); stream.writeU32(Object.keys(data).length); @@ -180,9 +180,9 @@ export class CaseCache implements Cacheable> { * deserialize() implements the Cacheable.deserialize interface. * @returns the deserialize data. */ - deserialize(array: Uint8Array): Record { + deserialize(array: Uint8Array): Record { const s = new BinaryStream(array.buffer); - const casesByName: Record = {}; + const casesByName: Record = {}; const numRecords = s.readU32(); for (let i = 0; i < numRecords; i++) { const name = s.readString(); diff --git a/src/webgpu/shader/execution/expression/expression.ts b/src/webgpu/shader/execution/expression/expression.ts index 3eda441681a7..c622e6c5fdde 100644 --- a/src/webgpu/shader/execution/expression/expression.ts +++ b/src/webgpu/shader/execution/expression/expression.ts @@ -17,7 +17,7 @@ import { scalarTypeOf, } from '../../../util/conversion.js'; -import { Case, CaseList } from './case.js'; +import { Case } from './case.js'; import { toComparator } from './expectation.js'; /** The input value source */ @@ -267,7 +267,7 @@ export async function run( parameterTypes: Array, resultType: Type, cfg: Config = { inputSource: 'storage_r' }, - cases: CaseList, + cases: Case[], batch_size?: number ) { // If the 'vectorize' config option was provided, pack the cases into vectors. @@ -324,7 +324,7 @@ export async function run( } }; - const processBatch = async (batchCases: CaseList) => { + const processBatch = async (batchCases: Case[]) => { const checkBatch = await submitBatch( t, shaderBuilder, @@ -375,7 +375,7 @@ async function submitBatch( shaderBuilder: ShaderBuilder, parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], inputSource: InputSource, pipelineCache: PipelineCache ): Promise<() => void> { @@ -469,7 +469,7 @@ function map(v: T | readonly T[], fn: (value: T, index?: number) => U): U[ export type ShaderBuilder = ( parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], inputSource: InputSource ) => string; @@ -536,7 +536,7 @@ struct Output { function wgslValuesArray( parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], expressionBuilder: ExpressionBuilder ): string { return ` @@ -586,7 +586,7 @@ function basicExpressionShaderBody( expressionBuilder: ExpressionBuilder, parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], inputSource: InputSource ): string { assert( @@ -683,7 +683,7 @@ export function basicExpressionBuilder(expressionBuilder: ExpressionBuilder): Sh return ( parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], inputSource: InputSource ) => { return `\ @@ -706,7 +706,7 @@ export function basicExpressionWithPredeclarationBuilder( return ( parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], inputSource: InputSource ) => { return `\ @@ -726,7 +726,7 @@ export function compoundAssignmentBuilder(op: string): ShaderBuilder { return ( parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], inputSource: InputSource ) => { ////////////////////////////////////////////////////////////////////////// @@ -953,7 +953,7 @@ export function abstractFloatShaderBuilder(expressionBuilder: ExpressionBuilder) return ( parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], inputSource: InputSource ) => { assert(inputSource === 'const', `'abstract-float' results are only defined for const-eval`); @@ -1037,7 +1037,7 @@ export function abstractIntShaderBuilder(expressionBuilder: ExpressionBuilder): return ( parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], inputSource: InputSource ) => { assert(inputSource === 'const', `'abstract-int' results are only defined for const-eval`); @@ -1084,7 +1084,7 @@ async function buildPipeline( shaderBuilder: ShaderBuilder, parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], inputSource: InputSource, outputBuffer: GPUBuffer, pipelineCache: PipelineCache @@ -1194,9 +1194,9 @@ async function buildPipeline( function packScalarsToVector( parameterTypes: Array, resultType: Type, - cases: CaseList, + cases: Case[], vectorWidth: number -): { cases: CaseList; parameterTypes: Array; resultType: Type } { +): { cases: Case[]; parameterTypes: Array; resultType: Type } { // Validate that the parameters and return type are all vectorizable for (let i = 0; i < parameterTypes.length; i++) { const ty = parameterTypes[i]; From adaa4793aae30c2b96c94eaa72b93c61f9ea62ab Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Thu, 7 Mar 2024 15:34:14 -0500 Subject: [PATCH 08/13] Simplify some type constants (#3461) This CL adds `kAllScalarsAndVec` and a `kConvertableToFloatScalarsAndVec` helper types which allow removing a couple of the combined type helpers. --- src/resources/cache/hashes.json | 216 +++++++++--------- .../cache/webgpu/shader/execution/bitcast.bin | Bin 2221448 -> 2221448 bytes .../expression/call/builtin/abs.spec.ts | 12 +- .../expression/call/builtin/acos.spec.ts | 8 +- .../expression/call/builtin/acosh.spec.ts | 8 +- .../expression/call/builtin/asin.spec.ts | 8 +- .../expression/call/builtin/asinh.spec.ts | 8 +- .../expression/call/builtin/atan.spec.ts | 8 +- .../expression/call/builtin/atanh.spec.ts | 8 +- .../expression/call/builtin/ceil.spec.ts | 8 +- .../expression/call/builtin/clamp.spec.ts | 8 +- .../expression/call/builtin/cos.spec.ts | 9 +- .../expression/call/builtin/cosh.spec.ts | 8 +- .../expression/call/builtin/degrees.spec.ts | 8 +- .../expression/call/builtin/exp.spec.ts | 10 +- .../expression/call/builtin/exp2.spec.ts | 10 +- .../expression/call/builtin/floor.spec.ts | 9 +- .../call/builtin/inverseSqrt.spec.ts | 9 +- .../expression/call/builtin/length.spec.ts | 20 +- .../expression/call/builtin/log.spec.ts | 8 +- .../expression/call/builtin/log2.spec.ts | 9 +- .../expression/call/builtin/modf.spec.ts | 8 +- .../expression/call/builtin/radians.spec.ts | 8 +- .../expression/call/builtin/round.spec.ts | 8 +- .../expression/call/builtin/saturate.spec.ts | 8 +- .../expression/call/builtin/sign.spec.ts | 8 +- .../expression/call/builtin/sin.spec.ts | 8 +- .../expression/call/builtin/sinh.spec.ts | 8 +- .../expression/call/builtin/sqrt.spec.ts | 8 +- .../expression/call/builtin/tan.spec.ts | 8 +- src/webgpu/util/conversion.ts | 72 +++--- 31 files changed, 214 insertions(+), 322 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 87d6fb0b51da..e92b0937b6d4 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "4440d4b4", - "webgpu/shader/execution/binary/af_logical.bin": "c54d7fff", - "webgpu/shader/execution/binary/af_division.bin": "d17fa436", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "7a788e99", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "f84dd903", - "webgpu/shader/execution/binary/af_multiplication.bin": "5334f9e1", - "webgpu/shader/execution/binary/af_remainder.bin": "470a66d7", - "webgpu/shader/execution/binary/af_subtraction.bin": "2c78e197", - "webgpu/shader/execution/binary/f16_addition.bin": "a579fc47", - "webgpu/shader/execution/binary/f16_logical.bin": "1f3267c4", - "webgpu/shader/execution/binary/f16_division.bin": "dfb9f322", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "ca9128f5", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "88155047", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "15278661", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "aaa23b6", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "7d9e4d5c", - "webgpu/shader/execution/binary/f16_multiplication.bin": "f9d4994", - "webgpu/shader/execution/binary/f16_remainder.bin": "244c4ad6", - "webgpu/shader/execution/binary/f16_subtraction.bin": "ed388083", - "webgpu/shader/execution/binary/f32_addition.bin": "22c323e7", - "webgpu/shader/execution/binary/f32_logical.bin": "b5a1fcd6", - "webgpu/shader/execution/binary/f32_division.bin": "af0c6a91", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "cea1c11f", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "4ca215e2", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "e0ecc16f", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "cd1f8542", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "d4d592a1", - "webgpu/shader/execution/binary/f32_multiplication.bin": "bad73246", - "webgpu/shader/execution/binary/f32_remainder.bin": "fd8b81ca", - "webgpu/shader/execution/binary/f32_subtraction.bin": "ab7a62cd", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "390c929d", - "webgpu/shader/execution/binary/i32_comparison.bin": "21a872e9", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "b6785d3e", - "webgpu/shader/execution/binary/u32_comparison.bin": "20e75aaf", - "webgpu/shader/execution/abs.bin": "7a1a8101", - "webgpu/shader/execution/acos.bin": "4f35a566", - "webgpu/shader/execution/acosh.bin": "1ddcdd7f", - "webgpu/shader/execution/asin.bin": "8c73cd", - "webgpu/shader/execution/asinh.bin": "9737e4c9", - "webgpu/shader/execution/atan.bin": "1274adbb", - "webgpu/shader/execution/atan2.bin": "6d414c8c", - "webgpu/shader/execution/atanh.bin": "ae463ca8", - "webgpu/shader/execution/bitcast.bin": "6acaafbe", - "webgpu/shader/execution/ceil.bin": "891015dc", - "webgpu/shader/execution/clamp.bin": "b1a757bd", - "webgpu/shader/execution/cos.bin": "1754b8bf", - "webgpu/shader/execution/cosh.bin": "db77b7bb", - "webgpu/shader/execution/cross.bin": "b4242751", - "webgpu/shader/execution/degrees.bin": "c19fa30", - "webgpu/shader/execution/determinant.bin": "28eea1a1", - "webgpu/shader/execution/distance.bin": "286532ef", - "webgpu/shader/execution/dot.bin": "68354d0d", - "webgpu/shader/execution/exp.bin": "8df9e76d", - "webgpu/shader/execution/exp2.bin": "494e5007", - "webgpu/shader/execution/faceForward.bin": "944e22", - "webgpu/shader/execution/floor.bin": "24434e6e", - "webgpu/shader/execution/fma.bin": "14d92aba", - "webgpu/shader/execution/fract.bin": "7bc1425a", - "webgpu/shader/execution/frexp.bin": "f6631fec", - "webgpu/shader/execution/inverseSqrt.bin": "511e859", - "webgpu/shader/execution/ldexp.bin": "f5d530d6", - "webgpu/shader/execution/length.bin": "b3dac96e", - "webgpu/shader/execution/log.bin": "34156eea", - "webgpu/shader/execution/log2.bin": "877d274b", - "webgpu/shader/execution/max.bin": "42174209", - "webgpu/shader/execution/min.bin": "e3ebfbb8", - "webgpu/shader/execution/mix.bin": "fd942c9a", - "webgpu/shader/execution/modf.bin": "1679c273", - "webgpu/shader/execution/normalize.bin": "be31d825", - "webgpu/shader/execution/pack2x16float.bin": "7e3e86c3", - "webgpu/shader/execution/pow.bin": "bf4ec2d7", - "webgpu/shader/execution/quantizeToF16.bin": "5d067d96", - "webgpu/shader/execution/radians.bin": "ebe086b6", - "webgpu/shader/execution/reflect.bin": "700f38a0", - "webgpu/shader/execution/refract.bin": "c859267f", - "webgpu/shader/execution/round.bin": "88045cd4", - "webgpu/shader/execution/saturate.bin": "dae68a14", - "webgpu/shader/execution/sign.bin": "91b4c481", - "webgpu/shader/execution/sin.bin": "1300bbbf", - "webgpu/shader/execution/sinh.bin": "27ab484c", - "webgpu/shader/execution/smoothstep.bin": "cfcaa136", - "webgpu/shader/execution/sqrt.bin": "4ae8ec04", - "webgpu/shader/execution/step.bin": "a83d7ab5", - "webgpu/shader/execution/tan.bin": "68b7a197", - "webgpu/shader/execution/tanh.bin": "32474b6e", - "webgpu/shader/execution/transpose.bin": "84e5ec15", - "webgpu/shader/execution/trunc.bin": "2fb9f2b", - "webgpu/shader/execution/unpack2x16float.bin": "4eef83ca", - "webgpu/shader/execution/unpack2x16snorm.bin": "5a8de9dc", - "webgpu/shader/execution/unpack2x16unorm.bin": "24b228cc", - "webgpu/shader/execution/unpack4x8snorm.bin": "7d98eec7", - "webgpu/shader/execution/unpack4x8unorm.bin": "546459f9", - "webgpu/shader/execution/unary/af_arithmetic.bin": "e31cea5d", - "webgpu/shader/execution/unary/af_assignment.bin": "ce59880f", - "webgpu/shader/execution/unary/bool_conversion.bin": "1bae513f", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "9276efd8", - "webgpu/shader/execution/unary/f16_conversion.bin": "254f60da", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "ed5d236e", - "webgpu/shader/execution/unary/f32_conversion.bin": "c570be4c", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "cc46fe", - "webgpu/shader/execution/unary/i32_conversion.bin": "457ade75", - "webgpu/shader/execution/unary/u32_conversion.bin": "4900fd1c", - "webgpu/shader/execution/unary/ai_assignment.bin": "1bdfc2f", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "80388ed7", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "fb7c8c8b", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "64af08ac", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "58e9a3ca", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "77ca543f" + "webgpu/shader/execution/binary/af_addition.bin": "1a138e40", + "webgpu/shader/execution/binary/af_logical.bin": "2a334d4b", + "webgpu/shader/execution/binary/af_division.bin": "801a63d2", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "d392437a", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "8a56ca30", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "3b36edc4", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "c2a167d1", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "3bdd724b", + "webgpu/shader/execution/binary/af_multiplication.bin": "af6472be", + "webgpu/shader/execution/binary/af_remainder.bin": "582570bc", + "webgpu/shader/execution/binary/af_subtraction.bin": "65e85cff", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "3876accd", + "webgpu/shader/execution/binary/f16_addition.bin": "12057229", + "webgpu/shader/execution/binary/f16_logical.bin": "b6ee9faf", + "webgpu/shader/execution/binary/f16_division.bin": "9f3479f0", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "a19f6dc5", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "be7786cc", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "afbc3dd2", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "84f6917", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "9036e4da", + "webgpu/shader/execution/binary/f16_multiplication.bin": "946528a5", + "webgpu/shader/execution/binary/f16_remainder.bin": "6e7cb89", + "webgpu/shader/execution/binary/f16_subtraction.bin": "55fe509d", + "webgpu/shader/execution/binary/f32_addition.bin": "3d2058bb", + "webgpu/shader/execution/binary/f32_logical.bin": "390ba598", + "webgpu/shader/execution/binary/f32_division.bin": "73fa9316", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "afb0e609", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "745cb3d9", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "1fa39b30", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "a0b386f7", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "89c1eca2", + "webgpu/shader/execution/binary/f32_multiplication.bin": "712f02a6", + "webgpu/shader/execution/binary/f32_remainder.bin": "8304c08d", + "webgpu/shader/execution/binary/f32_subtraction.bin": "ccdf094f", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "8a775ab8", + "webgpu/shader/execution/binary/i32_comparison.bin": "dd37b753", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "c3781404", + "webgpu/shader/execution/binary/u32_comparison.bin": "bed8f168", + "webgpu/shader/execution/abs.bin": "61e06f54", + "webgpu/shader/execution/acos.bin": "1402a723", + "webgpu/shader/execution/acosh.bin": "828c987f", + "webgpu/shader/execution/asin.bin": "557d3d3f", + "webgpu/shader/execution/asinh.bin": "be348f39", + "webgpu/shader/execution/atan.bin": "963520fc", + "webgpu/shader/execution/atan2.bin": "a912218b", + "webgpu/shader/execution/atanh.bin": "f46f32cc", + "webgpu/shader/execution/bitcast.bin": "44bd25ea", + "webgpu/shader/execution/ceil.bin": "7debbc4c", + "webgpu/shader/execution/clamp.bin": "26d3bfb", + "webgpu/shader/execution/cos.bin": "a64fea9a", + "webgpu/shader/execution/cosh.bin": "dfd5945e", + "webgpu/shader/execution/cross.bin": "4835bc3a", + "webgpu/shader/execution/degrees.bin": "528e631e", + "webgpu/shader/execution/determinant.bin": "e3866c2b", + "webgpu/shader/execution/distance.bin": "4d015170", + "webgpu/shader/execution/dot.bin": "c67a6363", + "webgpu/shader/execution/exp.bin": "c483696", + "webgpu/shader/execution/exp2.bin": "b248fa4b", + "webgpu/shader/execution/faceForward.bin": "429078d0", + "webgpu/shader/execution/floor.bin": "2f4fb64b", + "webgpu/shader/execution/fma.bin": "8cf2c018", + "webgpu/shader/execution/fract.bin": "4fe1141d", + "webgpu/shader/execution/frexp.bin": "38dd5724", + "webgpu/shader/execution/inverseSqrt.bin": "b010463d", + "webgpu/shader/execution/ldexp.bin": "94762c6c", + "webgpu/shader/execution/length.bin": "9007cea7", + "webgpu/shader/execution/log.bin": "8918d035", + "webgpu/shader/execution/log2.bin": "55ab0d73", + "webgpu/shader/execution/max.bin": "ea6871eb", + "webgpu/shader/execution/min.bin": "da3976b4", + "webgpu/shader/execution/mix.bin": "716aa87b", + "webgpu/shader/execution/modf.bin": "97fb95d0", + "webgpu/shader/execution/normalize.bin": "b4b3d66d", + "webgpu/shader/execution/pack2x16float.bin": "65dca45b", + "webgpu/shader/execution/pow.bin": "9c93c51e", + "webgpu/shader/execution/quantizeToF16.bin": "9bba3a59", + "webgpu/shader/execution/radians.bin": "31835823", + "webgpu/shader/execution/reflect.bin": "cef377fe", + "webgpu/shader/execution/refract.bin": "6a72b15f", + "webgpu/shader/execution/round.bin": "eb65ff2d", + "webgpu/shader/execution/saturate.bin": "6be2db42", + "webgpu/shader/execution/sign.bin": "6c0ac68f", + "webgpu/shader/execution/sin.bin": "6548201", + "webgpu/shader/execution/sinh.bin": "df389ed", + "webgpu/shader/execution/smoothstep.bin": "90af9360", + "webgpu/shader/execution/sqrt.bin": "37f1d81b", + "webgpu/shader/execution/step.bin": "a2a1103e", + "webgpu/shader/execution/tan.bin": "db82969", + "webgpu/shader/execution/tanh.bin": "9469a1c6", + "webgpu/shader/execution/transpose.bin": "51d503cb", + "webgpu/shader/execution/trunc.bin": "c5e6ed27", + "webgpu/shader/execution/unpack2x16float.bin": "a4812794", + "webgpu/shader/execution/unpack2x16snorm.bin": "48982cdb", + "webgpu/shader/execution/unpack2x16unorm.bin": "8a52d694", + "webgpu/shader/execution/unpack4x8snorm.bin": "de64f4f", + "webgpu/shader/execution/unpack4x8unorm.bin": "42074fbd", + "webgpu/shader/execution/unary/af_arithmetic.bin": "677d6662", + "webgpu/shader/execution/unary/af_assignment.bin": "9035f5c0", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "62304038", + "webgpu/shader/execution/unary/ai_assignment.bin": "d0ecdfb1", + "webgpu/shader/execution/unary/bool_conversion.bin": "9df31641", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "55060b95", + "webgpu/shader/execution/unary/f16_conversion.bin": "2a471445", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "de075203", + "webgpu/shader/execution/unary/f32_conversion.bin": "8c96deed", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "11e8bb3", + "webgpu/shader/execution/unary/i32_conversion.bin": "a3062e7d", + "webgpu/shader/execution/unary/u32_conversion.bin": "c1c5240c" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/bitcast.bin b/src/resources/cache/webgpu/shader/execution/bitcast.bin index d3954903ac89afcfbe5049a356005ed02cf10aae..3c24c88e1ffbc42f87639912bacab411154e6d63 100644 GIT binary patch delta 151 zcmWN=M-6~L006yPYE?jRVw)gr0)tx&a delta 152 zcmWN=%MpSw6hP4l3d%o-f*(*Rf!897WKSO%h7G$EpiObl%&~u?EEgxU8};PHcu@>^ r;E^FCo_J==gef!TELc*r;)OM@ys=@+J0E=V#g0AS{P4SZ8Gh>y9V9$( diff --git a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts index b51fc02ade0d..183fa296248f 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts @@ -5,12 +5,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; -import { - TypeF16, - elementType, - kAllFloatAndConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, -} from '../../../../../util/conversion.js'; +import { TypeF16, elementType, kAllScalarsAndVectors } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; import { @@ -22,10 +17,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatAndConcreteIntegerScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kAllScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts index 8883bd72e11c..dc9ed497574e 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -27,10 +26,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts index 29cb1aad44e1..31e44f373b0d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts @@ -9,10 +9,9 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, TypeAbstractInt, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -29,10 +28,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts index 82f426035729..5c698f589f74 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -27,10 +26,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts index 6cff91c384da..4e1fc37ffd5e 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -29,10 +28,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); const additionalRangeForType = rangeForType( linearRange(-2000, 2000, 10), diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts index 8ad4230bc58d..201ff5efd550 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -26,10 +25,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts index a299284c877b..d45df767c223 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -27,10 +26,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts index d415c4b9dd3c..bf76b3f2f11b 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -24,10 +23,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts index b05bc030f437..638c5ae2e884 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts @@ -8,7 +8,8 @@ import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tabl import { TypeF16, elementType, - kAllFloatAndConcreteIntegerScalarsAndVectors, + kAllFloatScalarsAndVectors, + kAllConcreteIntegerScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -21,7 +22,10 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord(kAllFloatAndConcreteIntegerScalarsAndVectors); +const kValuesTypes = objectsToRecord([ + ...kAllFloatScalarsAndVectors, + ...kAllConcreteIntegerScalarsAndVectors, +]); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts index b62c236803c0..7975d7d7c6d5 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -26,10 +25,8 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); + g.test('values') .desc( ` diff --git a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts index 55d0200ef16b..9a4864c4f972 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -26,10 +25,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts index 4e4137daf4bd..9c60548e01dd 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -26,10 +25,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts index a7a37fb08cdb..b4a76018f2a0 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts @@ -9,11 +9,10 @@ import { kValue } from '../../../../../util/constants.js'; import { TypeF16, TypeF32, + TypeAbstractFloat, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - TypeAbstractFloat, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -27,10 +26,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); const valueForType = rangeForType( [ diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts index 40a8542b8d95..36074ee42ecc 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts @@ -9,11 +9,10 @@ import { kValue } from '../../../../../util/constants.js'; import { TypeF16, TypeF32, + TypeAbstractFloat, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - TypeAbstractFloat, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -27,10 +26,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); const valueForType = rangeForType( [ diff --git a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts index 6a12c3d67db8..998ca5618344 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -24,10 +23,8 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); + g.test('values') .desc( ` diff --git a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts index f459303d580b..fc9351584b08 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts @@ -9,10 +9,9 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, TypeAbstractFloat, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -28,10 +27,8 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); + g.test('values') .desc( ` diff --git a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts index 1f24a86d9107..cb9be19963f2 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts @@ -10,16 +10,12 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalars, - kAllFloatVector2, - kAllFloatVector3, - kAllFloatVector4, kAllConcreteIntegerScalarsAndVectors, - kAbstractIntegerScalar, - kAbstractIntegerVector2, + kConvertableToFloatScalar, + kConvertableToFloatVec2, + kConvertableToFloatVec3, + kConvertableToFloatVec4, TypeAbstractFloat, - kAbstractIntegerVector3, - kAbstractIntegerVector4, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -72,7 +68,7 @@ function calculate( }; } -const kScalarTypes = objectsToRecord([...kAbstractIntegerScalar, ...kAllFloatScalars]); +const kScalarTypes = objectsToRecord(kConvertableToFloatScalar); g.test('scalar') .desc( @@ -106,7 +102,7 @@ the input scalar value always compiles without error ); }); -const kVec2Types = objectsToRecord([...kAbstractIntegerVector2, ...kAllFloatVector2]); +const kVec2Types = objectsToRecord(kConvertableToFloatVec2); g.test('vec2') .desc( @@ -141,7 +137,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with ); }); -const kVec3Types = objectsToRecord([...kAbstractIntegerVector3, ...kAllFloatVector3]); +const kVec3Types = objectsToRecord(kConvertableToFloatVec3); g.test('vec3') .desc( @@ -177,7 +173,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with ); }); -const kVec4Types = objectsToRecord([...kAbstractIntegerVector4, ...kAllFloatVector4]); +const kVec4Types = objectsToRecord(kConvertableToFloatVec4); g.test('vec4') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts index bffc433103e8..79036e5d86b9 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -24,10 +23,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts index 6eaf08993c8c..dff27e655c99 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -24,10 +23,8 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); + g.test('values') .desc( ` diff --git a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts index bdb0691436f4..6b809dc31ca1 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -24,10 +23,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts index 8cb7f62d92b9..3c9105a11fd6 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -24,10 +23,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts index 267c743c805f..39c170496d5e 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { fpTraitsFor } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -26,10 +25,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts index c7b0a50e9409..8ac53d8a4e9a 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -24,10 +23,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts index f844961aee27..b9d58a4cf9ca 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts @@ -9,7 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatAndSignedIntegerScalarsAndVectors, + kAllFloatScalarsAndVectors, + kAllSignedIntegerScalarsAndVectors, kAllUnsignedIntegerScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -23,7 +24,10 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord(kAllFloatAndSignedIntegerScalarsAndVectors); +const kValuesTypes = objectsToRecord([ + ...kAllFloatScalarsAndVectors, + ...kAllSignedIntegerScalarsAndVectors, +]); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts index 9e0d091e11d0..d300af4e26e9 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -26,10 +25,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts index d4c94e1a5290..6f7764c0dbd4 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -26,10 +25,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts index 0844652acdf6..96b3d8cbba5b 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts @@ -9,9 +9,8 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, + kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -28,10 +27,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts index 02616455a67e..7f24912476f5 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts @@ -9,10 +9,9 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, kAllConcreteIntegerScalarsAndVectors, - kAllAbstractIntegerScalarAndVectors, TypeAbstractFloat, + kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { fpTraitsFor } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -28,10 +27,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord([ - ...kAllAbstractIntegerScalarAndVectors, - ...kAllFloatScalarsAndVectors, -]); +const kValuesTypes = objectsToRecord(kConvertableToFloatScalarsAndVectors); g.test('values') .desc( diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index ac6ff5bbea38..6e4f09cc5940 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -1654,33 +1654,26 @@ export function isFloatType(ty: Type): boolean { export const kAllFloatScalars = [TypeAbstractFloat, TypeF32, TypeF16] as const; /// All floating-point vec2 types -export const kAllFloatVector2 = [ +const kAllFloatVec2 = [ TypeVec(2, TypeAbstractFloat), TypeVec(2, TypeF32), TypeVec(2, TypeF16), ] as const; /// All floating-point vec3 types -export const kAllFloatVector3 = [ +const kAllFloatVec3 = [ TypeVec(3, TypeAbstractFloat), TypeVec(3, TypeF32), TypeVec(3, TypeF16), ] as const; /// All floating-point vec4 types -export const kAllFloatVector4 = [ +const kAllFloatVec4 = [ TypeVec(4, TypeAbstractFloat), TypeVec(4, TypeF32), TypeVec(4, TypeF16), ] as const; -/// All floating-point vector types -export const kAllFloatVectors = [ - ...kAllFloatVector2, - ...kAllFloatVector3, - ...kAllFloatVector4, -] as const; - /// All f16 floating-point scalar and vector types export const kAllF16ScalarsAndVectors = [ TypeF16, @@ -1690,31 +1683,11 @@ export const kAllF16ScalarsAndVectors = [ ] as const; /// All floating-point scalar and vector types -export const kAllFloatScalarsAndVectors = [...kAllFloatScalars, ...kAllFloatVectors] as const; - -/// Abstract integer scalar type -export const kAbstractIntegerScalar = [TypeAbstractInt] as const; - -/// Abstract integer vec2 type -export const kAbstractIntegerVector2 = [TypeVec(2, TypeAbstractInt)] as const; - -/// Abstract integer vec3 type -export const kAbstractIntegerVector3 = [TypeVec(3, TypeAbstractInt)] as const; - -/// Abstract integer vec4 type -export const kAbstractIntegerVector4 = [TypeVec(4, TypeAbstractInt)] as const; - -/// All abstract integer scalar vector types -export const kAbstractIntegerVectors = [ - ...kAbstractIntegerVector2, - ...kAbstractIntegerVector3, - ...kAbstractIntegerVector4, -] as const; - -/// Abstract integer scalar and vector types -export const kAllAbstractIntegerScalarAndVectors = [ - ...kAbstractIntegerScalar, - ...kAbstractIntegerVectors, +export const kAllFloatScalarsAndVectors = [ + ...kAllFloatScalars, + ...kAllFloatVec2, + ...kAllFloatVec3, + ...kAllFloatVec4, ] as const; // Abstract and concrete integer types are not grouped into an 'all' type, @@ -1752,16 +1725,31 @@ export const kAllUnsignedIntegerScalarsAndVectors = [ TypeVec(4, TypeU32), ] as const; -/// All floating-point and integer scalar and vector types -export const kAllFloatAndConcreteIntegerScalarsAndVectors = [ +/// All types which are convertable to floating-point scalar types. +export const kConvertableToFloatScalar = [TypeAbstractInt, ...kAllFloatScalars] as const; + +/// All types which are convertable to floating-point vector 2 types. +export const kConvertableToFloatVec2 = [TypeVec(2, TypeAbstractInt), ...kAllFloatVec2] as const; + +/// All types which are convertable to floating-point vector 3 types. +export const kConvertableToFloatVec3 = [TypeVec(3, TypeAbstractInt), ...kAllFloatVec3] as const; + +/// All types which are convertable to floating-point vector 4 types. +export const kConvertableToFloatVec4 = [TypeVec(4, TypeAbstractInt), ...kAllFloatVec4] as const; + +/// All types which are convertable to floating-point scalar or vector types. +export const kConvertableToFloatScalarsAndVectors = [ + TypeAbstractInt, + TypeVec(2, TypeAbstractInt), + TypeVec(3, TypeAbstractInt), + TypeVec(4, TypeAbstractInt), ...kAllFloatScalarsAndVectors, - ...kAllConcreteIntegerScalarsAndVectors, ] as const; -/// All floating-point and signed integer scalar and vector types -export const kAllFloatAndSignedIntegerScalarsAndVectors = [ - ...kAllFloatScalarsAndVectors, - ...kAllSignedIntegerScalarsAndVectors, +/// All the scalar and vector types +export const kAllScalarsAndVectors = [ + ...kConvertableToFloatScalarsAndVectors, + ...kAllConcreteIntegerScalarsAndVectors, ] as const; /** @returns the inner element type of the given type */ From 485e4115fbeb6b992d411e2770913cf71d9777a3 Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Thu, 7 Mar 2024 16:08:34 -0500 Subject: [PATCH 09/13] Update All and Concrete type names (#3464) This CL updates some of the type exports to remove the `All` modifier and adds `Concrete` to a few exports for consistency. --- src/resources/cache/hashes.json | 216 +++++++++--------- .../expression/call/builtin/acos.spec.ts | 4 +- .../expression/call/builtin/acosh.spec.ts | 4 +- .../expression/call/builtin/asin.spec.ts | 4 +- .../expression/call/builtin/asinh.spec.ts | 4 +- .../expression/call/builtin/atan.spec.ts | 4 +- .../expression/call/builtin/atan2.spec.ts | 8 +- .../expression/call/builtin/atanh.spec.ts | 4 +- .../expression/call/builtin/ceil.spec.ts | 4 +- .../expression/call/builtin/clamp.spec.ts | 8 +- .../expression/call/builtin/cos.spec.ts | 4 +- .../expression/call/builtin/cosh.spec.ts | 4 +- .../expression/call/builtin/degrees.spec.ts | 4 +- .../call/builtin/derivatives.spec.ts | 8 +- .../expression/call/builtin/exp.spec.ts | 4 +- .../expression/call/builtin/exp2.spec.ts | 4 +- .../expression/call/builtin/floor.spec.ts | 4 +- .../call/builtin/inverseSqrt.spec.ts | 4 +- .../expression/call/builtin/length.spec.ts | 4 +- .../expression/call/builtin/log.spec.ts | 4 +- .../expression/call/builtin/log2.spec.ts | 4 +- .../expression/call/builtin/modf.spec.ts | 4 +- .../expression/call/builtin/radians.spec.ts | 4 +- .../expression/call/builtin/round.spec.ts | 4 +- .../expression/call/builtin/saturate.spec.ts | 4 +- .../expression/call/builtin/sign.spec.ts | 12 +- .../expression/call/builtin/sin.spec.ts | 4 +- .../expression/call/builtin/sinh.spec.ts | 4 +- .../expression/call/builtin/sqrt.spec.ts | 4 +- .../expression/call/builtin/tan.spec.ts | 4 +- src/webgpu/util/conversion.ts | 38 +-- 31 files changed, 195 insertions(+), 195 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index e92b0937b6d4..61bc0ff73192 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "1a138e40", - "webgpu/shader/execution/binary/af_logical.bin": "2a334d4b", - "webgpu/shader/execution/binary/af_division.bin": "801a63d2", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "d392437a", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "8a56ca30", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "3b36edc4", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "c2a167d1", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "3bdd724b", - "webgpu/shader/execution/binary/af_multiplication.bin": "af6472be", - "webgpu/shader/execution/binary/af_remainder.bin": "582570bc", - "webgpu/shader/execution/binary/af_subtraction.bin": "65e85cff", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "3876accd", - "webgpu/shader/execution/binary/f16_addition.bin": "12057229", - "webgpu/shader/execution/binary/f16_logical.bin": "b6ee9faf", - "webgpu/shader/execution/binary/f16_division.bin": "9f3479f0", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "a19f6dc5", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "be7786cc", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "afbc3dd2", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "84f6917", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "9036e4da", - "webgpu/shader/execution/binary/f16_multiplication.bin": "946528a5", - "webgpu/shader/execution/binary/f16_remainder.bin": "6e7cb89", - "webgpu/shader/execution/binary/f16_subtraction.bin": "55fe509d", - "webgpu/shader/execution/binary/f32_addition.bin": "3d2058bb", - "webgpu/shader/execution/binary/f32_logical.bin": "390ba598", - "webgpu/shader/execution/binary/f32_division.bin": "73fa9316", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "afb0e609", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "745cb3d9", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "1fa39b30", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "a0b386f7", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "89c1eca2", - "webgpu/shader/execution/binary/f32_multiplication.bin": "712f02a6", - "webgpu/shader/execution/binary/f32_remainder.bin": "8304c08d", - "webgpu/shader/execution/binary/f32_subtraction.bin": "ccdf094f", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "8a775ab8", - "webgpu/shader/execution/binary/i32_comparison.bin": "dd37b753", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "c3781404", - "webgpu/shader/execution/binary/u32_comparison.bin": "bed8f168", - "webgpu/shader/execution/abs.bin": "61e06f54", - "webgpu/shader/execution/acos.bin": "1402a723", - "webgpu/shader/execution/acosh.bin": "828c987f", - "webgpu/shader/execution/asin.bin": "557d3d3f", - "webgpu/shader/execution/asinh.bin": "be348f39", - "webgpu/shader/execution/atan.bin": "963520fc", - "webgpu/shader/execution/atan2.bin": "a912218b", - "webgpu/shader/execution/atanh.bin": "f46f32cc", - "webgpu/shader/execution/bitcast.bin": "44bd25ea", - "webgpu/shader/execution/ceil.bin": "7debbc4c", - "webgpu/shader/execution/clamp.bin": "26d3bfb", - "webgpu/shader/execution/cos.bin": "a64fea9a", - "webgpu/shader/execution/cosh.bin": "dfd5945e", - "webgpu/shader/execution/cross.bin": "4835bc3a", - "webgpu/shader/execution/degrees.bin": "528e631e", - "webgpu/shader/execution/determinant.bin": "e3866c2b", - "webgpu/shader/execution/distance.bin": "4d015170", - "webgpu/shader/execution/dot.bin": "c67a6363", - "webgpu/shader/execution/exp.bin": "c483696", - "webgpu/shader/execution/exp2.bin": "b248fa4b", - "webgpu/shader/execution/faceForward.bin": "429078d0", - "webgpu/shader/execution/floor.bin": "2f4fb64b", - "webgpu/shader/execution/fma.bin": "8cf2c018", - "webgpu/shader/execution/fract.bin": "4fe1141d", - "webgpu/shader/execution/frexp.bin": "38dd5724", - "webgpu/shader/execution/inverseSqrt.bin": "b010463d", - "webgpu/shader/execution/ldexp.bin": "94762c6c", - "webgpu/shader/execution/length.bin": "9007cea7", - "webgpu/shader/execution/log.bin": "8918d035", - "webgpu/shader/execution/log2.bin": "55ab0d73", - "webgpu/shader/execution/max.bin": "ea6871eb", - "webgpu/shader/execution/min.bin": "da3976b4", - "webgpu/shader/execution/mix.bin": "716aa87b", - "webgpu/shader/execution/modf.bin": "97fb95d0", - "webgpu/shader/execution/normalize.bin": "b4b3d66d", - "webgpu/shader/execution/pack2x16float.bin": "65dca45b", - "webgpu/shader/execution/pow.bin": "9c93c51e", - "webgpu/shader/execution/quantizeToF16.bin": "9bba3a59", - "webgpu/shader/execution/radians.bin": "31835823", - "webgpu/shader/execution/reflect.bin": "cef377fe", - "webgpu/shader/execution/refract.bin": "6a72b15f", - "webgpu/shader/execution/round.bin": "eb65ff2d", - "webgpu/shader/execution/saturate.bin": "6be2db42", - "webgpu/shader/execution/sign.bin": "6c0ac68f", - "webgpu/shader/execution/sin.bin": "6548201", - "webgpu/shader/execution/sinh.bin": "df389ed", - "webgpu/shader/execution/smoothstep.bin": "90af9360", - "webgpu/shader/execution/sqrt.bin": "37f1d81b", - "webgpu/shader/execution/step.bin": "a2a1103e", - "webgpu/shader/execution/tan.bin": "db82969", - "webgpu/shader/execution/tanh.bin": "9469a1c6", - "webgpu/shader/execution/transpose.bin": "51d503cb", - "webgpu/shader/execution/trunc.bin": "c5e6ed27", - "webgpu/shader/execution/unpack2x16float.bin": "a4812794", - "webgpu/shader/execution/unpack2x16snorm.bin": "48982cdb", - "webgpu/shader/execution/unpack2x16unorm.bin": "8a52d694", - "webgpu/shader/execution/unpack4x8snorm.bin": "de64f4f", - "webgpu/shader/execution/unpack4x8unorm.bin": "42074fbd", - "webgpu/shader/execution/unary/af_arithmetic.bin": "677d6662", - "webgpu/shader/execution/unary/af_assignment.bin": "9035f5c0", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "62304038", - "webgpu/shader/execution/unary/ai_assignment.bin": "d0ecdfb1", - "webgpu/shader/execution/unary/bool_conversion.bin": "9df31641", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "55060b95", - "webgpu/shader/execution/unary/f16_conversion.bin": "2a471445", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "de075203", - "webgpu/shader/execution/unary/f32_conversion.bin": "8c96deed", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "11e8bb3", - "webgpu/shader/execution/unary/i32_conversion.bin": "a3062e7d", - "webgpu/shader/execution/unary/u32_conversion.bin": "c1c5240c" + "webgpu/shader/execution/binary/af_addition.bin": "1843098d", + "webgpu/shader/execution/binary/af_logical.bin": "1382526e", + "webgpu/shader/execution/binary/af_division.bin": "e0262a88", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "1e39ddea", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "dd6bc727", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "fb15115", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "d7ba7836", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "5a23b6cc", + "webgpu/shader/execution/binary/af_multiplication.bin": "e6a5c5e4", + "webgpu/shader/execution/binary/af_remainder.bin": "e063382c", + "webgpu/shader/execution/binary/af_subtraction.bin": "d43eb5af", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "2d6a0f7e", + "webgpu/shader/execution/binary/f16_addition.bin": "3346b4bd", + "webgpu/shader/execution/binary/f16_logical.bin": "b0bfe351", + "webgpu/shader/execution/binary/f16_division.bin": "db83c0cc", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "882f40bb", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "cfc4cb6", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "b2344dc1", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "1822ca", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "f5529ab4", + "webgpu/shader/execution/binary/f16_multiplication.bin": "9ea35453", + "webgpu/shader/execution/binary/f16_remainder.bin": "39fe22c1", + "webgpu/shader/execution/binary/f16_subtraction.bin": "d65e2e11", + "webgpu/shader/execution/binary/f32_addition.bin": "e2576a3b", + "webgpu/shader/execution/binary/f32_logical.bin": "a4e17c69", + "webgpu/shader/execution/binary/f32_division.bin": "24a41b78", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "4af13f54", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "ffe9e80b", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "1eb1789c", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "e2694b78", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "bbf90", + "webgpu/shader/execution/binary/f32_multiplication.bin": "63f63b02", + "webgpu/shader/execution/binary/f32_remainder.bin": "6a44daa2", + "webgpu/shader/execution/binary/f32_subtraction.bin": "a6240f28", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "6fee3e4e", + "webgpu/shader/execution/binary/i32_comparison.bin": "788881cd", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "23da1ed8", + "webgpu/shader/execution/binary/u32_comparison.bin": "8ab2e08c", + "webgpu/shader/execution/abs.bin": "19017bb1", + "webgpu/shader/execution/acos.bin": "9dd5c222", + "webgpu/shader/execution/acosh.bin": "c5b7a2b9", + "webgpu/shader/execution/asin.bin": "998df766", + "webgpu/shader/execution/asinh.bin": "ace06d5a", + "webgpu/shader/execution/atan.bin": "b94c5065", + "webgpu/shader/execution/atan2.bin": "a2c19388", + "webgpu/shader/execution/atanh.bin": "f726651b", + "webgpu/shader/execution/bitcast.bin": "81370da9", + "webgpu/shader/execution/ceil.bin": "f6fda5fa", + "webgpu/shader/execution/clamp.bin": "47a17327", + "webgpu/shader/execution/cos.bin": "6b63dc60", + "webgpu/shader/execution/cosh.bin": "8fe08827", + "webgpu/shader/execution/cross.bin": "2ef763e5", + "webgpu/shader/execution/degrees.bin": "c30f7423", + "webgpu/shader/execution/determinant.bin": "4dedfa96", + "webgpu/shader/execution/distance.bin": "9b71fb94", + "webgpu/shader/execution/dot.bin": "db15b119", + "webgpu/shader/execution/exp.bin": "65c5d3ab", + "webgpu/shader/execution/exp2.bin": "6e7766a1", + "webgpu/shader/execution/faceForward.bin": "282fbba6", + "webgpu/shader/execution/floor.bin": "dce512dd", + "webgpu/shader/execution/fma.bin": "21d0a25e", + "webgpu/shader/execution/fract.bin": "82f96adc", + "webgpu/shader/execution/frexp.bin": "a41c87a4", + "webgpu/shader/execution/inverseSqrt.bin": "ec953eb3", + "webgpu/shader/execution/ldexp.bin": "fcfd6224", + "webgpu/shader/execution/length.bin": "4da1998f", + "webgpu/shader/execution/log.bin": "d99e6690", + "webgpu/shader/execution/log2.bin": "4f7644b3", + "webgpu/shader/execution/max.bin": "f8bc9388", + "webgpu/shader/execution/min.bin": "7b1b016c", + "webgpu/shader/execution/mix.bin": "7b257d9c", + "webgpu/shader/execution/modf.bin": "fa632cd1", + "webgpu/shader/execution/normalize.bin": "ace99215", + "webgpu/shader/execution/pack2x16float.bin": "6ef332cb", + "webgpu/shader/execution/pow.bin": "7c042e04", + "webgpu/shader/execution/quantizeToF16.bin": "7fd41be3", + "webgpu/shader/execution/radians.bin": "3e3aae03", + "webgpu/shader/execution/reflect.bin": "c75fc3e0", + "webgpu/shader/execution/refract.bin": "8455f497", + "webgpu/shader/execution/round.bin": "95773d03", + "webgpu/shader/execution/saturate.bin": "e2d02ac5", + "webgpu/shader/execution/sign.bin": "9c56fda6", + "webgpu/shader/execution/sin.bin": "7256a0d6", + "webgpu/shader/execution/sinh.bin": "593345a9", + "webgpu/shader/execution/smoothstep.bin": "5064d00b", + "webgpu/shader/execution/sqrt.bin": "601a4fbc", + "webgpu/shader/execution/step.bin": "5596f86a", + "webgpu/shader/execution/tan.bin": "ba7975c1", + "webgpu/shader/execution/tanh.bin": "cac48441", + "webgpu/shader/execution/transpose.bin": "e5d5b2a9", + "webgpu/shader/execution/trunc.bin": "95811ca", + "webgpu/shader/execution/unpack2x16float.bin": "9995d9a7", + "webgpu/shader/execution/unpack2x16snorm.bin": "a4c1d0b4", + "webgpu/shader/execution/unpack2x16unorm.bin": "7b130e1f", + "webgpu/shader/execution/unpack4x8snorm.bin": "9879686e", + "webgpu/shader/execution/unpack4x8unorm.bin": "727028f2", + "webgpu/shader/execution/unary/af_arithmetic.bin": "4894a2dc", + "webgpu/shader/execution/unary/af_assignment.bin": "b4c538d5", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "3272cb52", + "webgpu/shader/execution/unary/ai_assignment.bin": "c2877676", + "webgpu/shader/execution/unary/bool_conversion.bin": "97ef95b3", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "86b56801", + "webgpu/shader/execution/unary/f16_conversion.bin": "5cf66fac", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "c7b8416", + "webgpu/shader/execution/unary/f32_conversion.bin": "b0af00cd", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "b2d016ce", + "webgpu/shader/execution/unary/i32_conversion.bin": "ca9cd82d", + "webgpu/shader/execution/unary/u32_conversion.bin": "40797b79" } \ No newline at end of file diff --git a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts index dc9ed497574e..cabdf6b659d6 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; @@ -67,7 +67,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec }); // f32 is included here to confirm that validation is failing due to a type issue and not something else. -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts index 31e44f373b0d..3526b45f32ad 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, TypeAbstractInt, kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, @@ -70,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord(kAllConcreteIntegerScalarsAndVectors); +const kIntegerArgumentTypes = objectsToRecord(kConcreteIntegerScalarsAndVectors); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts index 5c698f589f74..3b7cdf8b8668 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; @@ -66,7 +66,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts index 4e1fc37ffd5e..c205ea2d6ecb 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; @@ -72,7 +72,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts index 201ff5efd550..3b7dad4c4059 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -63,7 +63,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts index 9bfefe287ffe..4c61b331e2ad 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts @@ -11,8 +11,8 @@ import { Vector, VectorType, elementType, - kAllFloatScalarsAndVectors, - kAllConcreteIntegerScalarsAndVectors, + kFloatScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -28,7 +28,7 @@ import { export const g = makeTestGroup(ShaderValidationTest); -const kValuesTypes = objectsToRecord(kAllFloatScalarsAndVectors); +const kValuesTypes = objectsToRecord(kFloatScalarsAndVectors); g.test('values') .desc( @@ -75,7 +75,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument_y') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts index d45df767c223..1d31ed88db28 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; @@ -66,7 +66,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts index bf76b3f2f11b..09018d12a3a1 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -55,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts index 638c5ae2e884..6342189ad643 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts @@ -8,8 +8,8 @@ import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tabl import { TypeF16, elementType, - kAllFloatScalarsAndVectors, - kAllConcreteIntegerScalarsAndVectors, + kFloatScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -23,8 +23,8 @@ import { export const g = makeTestGroup(ShaderValidationTest); const kValuesTypes = objectsToRecord([ - ...kAllFloatScalarsAndVectors, - ...kAllConcreteIntegerScalarsAndVectors, + ...kFloatScalarsAndVectors, + ...kConcreteIntegerScalarsAndVectors, ]); g.test('values') diff --git a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts index 7975d7d7c6d5..d010cf499276 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -61,7 +61,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts index 9a4864c4f972..6a7fcddba232 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; @@ -62,7 +62,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts index 9c60548e01dd..2805b97b1597 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; @@ -62,7 +62,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts index 474f14e03ee7..cf5a707ba7c7 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts @@ -9,8 +9,8 @@ import { TypeF32, TypeMat, elementType, - kAllConcreteIntegerScalarsAndVectors, - kAllF16ScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, + kConcreteF16ScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -99,8 +99,8 @@ fn foo() { // The list of invalid argument types to test, with an f32 control case. const kArgumentTypes = objectsToRecord([ TypeF32, - ...kAllConcreteIntegerScalarsAndVectors, - ...kAllF16ScalarsAndVectors, + ...kConcreteIntegerScalarsAndVectors, + ...kConcreteF16ScalarsAndVectors, TypeMat(2, 2, TypeF32), ]); diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts index b4a76018f2a0..6168fafb2336 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts @@ -11,7 +11,7 @@ import { TypeF32, TypeAbstractFloat, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -92,7 +92,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts index 36074ee42ecc..686cb3f69570 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts @@ -11,7 +11,7 @@ import { TypeF32, TypeAbstractFloat, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -92,7 +92,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts index 998ca5618344..25dd950dc602 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -55,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts index fc9351584b08..a192bc098cd9 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, TypeAbstractFloat, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; @@ -71,7 +71,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts index cb9be19963f2..3fca4fec2cc8 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts @@ -10,7 +10,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalar, kConvertableToFloatVec2, kConvertableToFloatVec3, @@ -210,7 +210,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts index 79036e5d86b9..b4cf923a50ce 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -55,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts index dff27e655c99..066fb652499b 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -55,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts index 6b809dc31ca1..3c32e3e37495 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -55,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts index 3c9105a11fd6..320bde062098 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -55,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts index 39c170496d5e..654729cadb4e 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { fpTraitsFor } from '../../../../../util/floating_point.js'; @@ -67,7 +67,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts index 8ac53d8a4e9a..709a006ea8bd 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -55,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts index b9d58a4cf9ca..2a4560576ca5 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts @@ -9,9 +9,9 @@ import { TypeF16, TypeF32, elementType, - kAllFloatScalarsAndVectors, - kAllSignedIntegerScalarsAndVectors, - kAllUnsignedIntegerScalarsAndVectors, + kFloatScalarsAndVectors, + kConcreteSignedIntegerScalarsAndVectors, + kConcreteUnsignedIntegerScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -25,8 +25,8 @@ import { export const g = makeTestGroup(ShaderValidationTest); const kValuesTypes = objectsToRecord([ - ...kAllFloatScalarsAndVectors, - ...kAllSignedIntegerScalarsAndVectors, + ...kFloatScalarsAndVectors, + ...kConcreteSignedIntegerScalarsAndVectors, ]); g.test('values') @@ -61,7 +61,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input const kUnsignedIntegerArgumentTypes = objectsToRecord([ TypeF32, - ...kAllUnsignedIntegerScalarsAndVectors, + ...kConcreteUnsignedIntegerScalarsAndVectors, ]); g.test('unsigned_integer_argument') diff --git a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts index d300af4e26e9..2cd4ee002a3f 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -61,7 +61,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts index 6f7764c0dbd4..45550d5c088f 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; @@ -62,7 +62,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts index 96b3d8cbba5b..125a9d20de62 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, TypeAbstractFloat, } from '../../../../../util/conversion.js'; @@ -71,7 +71,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts index 7f24912476f5..38b0b204059c 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts @@ -9,7 +9,7 @@ import { TypeF16, TypeF32, elementType, - kAllConcreteIntegerScalarsAndVectors, + kConcreteIntegerScalarsAndVectors, TypeAbstractFloat, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; @@ -71,7 +71,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kAllConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 6e4f09cc5940..8599e2335869 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -1651,31 +1651,31 @@ export function isFloatType(ty: Type): boolean { } /// All floating-point scalar types -export const kAllFloatScalars = [TypeAbstractFloat, TypeF32, TypeF16] as const; +const kFloatScalars = [TypeAbstractFloat, TypeF32, TypeF16] as const; /// All floating-point vec2 types -const kAllFloatVec2 = [ +const kFloatVec2 = [ TypeVec(2, TypeAbstractFloat), TypeVec(2, TypeF32), TypeVec(2, TypeF16), ] as const; /// All floating-point vec3 types -const kAllFloatVec3 = [ +const kFloatVec3 = [ TypeVec(3, TypeAbstractFloat), TypeVec(3, TypeF32), TypeVec(3, TypeF16), ] as const; /// All floating-point vec4 types -const kAllFloatVec4 = [ +const kFloatVec4 = [ TypeVec(4, TypeAbstractFloat), TypeVec(4, TypeF32), TypeVec(4, TypeF16), ] as const; /// All f16 floating-point scalar and vector types -export const kAllF16ScalarsAndVectors = [ +export const kConcreteF16ScalarsAndVectors = [ TypeF16, TypeVec(2, TypeF16), TypeVec(3, TypeF16), @@ -1683,11 +1683,11 @@ export const kAllF16ScalarsAndVectors = [ ] as const; /// All floating-point scalar and vector types -export const kAllFloatScalarsAndVectors = [ - ...kAllFloatScalars, - ...kAllFloatVec2, - ...kAllFloatVec3, - ...kAllFloatVec4, +export const kFloatScalarsAndVectors = [ + ...kFloatScalars, + ...kFloatVec2, + ...kFloatVec3, + ...kFloatVec4, ] as const; // Abstract and concrete integer types are not grouped into an 'all' type, @@ -1698,7 +1698,7 @@ export const kAllFloatScalarsAndVectors = [ // for the things that might be valid and those that are never valid. /// All concrete integer scalar and vector types -export const kAllConcreteIntegerScalarsAndVectors = [ +export const kConcreteIntegerScalarsAndVectors = [ TypeI32, TypeVec(2, TypeI32), TypeVec(3, TypeI32), @@ -1710,7 +1710,7 @@ export const kAllConcreteIntegerScalarsAndVectors = [ ] as const; /// All signed integer scalar and vector types -export const kAllSignedIntegerScalarsAndVectors = [ +export const kConcreteSignedIntegerScalarsAndVectors = [ TypeI32, TypeVec(2, TypeI32), TypeVec(3, TypeI32), @@ -1718,7 +1718,7 @@ export const kAllSignedIntegerScalarsAndVectors = [ ] as const; /// All unsigned integer scalar and vector types -export const kAllUnsignedIntegerScalarsAndVectors = [ +export const kConcreteUnsignedIntegerScalarsAndVectors = [ TypeU32, TypeVec(2, TypeU32), TypeVec(3, TypeU32), @@ -1726,16 +1726,16 @@ export const kAllUnsignedIntegerScalarsAndVectors = [ ] as const; /// All types which are convertable to floating-point scalar types. -export const kConvertableToFloatScalar = [TypeAbstractInt, ...kAllFloatScalars] as const; +export const kConvertableToFloatScalar = [TypeAbstractInt, ...kFloatScalars] as const; /// All types which are convertable to floating-point vector 2 types. -export const kConvertableToFloatVec2 = [TypeVec(2, TypeAbstractInt), ...kAllFloatVec2] as const; +export const kConvertableToFloatVec2 = [TypeVec(2, TypeAbstractInt), ...kFloatVec2] as const; /// All types which are convertable to floating-point vector 3 types. -export const kConvertableToFloatVec3 = [TypeVec(3, TypeAbstractInt), ...kAllFloatVec3] as const; +export const kConvertableToFloatVec3 = [TypeVec(3, TypeAbstractInt), ...kFloatVec3] as const; /// All types which are convertable to floating-point vector 4 types. -export const kConvertableToFloatVec4 = [TypeVec(4, TypeAbstractInt), ...kAllFloatVec4] as const; +export const kConvertableToFloatVec4 = [TypeVec(4, TypeAbstractInt), ...kFloatVec4] as const; /// All types which are convertable to floating-point scalar or vector types. export const kConvertableToFloatScalarsAndVectors = [ @@ -1743,13 +1743,13 @@ export const kConvertableToFloatScalarsAndVectors = [ TypeVec(2, TypeAbstractInt), TypeVec(3, TypeAbstractInt), TypeVec(4, TypeAbstractInt), - ...kAllFloatScalarsAndVectors, + ...kFloatScalarsAndVectors, ] as const; /// All the scalar and vector types export const kAllScalarsAndVectors = [ ...kConvertableToFloatScalarsAndVectors, - ...kAllConcreteIntegerScalarsAndVectors, + ...kConcreteIntegerScalarsAndVectors, ] as const; /** @returns the inner element type of the given type */ From ac1af685a3ae08e89d22c5497c56cab6453c9ab2 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Thu, 7 Mar 2024 12:58:56 +0000 Subject: [PATCH 10/13] Split the 'Scalar' class into one class per type. Attempting to support all the types in a single class resulted in messy dynamic type dispatching. --- src/resources/cache/hashes.json | 216 ++--- .../cache/webgpu/shader/execution/bitcast.bin | Bin 2221448 -> 2221448 bytes .../expression/call/builtin/bitcast.spec.ts | 72 +- .../shader/execution/expression/case_cache.ts | 4 +- .../execution/expression/expectation.ts | 4 +- src/webgpu/util/compare.ts | 6 +- src/webgpu/util/conversion.ts | 776 +++++++++++++----- 7 files changed, 721 insertions(+), 357 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 61bc0ff73192..8332f3962c10 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "1843098d", - "webgpu/shader/execution/binary/af_logical.bin": "1382526e", - "webgpu/shader/execution/binary/af_division.bin": "e0262a88", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "1e39ddea", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "dd6bc727", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "fb15115", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "d7ba7836", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "5a23b6cc", - "webgpu/shader/execution/binary/af_multiplication.bin": "e6a5c5e4", - "webgpu/shader/execution/binary/af_remainder.bin": "e063382c", - "webgpu/shader/execution/binary/af_subtraction.bin": "d43eb5af", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "2d6a0f7e", - "webgpu/shader/execution/binary/f16_addition.bin": "3346b4bd", - "webgpu/shader/execution/binary/f16_logical.bin": "b0bfe351", - "webgpu/shader/execution/binary/f16_division.bin": "db83c0cc", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "882f40bb", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "cfc4cb6", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "b2344dc1", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "1822ca", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "f5529ab4", - "webgpu/shader/execution/binary/f16_multiplication.bin": "9ea35453", - "webgpu/shader/execution/binary/f16_remainder.bin": "39fe22c1", - "webgpu/shader/execution/binary/f16_subtraction.bin": "d65e2e11", - "webgpu/shader/execution/binary/f32_addition.bin": "e2576a3b", - "webgpu/shader/execution/binary/f32_logical.bin": "a4e17c69", - "webgpu/shader/execution/binary/f32_division.bin": "24a41b78", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "4af13f54", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "ffe9e80b", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "1eb1789c", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "e2694b78", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "bbf90", - "webgpu/shader/execution/binary/f32_multiplication.bin": "63f63b02", - "webgpu/shader/execution/binary/f32_remainder.bin": "6a44daa2", - "webgpu/shader/execution/binary/f32_subtraction.bin": "a6240f28", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "6fee3e4e", - "webgpu/shader/execution/binary/i32_comparison.bin": "788881cd", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "23da1ed8", - "webgpu/shader/execution/binary/u32_comparison.bin": "8ab2e08c", - "webgpu/shader/execution/abs.bin": "19017bb1", - "webgpu/shader/execution/acos.bin": "9dd5c222", - "webgpu/shader/execution/acosh.bin": "c5b7a2b9", - "webgpu/shader/execution/asin.bin": "998df766", - "webgpu/shader/execution/asinh.bin": "ace06d5a", - "webgpu/shader/execution/atan.bin": "b94c5065", - "webgpu/shader/execution/atan2.bin": "a2c19388", - "webgpu/shader/execution/atanh.bin": "f726651b", - "webgpu/shader/execution/bitcast.bin": "81370da9", - "webgpu/shader/execution/ceil.bin": "f6fda5fa", - "webgpu/shader/execution/clamp.bin": "47a17327", - "webgpu/shader/execution/cos.bin": "6b63dc60", - "webgpu/shader/execution/cosh.bin": "8fe08827", - "webgpu/shader/execution/cross.bin": "2ef763e5", - "webgpu/shader/execution/degrees.bin": "c30f7423", - "webgpu/shader/execution/determinant.bin": "4dedfa96", - "webgpu/shader/execution/distance.bin": "9b71fb94", - "webgpu/shader/execution/dot.bin": "db15b119", - "webgpu/shader/execution/exp.bin": "65c5d3ab", - "webgpu/shader/execution/exp2.bin": "6e7766a1", - "webgpu/shader/execution/faceForward.bin": "282fbba6", - "webgpu/shader/execution/floor.bin": "dce512dd", - "webgpu/shader/execution/fma.bin": "21d0a25e", - "webgpu/shader/execution/fract.bin": "82f96adc", - "webgpu/shader/execution/frexp.bin": "a41c87a4", - "webgpu/shader/execution/inverseSqrt.bin": "ec953eb3", - "webgpu/shader/execution/ldexp.bin": "fcfd6224", - "webgpu/shader/execution/length.bin": "4da1998f", - "webgpu/shader/execution/log.bin": "d99e6690", - "webgpu/shader/execution/log2.bin": "4f7644b3", - "webgpu/shader/execution/max.bin": "f8bc9388", - "webgpu/shader/execution/min.bin": "7b1b016c", - "webgpu/shader/execution/mix.bin": "7b257d9c", - "webgpu/shader/execution/modf.bin": "fa632cd1", - "webgpu/shader/execution/normalize.bin": "ace99215", - "webgpu/shader/execution/pack2x16float.bin": "6ef332cb", - "webgpu/shader/execution/pow.bin": "7c042e04", - "webgpu/shader/execution/quantizeToF16.bin": "7fd41be3", - "webgpu/shader/execution/radians.bin": "3e3aae03", - "webgpu/shader/execution/reflect.bin": "c75fc3e0", - "webgpu/shader/execution/refract.bin": "8455f497", - "webgpu/shader/execution/round.bin": "95773d03", - "webgpu/shader/execution/saturate.bin": "e2d02ac5", - "webgpu/shader/execution/sign.bin": "9c56fda6", - "webgpu/shader/execution/sin.bin": "7256a0d6", - "webgpu/shader/execution/sinh.bin": "593345a9", - "webgpu/shader/execution/smoothstep.bin": "5064d00b", - "webgpu/shader/execution/sqrt.bin": "601a4fbc", - "webgpu/shader/execution/step.bin": "5596f86a", - "webgpu/shader/execution/tan.bin": "ba7975c1", - "webgpu/shader/execution/tanh.bin": "cac48441", - "webgpu/shader/execution/transpose.bin": "e5d5b2a9", - "webgpu/shader/execution/trunc.bin": "95811ca", - "webgpu/shader/execution/unpack2x16float.bin": "9995d9a7", - "webgpu/shader/execution/unpack2x16snorm.bin": "a4c1d0b4", - "webgpu/shader/execution/unpack2x16unorm.bin": "7b130e1f", - "webgpu/shader/execution/unpack4x8snorm.bin": "9879686e", - "webgpu/shader/execution/unpack4x8unorm.bin": "727028f2", - "webgpu/shader/execution/unary/af_arithmetic.bin": "4894a2dc", - "webgpu/shader/execution/unary/af_assignment.bin": "b4c538d5", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "3272cb52", - "webgpu/shader/execution/unary/ai_assignment.bin": "c2877676", - "webgpu/shader/execution/unary/bool_conversion.bin": "97ef95b3", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "86b56801", - "webgpu/shader/execution/unary/f16_conversion.bin": "5cf66fac", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "c7b8416", - "webgpu/shader/execution/unary/f32_conversion.bin": "b0af00cd", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "b2d016ce", - "webgpu/shader/execution/unary/i32_conversion.bin": "ca9cd82d", - "webgpu/shader/execution/unary/u32_conversion.bin": "40797b79" + "webgpu/shader/execution/binary/af_addition.bin": "cfc28768", + "webgpu/shader/execution/binary/af_logical.bin": "ae9b9ba0", + "webgpu/shader/execution/binary/af_division.bin": "30839274", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "e43d31a5", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "fbec81c8", + "webgpu/shader/execution/binary/af_multiplication.bin": "19681c0c", + "webgpu/shader/execution/binary/af_remainder.bin": "fb9c916", + "webgpu/shader/execution/binary/af_subtraction.bin": "6f448b72", + "webgpu/shader/execution/binary/f16_addition.bin": "83ac7e89", + "webgpu/shader/execution/binary/f16_logical.bin": "6c9cdfe8", + "webgpu/shader/execution/binary/f16_division.bin": "487729fe", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "54aa39b", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "3a4159f8", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "fceaaf1f", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "92151406", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "d920f7ec", + "webgpu/shader/execution/binary/f16_multiplication.bin": "fc8c2a0f", + "webgpu/shader/execution/binary/f16_remainder.bin": "6e60c5d0", + "webgpu/shader/execution/binary/f16_subtraction.bin": "4277226e", + "webgpu/shader/execution/binary/f32_addition.bin": "5b9c4270", + "webgpu/shader/execution/binary/f32_logical.bin": "32188047", + "webgpu/shader/execution/binary/f32_division.bin": "22edca65", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "62107e07", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "9e5677c9", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "d69476cb", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c429b29b", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "ded0240e", + "webgpu/shader/execution/binary/f32_multiplication.bin": "f088c036", + "webgpu/shader/execution/binary/f32_remainder.bin": "dcb44e72", + "webgpu/shader/execution/binary/f32_subtraction.bin": "dab334aa", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "2b48d275", + "webgpu/shader/execution/binary/i32_comparison.bin": "f2c3cd37", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "7ec7b66", + "webgpu/shader/execution/binary/u32_comparison.bin": "50ab2f61", + "webgpu/shader/execution/abs.bin": "e37fc25a", + "webgpu/shader/execution/acos.bin": "4dc16324", + "webgpu/shader/execution/acosh.bin": "1f999f3c", + "webgpu/shader/execution/asin.bin": "a653cf5b", + "webgpu/shader/execution/asinh.bin": "515e83f7", + "webgpu/shader/execution/atan.bin": "95006a05", + "webgpu/shader/execution/atan2.bin": "3cc4efac", + "webgpu/shader/execution/atanh.bin": "e2985906", + "webgpu/shader/execution/bitcast.bin": "bda67f6d", + "webgpu/shader/execution/ceil.bin": "5d285a4", + "webgpu/shader/execution/clamp.bin": "cd0271cf", + "webgpu/shader/execution/cos.bin": "dfd7be1f", + "webgpu/shader/execution/cosh.bin": "8a9b86a5", + "webgpu/shader/execution/cross.bin": "80fe94ce", + "webgpu/shader/execution/degrees.bin": "d9a6c350", + "webgpu/shader/execution/determinant.bin": "2e19c9bd", + "webgpu/shader/execution/distance.bin": "64daad1a", + "webgpu/shader/execution/dot.bin": "c78ce327", + "webgpu/shader/execution/exp.bin": "c1b5a4fd", + "webgpu/shader/execution/exp2.bin": "63ac077", + "webgpu/shader/execution/faceForward.bin": "92b334a6", + "webgpu/shader/execution/floor.bin": "7e1bf2c3", + "webgpu/shader/execution/fma.bin": "701a86c6", + "webgpu/shader/execution/fract.bin": "c29aadce", + "webgpu/shader/execution/frexp.bin": "5ae6ca4", + "webgpu/shader/execution/inverseSqrt.bin": "9563f18d", + "webgpu/shader/execution/ldexp.bin": "f005b4b5", + "webgpu/shader/execution/length.bin": "ee8e5573", + "webgpu/shader/execution/log.bin": "29c4187b", + "webgpu/shader/execution/log2.bin": "9ce20393", + "webgpu/shader/execution/max.bin": "7516dbff", + "webgpu/shader/execution/min.bin": "75e9d526", + "webgpu/shader/execution/mix.bin": "b224306a", + "webgpu/shader/execution/modf.bin": "ba63df78", + "webgpu/shader/execution/normalize.bin": "9842b756", + "webgpu/shader/execution/pack2x16float.bin": "5dee8700", + "webgpu/shader/execution/pow.bin": "81bac0a9", + "webgpu/shader/execution/quantizeToF16.bin": "6973d05d", + "webgpu/shader/execution/radians.bin": "128a0639", + "webgpu/shader/execution/reflect.bin": "5de2177e", + "webgpu/shader/execution/refract.bin": "de1e1ade", + "webgpu/shader/execution/round.bin": "70a9da36", + "webgpu/shader/execution/saturate.bin": "e8daa151", + "webgpu/shader/execution/sign.bin": "503847e9", + "webgpu/shader/execution/sin.bin": "51df6447", + "webgpu/shader/execution/sinh.bin": "dbc469f3", + "webgpu/shader/execution/smoothstep.bin": "16cdfbe9", + "webgpu/shader/execution/sqrt.bin": "b59d4606", + "webgpu/shader/execution/step.bin": "5df4b04b", + "webgpu/shader/execution/tan.bin": "f1535f0c", + "webgpu/shader/execution/tanh.bin": "6e653c6d", + "webgpu/shader/execution/transpose.bin": "41ff2823", + "webgpu/shader/execution/trunc.bin": "7c3baf7", + "webgpu/shader/execution/unpack2x16float.bin": "f994910", + "webgpu/shader/execution/unpack2x16snorm.bin": "df36ee6f", + "webgpu/shader/execution/unpack2x16unorm.bin": "ac195462", + "webgpu/shader/execution/unpack4x8snorm.bin": "39a4743a", + "webgpu/shader/execution/unpack4x8unorm.bin": "ecae6fe5", + "webgpu/shader/execution/unary/af_arithmetic.bin": "9800df7f", + "webgpu/shader/execution/unary/af_assignment.bin": "85c4d516", + "webgpu/shader/execution/unary/bool_conversion.bin": "85664e85", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "3e0dea1", + "webgpu/shader/execution/unary/f16_conversion.bin": "c1f37dd1", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "f2cd5bd2", + "webgpu/shader/execution/unary/f32_conversion.bin": "b4174de", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "b327298b", + "webgpu/shader/execution/unary/i32_conversion.bin": "ebdc408a", + "webgpu/shader/execution/unary/u32_conversion.bin": "aafe3d79", + "webgpu/shader/execution/unary/ai_assignment.bin": "ef0b469b", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "b22511b9", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "67c3d3b6", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "b72f2c53", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "7f328797", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "b34a4a2b" } \ No newline at end of file diff --git a/src/resources/cache/webgpu/shader/execution/bitcast.bin b/src/resources/cache/webgpu/shader/execution/bitcast.bin index 3c24c88e1ffbc42f87639912bacab411154e6d63..d3954903ac89afcfbe5049a356005ed02cf10aae 100644 GIT binary patch delta 152 zcmWN=%MpSw6hP4l3d%o-f*(*Rf!897WKSO%h7G$EpiObl%&~u?EEgxU8};PHcu@>^ r;E^FCo_J==gef!TELc*r;)OM@ys=@+J0E=V#g0AS{P4SZ8Gh>y9V9$( delta 151 zcmWN=M-6~L006yPYE?jRVw)gr0)tx&a diff --git a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts index 5e7add44048d..4c1a8aa618e5 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts @@ -522,24 +522,24 @@ g.test('af_to_i32') 1, 10, 256, - u32Bits(0b11111111011111111111111111111111).value as number, - u32Bits(0b11111111010000000000000000000000).value as number, - u32Bits(0b11111110110000000000000000000000).value as number, - u32Bits(0b11111101110000000000000000000000).value as number, - u32Bits(0b11111011110000000000000000000000).value as number, - u32Bits(0b11110111110000000000000000000000).value as number, - u32Bits(0b11101111110000000000000000000000).value as number, - u32Bits(0b11011111110000000000000000000000).value as number, - u32Bits(0b10111111110000000000000000000000).value as number, - u32Bits(0b01111111011111111111111111111111).value as number, - u32Bits(0b01111111010000000000000000000000).value as number, - u32Bits(0b01111110110000000000000000000000).value as number, - u32Bits(0b01111101110000000000000000000000).value as number, - u32Bits(0b01111011110000000000000000000000).value as number, - u32Bits(0b01110111110000000000000000000000).value as number, - u32Bits(0b01101111110000000000000000000000).value as number, - u32Bits(0b01011111110000000000000000000000).value as number, - u32Bits(0b00111111110000000000000000000000).value as number, + u32Bits(0b11111111011111111111111111111111).value, + u32Bits(0b11111111010000000000000000000000).value, + u32Bits(0b11111110110000000000000000000000).value, + u32Bits(0b11111101110000000000000000000000).value, + u32Bits(0b11111011110000000000000000000000).value, + u32Bits(0b11110111110000000000000000000000).value, + u32Bits(0b11101111110000000000000000000000).value, + u32Bits(0b11011111110000000000000000000000).value, + u32Bits(0b10111111110000000000000000000000).value, + u32Bits(0b01111111011111111111111111111111).value, + u32Bits(0b01111111010000000000000000000000).value, + u32Bits(0b01111110110000000000000000000000).value, + u32Bits(0b01111101110000000000000000000000).value, + u32Bits(0b01111011110000000000000000000000).value, + u32Bits(0b01110111110000000000000000000000).value, + u32Bits(0b01101111110000000000000000000000).value, + u32Bits(0b01011111110000000000000000000000).value, + u32Bits(0b00111111110000000000000000000000).value, ]; const cases = values.map(u => { @@ -566,24 +566,24 @@ g.test('af_to_u32') 1, 10, 256, - u32Bits(0b11111111011111111111111111111111).value as number, - u32Bits(0b11111111010000000000000000000000).value as number, - u32Bits(0b11111110110000000000000000000000).value as number, - u32Bits(0b11111101110000000000000000000000).value as number, - u32Bits(0b11111011110000000000000000000000).value as number, - u32Bits(0b11110111110000000000000000000000).value as number, - u32Bits(0b11101111110000000000000000000000).value as number, - u32Bits(0b11011111110000000000000000000000).value as number, - u32Bits(0b10111111110000000000000000000000).value as number, - u32Bits(0b01111111011111111111111111111111).value as number, - u32Bits(0b01111111010000000000000000000000).value as number, - u32Bits(0b01111110110000000000000000000000).value as number, - u32Bits(0b01111101110000000000000000000000).value as number, - u32Bits(0b01111011110000000000000000000000).value as number, - u32Bits(0b01110111110000000000000000000000).value as number, - u32Bits(0b01101111110000000000000000000000).value as number, - u32Bits(0b01011111110000000000000000000000).value as number, - u32Bits(0b00111111110000000000000000000000).value as number, + u32Bits(0b11111111011111111111111111111111).value, + u32Bits(0b11111111010000000000000000000000).value, + u32Bits(0b11111110110000000000000000000000).value, + u32Bits(0b11111101110000000000000000000000).value, + u32Bits(0b11111011110000000000000000000000).value, + u32Bits(0b11110111110000000000000000000000).value, + u32Bits(0b11101111110000000000000000000000).value, + u32Bits(0b11011111110000000000000000000000).value, + u32Bits(0b10111111110000000000000000000000).value, + u32Bits(0b01111111011111111111111111111111).value, + u32Bits(0b01111111010000000000000000000000).value, + u32Bits(0b01111110110000000000000000000000).value, + u32Bits(0b01111101110000000000000000000000).value, + u32Bits(0b01111011110000000000000000000000).value, + u32Bits(0b01110111110000000000000000000000).value, + u32Bits(0b01101111110000000000000000000000).value, + u32Bits(0b01011111110000000000000000000000).value, + u32Bits(0b00111111110000000000000000000000).value, ]; const cases = values.map(u => { diff --git a/src/webgpu/shader/execution/expression/case_cache.ts b/src/webgpu/shader/execution/expression/case_cache.ts index 1991c78217e9..4321475bf83a 100644 --- a/src/webgpu/shader/execution/expression/case_cache.ts +++ b/src/webgpu/shader/execution/expression/case_cache.ts @@ -4,10 +4,10 @@ import BinaryStream from '../../../util/binary_stream.js'; import { deserializeComparator, serializeComparator } from '../../../util/compare.js'; import { Matrix, - Scalar, Value, Vector, deserializeValue, + isScalar, serializeValue, } from '../../../util/conversion.js'; import { @@ -31,7 +31,7 @@ enum SerializedExpectationKind { /** serializeExpectation() serializes an Expectation to a BinaryStream */ export function serializeExpectation(s: BinaryStream, e: Expectation) { - if (e instanceof Scalar || e instanceof Vector || e instanceof Matrix) { + if (isScalar(e) || e instanceof Vector || e instanceof Matrix) { s.writeU8(SerializedExpectationKind.Value); serializeValue(s, e); return; diff --git a/src/webgpu/shader/execution/expression/expectation.ts b/src/webgpu/shader/execution/expression/expectation.ts index 8078673f2569..e56577580c52 100644 --- a/src/webgpu/shader/execution/expression/expectation.ts +++ b/src/webgpu/shader/execution/expression/expectation.ts @@ -1,6 +1,6 @@ import { ROArrayArray } from '../../../../common/util/types.js'; import { Comparator, compare } from '../../../util/compare.js'; -import { Matrix, Scalar, Value, Vector } from '../../../util/conversion.js'; +import { Matrix, Value, Vector, isScalar } from '../../../util/conversion.js'; import { FPInterval } from '../../../util/floating_point.js'; export type Expectation = @@ -14,7 +14,7 @@ export type Expectation = export function isComparator(e: Expectation): e is Comparator { return !( e instanceof FPInterval || - e instanceof Scalar || + isScalar(e) || e instanceof Vector || e instanceof Matrix || e instanceof Array diff --git a/src/webgpu/util/compare.ts b/src/webgpu/util/compare.ts index 95573e6ceae2..d9cc693184f1 100644 --- a/src/webgpu/util/compare.ts +++ b/src/webgpu/util/compare.ts @@ -8,7 +8,7 @@ import { import { Expectation, toComparator } from '../shader/execution/expression/expectation.js'; import BinaryStream from './binary_stream.js'; -import { isFloatValue, Matrix, Scalar, Value, Vector } from './conversion.js'; +import { isFloatValue, isScalar, Matrix, Scalar, Value, Vector } from './conversion.js'; import { FPInterval } from './floating_point.js'; /** Comparison describes the result of a Comparator function. */ @@ -98,7 +98,7 @@ function compareValue(got: Value, expected: Value): Comparison { } } - if (got instanceof Scalar) { + if (isScalar(got)) { const g = got; const e = expected as Scalar; const isFloat = g.type.kind === 'f64' || g.type.kind === 'f32' || g.type.kind === 'f16'; @@ -175,7 +175,7 @@ function compareInterval(got: Value, expected: FPInterval): Comparison { } } - if (got instanceof Scalar) { + if (isScalar(got)) { const g = got.value as number; const matched = expected.contains(g); return { diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 8599e2335869..31ef330d50a5 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -800,44 +800,44 @@ function valueFromBytes( return workingDataOut[0] as ArrayElementType; } -export const TypeAbstractInt = new ScalarType( +export const TypeAbstractInt: ScalarType = new ScalarType( 'abstract-int', 8, (buf: Uint8Array, offset: number) => abstractInt(valueFromBytes(workingDataI64, buf, offset)) ); -export const TypeI32 = new ScalarType('i32', 4, (buf: Uint8Array, offset: number) => +export const TypeI32: ScalarType = new ScalarType('i32', 4, (buf: Uint8Array, offset: number) => i32(valueFromBytes(workingDataI32, buf, offset)) ); -export const TypeU32 = new ScalarType('u32', 4, (buf: Uint8Array, offset: number) => +export const TypeU32: ScalarType = new ScalarType('u32', 4, (buf: Uint8Array, offset: number) => u32(valueFromBytes(workingDataU32, buf, offset)) ); -export const TypeAbstractFloat = new ScalarType( +export const TypeI16: ScalarType = new ScalarType('i16', 2, (buf: Uint8Array, offset: number) => + i16(valueFromBytes(workingDataI16, buf, offset)) +); +export const TypeU16: ScalarType = new ScalarType('u16', 2, (buf: Uint8Array, offset: number) => + u16(valueFromBytes(workingDataU16, buf, offset)) +); +export const TypeI8: ScalarType = new ScalarType('i8', 1, (buf: Uint8Array, offset: number) => + i8(valueFromBytes(workingDataI8, buf, offset)) +); +export const TypeU8: ScalarType = new ScalarType('u8', 1, (buf: Uint8Array, offset: number) => + u8(valueFromBytes(workingDataU8, buf, offset)) +); +export const TypeAbstractFloat: ScalarType = new ScalarType( 'abstract-float', 8, (buf: Uint8Array, offset: number) => abstractFloat(valueFromBytes(workingDataF64, buf, offset)) ); -export const TypeF64 = new ScalarType('f64', 8, (buf: Uint8Array, offset: number) => +export const TypeF64: ScalarType = new ScalarType('f64', 8, (buf: Uint8Array, offset: number) => f64(valueFromBytes(workingDataF64, buf, offset)) ); -export const TypeF32 = new ScalarType('f32', 4, (buf: Uint8Array, offset: number) => +export const TypeF32: ScalarType = new ScalarType('f32', 4, (buf: Uint8Array, offset: number) => f32(valueFromBytes(workingDataF32, buf, offset)) ); -export const TypeI16 = new ScalarType('i16', 2, (buf: Uint8Array, offset: number) => - i16(valueFromBytes(workingDataI16, buf, offset)) -); -export const TypeU16 = new ScalarType('u16', 2, (buf: Uint8Array, offset: number) => - u16(valueFromBytes(workingDataU16, buf, offset)) -); -export const TypeF16 = new ScalarType('f16', 2, (buf: Uint8Array, offset: number) => +export const TypeF16: ScalarType = new ScalarType('f16', 2, (buf: Uint8Array, offset: number) => f16Bits(valueFromBytes(workingDataU16, buf, offset)) ); -export const TypeI8 = new ScalarType('i8', 1, (buf: Uint8Array, offset: number) => - i8(valueFromBytes(workingDataI8, buf, offset)) -); -export const TypeU8 = new ScalarType('u8', 1, (buf: Uint8Array, offset: number) => - u8(valueFromBytes(workingDataU8, buf, offset)) -); -export const TypeBool = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => +export const TypeBool: ScalarType = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => bool(valueFromBytes(workingDataU32, buf, offset) !== 0) ); @@ -887,7 +887,7 @@ export function numElementsOf(ty: Type): number { /** @returns the scalar elements of the given Value */ export function elementsOf(value: Value): Scalar[] { - if (value instanceof Scalar) { + if (isScalar(value)) { return [value]; } if (value instanceof Vector) { @@ -913,24 +913,34 @@ export function scalarTypeOf(ty: Type): ScalarType { throw new Error(`unhandled type ${ty}`); } -/** ScalarValue is the JS type that can be held by a Scalar */ -type ScalarValue = boolean | number | bigint; +function hex(sizeInBytes: number, bitsLow: number, bitsHigh?: number) { + let hex = ''; + workingDataU32[0] = bitsLow; + if (bitsHigh !== undefined) { + workingDataU32[1] = bitsHigh; + } + for (let i = 0; i < sizeInBytes; ++i) { + hex = workingDataU8[i].toString(16).padStart(2, '0') + hex; + } + return `0x${hex}`; +} -/** Class that encapsulates a single scalar value of various types. */ -export class Scalar { - readonly value: ScalarValue; // The scalar value - readonly type: ScalarType; // The type of the scalar +function withPoint(x: number) { + const str = `${x}`; + return str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; +} - // The scalar value, packed in one or two 32-bit unsigned integers. - // Whether or not the bits1 is used depends on `this.type.size`. - readonly bits1: number; - readonly bits0: number; +/** Class that encapsulates a single abstract-int value. */ +export class AbstractIntValue { + readonly value: bigint; // The abstract-integer value + readonly bitsLow: number; // The low 32 bits of the abstract-integer value. + readonly bitsHigh: number; // The high 32 bits of the abstract-integer value. + readonly type = TypeAbstractInt; // The type of the value. - public constructor(type: ScalarType, value: ScalarValue, bits1: number, bits0: number) { + public constructor(value: bigint, bitsLow: number, bitsHigh: number) { this.value = value; - this.type = type; - this.bits1 = bits1; - this.bits0 = bits0; + this.bitsLow = bitsLow; + this.bitsHigh = bitsHigh; } /** @@ -939,229 +949,583 @@ export class Scalar { * @param offset the offset in buffer, in units of `buffer` */ public copyTo(buffer: TypedArrayBufferView, offset: number) { - assert( - this.type.kind !== 'abstract-int', - `Copying 'abstract-int' values to/from buffers is yet implemented` - ); - assert(this.type.kind !== 'f64', `Copying f64 values to/from buffers is not defined`); - workingDataU32[1] = this.bits1; - workingDataU32[0] = this.bits0; - for (let i = 0; i < this.type.size; i++) { + workingDataU32[0] = this.bitsLow; + workingDataU32[1] = this.bitsHigh; + for (let i = 0; i < 8; i++) { buffer[offset + i] = workingDataU8[i]; } } + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + // WGSL parses negative numbers as a negated positive. + // This means '-9223372036854775808' parses as `-' & '9223372036854775808', so must be written as + // '(-9223372036854775807 - 1)' in WGSL, because '9223372036854775808' is not a valid AbstractInt. + if (this.value === -9223372036854775808n) { + return `(-9223372036854775807 - 1)`; + } + return `${this.value}`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(8, this.bitsLow, this.bitsHigh)})`; + } +} + +/** Class that encapsulates a single abstract-float value. */ +export class AbstractFloatValue { + readonly value: number; // The f32 value + readonly bitsLow: number; // The low 32 bits of the abstract-float value. + readonly bitsHigh: number; // The high 32 bits of the abstract-float value. + readonly type = TypeAbstractFloat; // The type of the value. + + public constructor(value: number, bitsLow: number, bitsHigh: number) { + this.value = value; + this.bitsLow = bitsLow; + this.bitsHigh = bitsHigh; + } + /** - * @returns the WGSL representation of this scalar value + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.bitsLow; + workingDataU32[1] = this.bitsHigh; + for (let i = 0; i < 8; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ public wgsl(): string { - const withPoint = (x: number) => { - const str = `${x}`; - return str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; - }; + return `${withPoint(this.value)}`; + } - switch (typeof this.value) { - case 'bigint': - if (this.type.kind === 'abstract-int') { - // WGSL parses negative numbers as a negated positive. - // This means '-9223372036854775808' parses as `-' & - // '9223372036854775808', so must be written as - // '(-9223372036854775807 - 1)' in WGSL, because '9223372036854775808' - // is not a valid AbstractInt. - if (this.value === -9223372036854775808n) { - return `(-9223372036854775807 - 1)`; - } - return `${this.value}`; - } - break; - case 'number': - if (!isFinite(this.value)) break; - switch (this.type.kind) { - case 'abstract-float': - return `${withPoint(this.value)}`; - case 'f64': - return `${withPoint(this.value)}`; - case 'f32': - return `${withPoint(this.value)}f`; - case 'f16': - return `${withPoint(this.value)}h`; - case 'u32': - return `${this.value}u`; - case 'i32': - return `i32(${this.value})`; - } - break; - case 'boolean': - return `${this.value}`; + public toString(): string { + switch (this.value) { + case Infinity: + case -Infinity: + return Colors.bold(this.value.toString()); + default: { + let str = this.value.toString(); + str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; + return isSubnormalNumberF64(this.value.valueOf()) + ? `${Colors.bold(str)} (${hex(8, this.bitsLow, this.bitsHigh)} subnormal)` + : `${Colors.bold(str)} (${hex(8, this.bitsLow, this.bitsHigh)})`; + } + } + } +} + +/** Class that encapsulates a single i32 value. */ +export class I32Value { + readonly value: number; // The i32 value + readonly bits: number; // The i32 value, bitcast to a 32-bit integer. + readonly type = TypeI32; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.bits; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; } + } - throw new Error( - `scalar of value ${this.value} and type ${this.type} has no WGSL representation` - ); + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `i32(${this.value})`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(4, this.bits)})`; + } +} + +/** Class that encapsulates a single u32 value. */ +export class U32Value { + readonly value: number; // The u32 value + readonly type = TypeU32; // The type of the value. + + public constructor(value: number) { + this.value = value; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.value; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `${this.value}u`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(4, this.value)})`; + } +} + +/** + * Class that encapsulates a single i16 value. + * @note type does not exist in WGSL yet + */ +export class I16Value { + readonly value: number; // The i16 value + readonly bits: number; // The i16 value, bitcast to a 16-bit integer. + readonly type = TypeI16; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU16[0] = this.bits; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `i16(${this.value})`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(2, this.bits)})`; + } +} + +/** + * Class that encapsulates a single u16 value. + * @note type does not exist in WGSL yet + */ +export class U16Value { + readonly value: number; // The u16 value + readonly type = TypeU16; // The type of the value. + + public constructor(value: number) { + this.value = value; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU16[0] = this.value; + for (let i = 0; i < 2; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + assert(false, 'u16 is not a WGSL type'); + return `u16(${this.value})`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(2, this.value)})`; + } +} + +/** + * Class that encapsulates a single i8 value. + * @note type does not exist in WGSL yet + */ +export class I8Value { + readonly value: number; // The i8 value + readonly bits: number; // The i8 value, bitcast to a 8-bit integer. + readonly type = TypeI8; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU8[0] = this.bits; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `i8(${this.value})`; } public toString(): string { - if (this.type.kind === 'bool') { - return Colors.bold(this.value.toString()); + return `${Colors.bold(this.value.toString())} (${hex(2, this.bits)})`; + } +} + +/** + * Class that encapsulates a single u8 value. + * @note type does not exist in WGSL yet + */ +export class U8Value { + readonly value: number; // The u8 value + readonly type = TypeU8; // The type of the value. + + public constructor(value: number) { + this.value = value; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU8[0] = this.value; + for (let i = 0; i < 2; i++) { + buffer[offset + i] = workingDataU8[i]; } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + assert(false, 'u8 is not a WGSL type'); + return `u8(${this.value})`; + } + + public toString(): string { + return `${Colors.bold(this.value.toString())} (${hex(2, this.value)})`; + } +} + +/** + * Class that encapsulates a single f64 value + * @note type does not exist in WGSL yet + */ +export class F64Value { + readonly value: number; // The f32 value + readonly bitsLow: number; // The low 32 bits of the abstract-float value. + readonly bitsHigh: number; // The high 32 bits of the abstract-float value. + readonly type = TypeF64; // The type of the value. + + public constructor(value: number, bitsLow: number, bitsHigh: number) { + this.value = value; + this.bitsLow = bitsLow; + this.bitsHigh = bitsHigh; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.bitsLow; + workingDataU32[1] = this.bitsHigh; + for (let i = 0; i < 8; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + assert(false, 'f64 is not a WGSL type'); + return `${withPoint(this.value)}`; + } + + public toString(): string { switch (this.value) { case Infinity: case -Infinity: return Colors.bold(this.value.toString()); default: { - workingDataU32[1] = this.bits1; - workingDataU32[0] = this.bits0; - let hex = ''; - for (let i = 0; i < this.type.size; ++i) { - hex = workingDataU8[i].toString(16).padStart(2, '0') + hex; - } - const n = this.value as Number; - if (n !== null && isFloatValue(this)) { - let str = this.value.toString(); - str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; - switch (this.type.kind) { - case 'abstract-float': - return isSubnormalNumberF64(n.valueOf()) - ? `${Colors.bold(str)} (0x${hex} subnormal)` - : `${Colors.bold(str)} (0x${hex})`; - case 'f64': - return isSubnormalNumberF64(n.valueOf()) - ? `${Colors.bold(str)} (0x${hex} subnormal)` - : `${Colors.bold(str)} (0x${hex})`; - case 'f32': - return isSubnormalNumberF32(n.valueOf()) - ? `${Colors.bold(str)} (0x${hex} subnormal)` - : `${Colors.bold(str)} (0x${hex})`; - case 'f16': - return isSubnormalNumberF16(n.valueOf()) - ? `${Colors.bold(str)} (0x${hex} subnormal)` - : `${Colors.bold(str)} (0x${hex})`; - default: - unreachable( - `Printing of floating point kind ${this.type.kind} is not implemented...` - ); - } - } - return `${Colors.bold(this.value.toString())} (0x${hex})`; + let str = this.value.toString(); + str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; + return isSubnormalNumberF64(this.value.valueOf()) + ? `${Colors.bold(str)} (${hex(8, this.bitsLow, this.bitsHigh)} subnormal)` + : `${Colors.bold(str)} (${hex(8, this.bitsLow, this.bitsHigh)})`; } } } } -export interface ScalarBuilder { - (value: T): Scalar; +/** Class that encapsulates a single f32 value. */ +export class F32Value { + readonly value: number; // The f32 value + readonly bits: number; // The f32 value, bitcast to a 32-bit integer. + readonly type = TypeF32; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU32[0] = this.bits; + for (let i = 0; i < 4; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `${withPoint(this.value)}f`; + } + + public toString(): string { + switch (this.value) { + case Infinity: + case -Infinity: + return Colors.bold(this.value.toString()); + default: { + let str = this.value.toString(); + str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; + return isSubnormalNumberF32(this.value.valueOf()) + ? `${Colors.bold(str)} (${hex(4, this.bits)} subnormal)` + : `${Colors.bold(str)} (${hex(4, this.bits)})`; + } + } + } } -/** Create a Scalar of `type` by storing `value` as an element of `workingDataArray` and retrieving it. - * The working data array *must* be an alias of `workingData`. - */ -function scalarFromValue( - type: ScalarType, - workingDataArray: A, - value: ArrayElementType -): Scalar { - // Clear all bits of the working data since `value` may be smaller; the upper bits should be 0. - workingDataU32[1] = 0; - workingDataU32[0] = 0; - workingDataArray[0] = value; - return new Scalar(type, workingDataArray[0], workingDataU32[1], workingDataU32[0]); -} - -/** Create a Scalar of `type` by storing `value` as an element of `workingDataStoreArray` and - * reinterpreting it as an element of `workingDataLoadArray`. - * Both working data arrays *must* be aliases of `workingData`. - */ -function scalarFromBits( - type: ScalarType, - workingDataStoreArray: A, - workingDataLoadArray: TypedArrayBufferView, - bits: ArrayElementType -): Scalar { - // Clear all bits of the working data since `value` may be smaller; the upper bits should be 0. - workingDataU32[1] = 0; - workingDataU32[0] = 0; - workingDataStoreArray[0] = bits; - return new Scalar(type, workingDataLoadArray[0], workingDataU32[1], workingDataU32[0]); +/** Class that encapsulates a single f16 value. */ +export class F16Value { + readonly value: number; // The f16 value + readonly bits: number; // The f16 value, bitcast to a 16-bit integer. + readonly type = TypeF16; // The type of the value. + + public constructor(value: number, bits: number) { + this.value = value; + this.bits = bits; + } + + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + workingDataU16[0] = this.bits; + for (let i = 0; i < 2; i++) { + buffer[offset + i] = workingDataU8[i]; + } + } + + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return `${withPoint(this.value)}h`; + } + + public toString(): string { + switch (this.value) { + case Infinity: + case -Infinity: + return Colors.bold(this.value.toString()); + default: { + let str = this.value.toString(); + str = str.indexOf('.') > 0 || str.indexOf('e') > 0 ? str : `${str}.0`; + return isSubnormalNumberF16(this.value.valueOf()) + ? `${Colors.bold(str)} (${hex(2, this.bits)} subnormal)` + : `${Colors.bold(str)} (${hex(2, this.bits)})`; + } + } + } } +/** Class that encapsulates a single bool value. */ +export class BoolValue { + readonly value: boolean; // The bool value + readonly type = TypeBool; // The type of the value. -/** Create an AbstractFloat from a numeric value, a JS `number`. */ -export const abstractFloat = (value: number): Scalar => - scalarFromValue(TypeAbstractFloat, workingDataF64, value); + public constructor(value: boolean) { + this.value = value; + } -/** Create an f64 from a numeric value, a JS `number`. */ -export const f64 = (value: number): Scalar => scalarFromValue(TypeF64, workingDataF64, value); + /** + * Copies the scalar value to the buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the offset in buffer, in units of `buffer` + */ + public copyTo(buffer: TypedArrayBufferView, offset: number) { + buffer[offset] = this.value ? 1 : 0; + } -/** Create an f32 from a numeric value, a JS `number`. */ -export const f32 = (value: number): Scalar => scalarFromValue(TypeF32, workingDataF32, value); + /** @returns the WGSL representation of this scalar value */ + public wgsl(): string { + return this.value.toString(); + } -/** Create an f16 from a numeric value, a JS `number`. */ -export const f16 = (value: number): Scalar => scalarFromValue(TypeF16, workingDataF16, value); + public toString(): string { + return Colors.bold(this.value.toString()); + } +} -/** Create an f32 from a bit representation, a uint32 represented as a JS `number`. */ -export const f32Bits = (bits: number): Scalar => - scalarFromBits(TypeF32, workingDataU32, workingDataF32, bits); +/** Scalar represents all the scalar value types */ +export type Scalar = + | AbstractIntValue + | AbstractFloatValue + | I32Value + | U32Value + | I16Value + | U16Value + | I8Value + | U8Value + | F64Value + | F32Value + | F16Value + | BoolValue; -/** Create an f16 from a bit representation, a uint16 represented as a JS `number`. */ -export const f16Bits = (bits: number): Scalar => - scalarFromBits(TypeF16, workingDataU16, workingDataF16, bits); +export interface ScalarBuilder { + (value: T): Scalar; +} -/** Create an AbstractInt from a numeric value, a JS `bigint`. */ -export const abstractInt = (value: bigint): Scalar => - scalarFromValue(TypeAbstractInt, workingDataI64, value); +export function isScalar(value: object): value is Scalar { + return ( + value instanceof AbstractIntValue || + value instanceof AbstractFloatValue || + value instanceof I32Value || + value instanceof U32Value || + value instanceof I16Value || + value instanceof U16Value || + value instanceof I8Value || + value instanceof U8Value || + value instanceof F64Value || + value instanceof F32Value || + value instanceof F16Value || + value instanceof BoolValue + ); +} -export const abstractIntBits = (bits: bigint): Scalar => - scalarFromBits(TypeAbstractInt, workingDataU64, workingDataI64, bits); +/** Create an AbstractInt from a numeric value, a JS `bigint`. */ +export function abstractInt(value: bigint) { + workingDataI64[0] = value; + return new AbstractIntValue(workingDataI64[0], workingDataU32[0], workingDataU32[1]); +} -/** Create an i32 from a numeric value, a JS `number`. */ -export const i32 = (value: number): Scalar => scalarFromValue(TypeI32, workingDataI32, value); +/** Create an AbstractInt from a bit representation, a uint64 represented as a JS `bigint`. */ +export function abstractIntBits(value: bigint) { + workingDataU64[0] = value; + return new AbstractIntValue(workingDataI64[0], workingDataU32[0], workingDataU32[1]); +} -/** Create an i16 from a numeric value, a JS `number`. */ -export const i16 = (value: number): Scalar => scalarFromValue(TypeI16, workingDataI16, value); +/** Create an AbstractFloat from a numeric value, a JS `number`. */ +export function abstractFloat(value: number) { + workingDataF64[0] = value; + return new AbstractFloatValue(workingDataF64[0], workingDataU32[0], workingDataU32[1]); +} -/** Create an i8 from a numeric value, a JS `number`. */ -export const i8 = (value: number): Scalar => scalarFromValue(TypeI8, workingDataI8, value); +/** Create an i32 from a numeric value, a JS `number`. */ +export function i32(value: number) { + workingDataI32[0] = value; + return new I32Value(workingDataI32[0], workingDataU32[0]); +} /** Create an i32 from a bit representation, a uint32 represented as a JS `number`. */ -export const i32Bits = (bits: number): Scalar => - scalarFromBits(TypeI32, workingDataU32, workingDataI32, bits); +export function i32Bits(bits: number) { + workingDataU32[0] = bits; + return new I32Value(workingDataI32[0], workingDataU32[0]); +} -/** Create an i16 from a bit representation, a uint16 represented as a JS `number`. */ -export const i16Bits = (bits: number): Scalar => - scalarFromBits(TypeI16, workingDataU16, workingDataI16, bits); +/** Create a u32 from a numeric value, a JS `number`. */ +export function u32(value: number) { + workingDataU32[0] = value; + return new U32Value(workingDataU32[0]); +} -/** Create an i8 from a bit representation, a uint8 represented as a JS `number`. */ -export const i8Bits = (bits: number): Scalar => - scalarFromBits(TypeI8, workingDataU8, workingDataI8, bits); +/** Create a u32 from a bit representation, a uint32 represented as a JS `number`. */ +export function u32Bits(bits: number) { + workingDataU32[0] = bits; + return new U32Value(workingDataU32[0]); +} -/** Create a u32 from a numeric value, a JS `number`. */ -export const u32 = (value: number): Scalar => scalarFromValue(TypeU32, workingDataU32, value); +/** Create an i16 from a numeric value, a JS `number`. */ +export function i16(value: number) { + workingDataI16[0] = value; + return new I16Value(value, workingDataU16[0]); +} /** Create a u16 from a numeric value, a JS `number`. */ -export const u16 = (value: number): Scalar => scalarFromValue(TypeU16, workingDataU16, value); +export function u16(value: number) { + workingDataU16[0] = value; + return new U16Value(workingDataU16[0]); +} + +/** Create an i8 from a numeric value, a JS `number`. */ +export function i8(value: number) { + workingDataI8[0] = value; + return new I8Value(value, workingDataU8[0]); +} /** Create a u8 from a numeric value, a JS `number`. */ -export const u8 = (value: number): Scalar => scalarFromValue(TypeU8, workingDataU8, value); +export function u8(value: number) { + workingDataU8[0] = value; + return new U8Value(workingDataU8[0]); +} -/** Create an u32 from a bit representation, a uint32 represented as a JS `number`. */ -export const u32Bits = (bits: number): Scalar => - scalarFromBits(TypeU32, workingDataU32, workingDataU32, bits); +/** Create an f64 from a numeric value, a JS `number`. */ +export function f64(value: number) { + workingDataF64[0] = value; + return new F64Value(value, workingDataU32[0], workingDataU32[1]); +} -/** Create an u16 from a bit representation, a uint16 represented as a JS `number`. */ -export const u16Bits = (bits: number): Scalar => - scalarFromBits(TypeU16, workingDataU16, workingDataU16, bits); +/** Create an f32 from a numeric value, a JS `number`. */ +export function f32(value: number) { + workingDataF32[0] = value; + return new F32Value(value, workingDataU32[0]); +} + +/** Create an f32 from a bit representation, a uint32 represented as a JS `number`. */ +export function f32Bits(bits: number) { + workingDataU32[0] = bits; + return new F32Value(workingDataF32[0], bits); +} -/** Create an u8 from a bit representation, a uint8 represented as a JS `number`. */ -export const u8Bits = (bits: number): Scalar => - scalarFromBits(TypeU8, workingDataU8, workingDataU8, bits); +/** Create an f16 from a numeric value, a JS `number`. */ +export function f16(value: number) { + workingDataF16[0] = value; + return new F16Value(value, workingDataU16[0]); +} + +/** Create an f16 from a bit representation, a uint16 represented as a JS `number`. */ +export function f16Bits(bits: number) { + workingDataU16[0] = bits; + return new F16Value(workingDataF16[0], bits); +} /** Create a boolean value. */ export function bool(value: boolean): Scalar { - // WGSL does not support using 'bool' types directly in storage / uniform - // buffers, so instead we pack booleans in a u32, where 'false' is zero and - // 'true' is any non-zero value. - workingDataU32[0] = value ? 1 : 0; - workingDataU32[1] = 0; - return new Scalar(TypeBool, value, workingDataU32[1], workingDataU32[0]); + return new BoolValue(value); } /** A 'true' literal value */ @@ -1528,7 +1892,7 @@ export function serializeValue(s: BinaryStream, v: Value) { } }; - if (v instanceof Scalar) { + if (isScalar(v)) { s.writeU8(SerializedValueKind.Scalar); serializeScalarKind(s, v.type.kind); serializeScalar(v, v.type.kind); From b9fda017e5f316d9bcc7e11be6aec9216aeaf9f5 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Thu, 7 Mar 2024 16:06:17 +0000 Subject: [PATCH 11/13] Refactor Value and Types Suffix all Value TS-types with 'Value'. Put all Types in a 'Type' namespace. This keeps naming consistent, closer to WGSL, and clears the way for Type.array. --- src/resources/cache/hashes.json | 216 +++++----- src/unittests/conversion.spec.ts | 14 +- .../render_pipeline/sample_mask.spec.ts | 12 +- .../expression/binary/af_addition.spec.ts | 20 +- .../expression/binary/af_comparison.cache.ts | 18 +- .../expression/binary/af_comparison.spec.ts | 44 +- .../expression/binary/af_division.spec.ts | 20 +- .../binary/af_matrix_addition.spec.ts | 8 +- .../af_matrix_matrix_multiplication.spec.ts | 6 +- .../af_matrix_scalar_multiplication.spec.ts | 10 +- .../binary/af_matrix_subtraction.spec.ts | 8 +- .../af_matrix_vector_multiplication.spec.ts | 10 +- .../binary/af_multiplication.spec.ts | 20 +- .../expression/binary/af_remainder.spec.ts | 18 +- .../expression/binary/af_subtraction.spec.ts | 20 +- .../expression/binary/ai_arithmetic.spec.ts | 62 +-- .../expression/binary/ai_comparison.spec.ts | 16 +- .../expression/binary/bitwise.spec.ts | 10 +- .../expression/binary/bitwise_shift.spec.ts | 10 +- .../expression/binary/bool_logical.spec.ts | 18 +- .../expression/binary/f16_addition.spec.ts | 20 +- .../expression/binary/f16_comparison.cache.ts | 30 +- .../expression/binary/f16_comparison.spec.ts | 14 +- .../expression/binary/f16_division.spec.ts | 20 +- .../binary/f16_matrix_addition.spec.ts | 10 +- .../f16_matrix_matrix_multiplication.spec.ts | 10 +- .../f16_matrix_scalar_multiplication.spec.ts | 14 +- .../binary/f16_matrix_subtraction.spec.ts | 10 +- .../f16_matrix_vector_multiplication.spec.ts | 14 +- .../binary/f16_multiplication.spec.ts | 20 +- .../expression/binary/f16_remainder.spec.ts | 20 +- .../expression/binary/f16_subtraction.spec.ts | 20 +- .../expression/binary/f32_addition.spec.ts | 20 +- .../expression/binary/f32_comparison.cache.ts | 30 +- .../expression/binary/f32_comparison.spec.ts | 14 +- .../expression/binary/f32_division.spec.ts | 20 +- .../binary/f32_matrix_addition.spec.ts | 10 +- .../f32_matrix_matrix_multiplication.spec.ts | 10 +- .../f32_matrix_scalar_multiplication.spec.ts | 14 +- .../binary/f32_matrix_subtraction.spec.ts | 10 +- .../f32_matrix_vector_multiplication.spec.ts | 14 +- .../binary/f32_multiplication.spec.ts | 20 +- .../expression/binary/f32_remainder.spec.ts | 20 +- .../expression/binary/f32_subtraction.spec.ts | 20 +- .../expression/binary/i32_arithmetic.spec.ts | 82 ++-- .../expression/binary/i32_comparison.spec.ts | 14 +- .../expression/binary/u32_arithmetic.spec.ts | 82 ++-- .../expression/binary/u32_comparison.spec.ts | 14 +- .../expression/call/builtin/abs.spec.ts | 29 +- .../expression/call/builtin/acos.spec.ts | 8 +- .../expression/call/builtin/acosh.spec.ts | 8 +- .../expression/call/builtin/all.spec.ts | 20 +- .../expression/call/builtin/any.spec.ts | 20 +- .../expression/call/builtin/asin.spec.ts | 8 +- .../expression/call/builtin/asinh.spec.ts | 8 +- .../expression/call/builtin/atan.spec.ts | 8 +- .../expression/call/builtin/atan2.spec.ts | 8 +- .../expression/call/builtin/atanh.spec.ts | 8 +- .../expression/call/builtin/bitcast.cache.ts | 32 +- .../expression/call/builtin/bitcast.spec.ts | 132 ++---- .../expression/call/builtin/ceil.spec.ts | 8 +- .../expression/call/builtin/clamp.cache.ts | 18 +- .../expression/call/builtin/clamp.spec.ts | 29 +- .../expression/call/builtin/cos.spec.ts | 8 +- .../expression/call/builtin/cosh.spec.ts | 8 +- .../call/builtin/countLeadingZeros.spec.ts | 6 +- .../call/builtin/countOneBits.spec.ts | 6 +- .../call/builtin/countTrailingZeros.spec.ts | 6 +- .../expression/call/builtin/cross.spec.ts | 26 +- .../expression/call/builtin/degrees.spec.ts | 12 +- .../call/builtin/determinant.spec.ts | 8 +- .../expression/call/builtin/distance.spec.ts | 62 +-- .../expression/call/builtin/dot.spec.ts | 131 +----- .../call/builtin/dot4I8Packed.spec.ts | 4 +- .../call/builtin/dot4U8Packed.spec.ts | 4 +- .../expression/call/builtin/exp.spec.ts | 8 +- .../expression/call/builtin/exp2.spec.ts | 8 +- .../call/builtin/extractBits.spec.ts | 20 +- .../call/builtin/faceForward.spec.ts | 28 +- .../call/builtin/firstLeadingBit.spec.ts | 6 +- .../call/builtin/firstTrailingBit.spec.ts | 6 +- .../expression/call/builtin/floor.spec.ts | 12 +- .../expression/call/builtin/fma.spec.ts | 12 +- .../expression/call/builtin/fract.spec.ts | 8 +- .../expression/call/builtin/frexp.cache.ts | 10 +- .../expression/call/builtin/frexp.spec.ts | 34 +- .../call/builtin/insertBits.spec.ts | 18 +- .../call/builtin/inversesqrt.spec.ts | 8 +- .../expression/call/builtin/ldexp.spec.ts | 10 +- .../expression/call/builtin/length.spec.ts | 20 +- .../expression/call/builtin/log.spec.ts | 8 +- .../expression/call/builtin/log2.spec.ts | 8 +- .../expression/call/builtin/max.spec.ts | 32 +- .../expression/call/builtin/min.spec.ts | 32 +- .../expression/call/builtin/mix.spec.ts | 80 +--- .../expression/call/builtin/modf.spec.ts | 78 ++-- .../expression/call/builtin/normalize.spec.ts | 16 +- .../call/builtin/pack2x16float.spec.ts | 4 +- .../call/builtin/pack2x16snorm.spec.ts | 12 +- .../call/builtin/pack2x16unorm.spec.ts | 12 +- .../call/builtin/pack4x8snorm.spec.ts | 20 +- .../call/builtin/pack4x8unorm.spec.ts | 20 +- .../expression/call/builtin/pack4xI8.spec.ts | 4 +- .../call/builtin/pack4xI8Clamp.spec.ts | 4 +- .../expression/call/builtin/pack4xU8.spec.ts | 4 +- .../call/builtin/pack4xU8Clamp.spec.ts | 4 +- .../expression/call/builtin/pow.spec.ts | 8 +- .../call/builtin/quantizeToF16.spec.ts | 4 +- .../expression/call/builtin/radians.spec.ts | 12 +- .../expression/call/builtin/reflect.spec.ts | 58 +-- .../expression/call/builtin/refract.spec.ts | 28 +- .../call/builtin/reverseBits.spec.ts | 6 +- .../expression/call/builtin/round.spec.ts | 12 +- .../expression/call/builtin/saturate.spec.ts | 12 +- .../expression/call/builtin/select.spec.ts | 47 +-- .../expression/call/builtin/sign.spec.ts | 22 +- .../expression/call/builtin/sin.spec.ts | 8 +- .../expression/call/builtin/sinh.spec.ts | 8 +- .../call/builtin/smoothstep.spec.ts | 8 +- .../expression/call/builtin/sqrt.spec.ts | 8 +- .../expression/call/builtin/step.spec.ts | 8 +- .../expression/call/builtin/tan.spec.ts | 8 +- .../expression/call/builtin/tanh.spec.ts | 8 +- .../expression/call/builtin/transpose.spec.ts | 16 +- .../expression/call/builtin/trunc.spec.ts | 12 +- .../call/builtin/unpack2x16float.spec.ts | 4 +- .../call/builtin/unpack2x16snorm.spec.ts | 4 +- .../call/builtin/unpack2x16unorm.spec.ts | 4 +- .../call/builtin/unpack4x8snorm.spec.ts | 4 +- .../call/builtin/unpack4x8unorm.spec.ts | 4 +- .../call/builtin/unpack4xI8.spec.ts | 4 +- .../call/builtin/unpack4xU8.spec.ts | 4 +- .../shader/execution/expression/case.ts | 21 +- .../shader/execution/expression/case_cache.ts | 8 +- .../execution/expression/expectation.ts | 8 +- .../shader/execution/expression/expression.ts | 26 +- .../expression/unary/af_arithmetic.spec.ts | 8 +- .../expression/unary/af_assignment.spec.ts | 16 +- .../expression/unary/ai_arithmetic.spec.ts | 4 +- .../expression/unary/ai_assignment.spec.ts | 8 +- .../expression/unary/ai_complement.spec.ts | 6 +- .../expression/unary/bool_conversion.cache.ts | 6 +- .../expression/unary/bool_conversion.spec.ts | 19 +- .../expression/unary/bool_logical.spec.ts | 4 +- .../expression/unary/f16_arithmetic.spec.ts | 4 +- .../expression/unary/f16_conversion.spec.ts | 27 +- .../expression/unary/f32_arithmetic.spec.ts | 4 +- .../expression/unary/f32_conversion.spec.ts | 27 +- .../expression/unary/i32_arithmetic.spec.ts | 4 +- .../expression/unary/i32_complement.spec.ts | 4 +- .../expression/unary/i32_conversion.spec.ts | 12 +- .../expression/unary/u32_complement.spec.ts | 4 +- .../expression/unary/u32_conversion.spec.ts | 14 +- .../expression/call/builtin/abs.spec.ts | 4 +- .../expression/call/builtin/acos.spec.ts | 9 +- .../expression/call/builtin/acosh.spec.ts | 13 +- .../expression/call/builtin/asin.spec.ts | 9 +- .../expression/call/builtin/asinh.spec.ts | 12 +- .../expression/call/builtin/atan.spec.ts | 9 +- .../expression/call/builtin/atan2.spec.ts | 18 +- .../expression/call/builtin/atanh.spec.ts | 9 +- .../expression/call/builtin/ceil.spec.ts | 9 +- .../expression/call/builtin/clamp.spec.ts | 4 +- .../call/builtin/const_override_validation.ts | 3 +- .../expression/call/builtin/cos.spec.ts | 9 +- .../expression/call/builtin/cosh.spec.ts | 12 +- .../expression/call/builtin/degrees.spec.ts | 12 +- .../call/builtin/derivatives.spec.ts | 14 +- .../expression/call/builtin/exp.spec.ts | 12 +- .../expression/call/builtin/exp2.spec.ts | 12 +- .../expression/call/builtin/floor.spec.ts | 9 +- .../call/builtin/inverseSqrt.spec.ts | 12 +- .../expression/call/builtin/length.spec.ts | 20 +- .../expression/call/builtin/log.spec.ts | 9 +- .../expression/call/builtin/log2.spec.ts | 9 +- .../expression/call/builtin/modf.spec.ts | 9 +- .../expression/call/builtin/radians.spec.ts | 9 +- .../expression/call/builtin/round.spec.ts | 9 +- .../expression/call/builtin/saturate.spec.ts | 9 +- .../expression/call/builtin/sign.spec.ts | 9 +- .../expression/call/builtin/sin.spec.ts | 9 +- .../expression/call/builtin/sinh.spec.ts | 12 +- .../expression/call/builtin/sqrt.spec.ts | 12 +- .../expression/call/builtin/tan.spec.ts | 12 +- src/webgpu/util/compare.ts | 27 +- src/webgpu/util/conversion.ts | 387 ++++++++++-------- src/webgpu/util/floating_point.ts | 6 +- 187 files changed, 1628 insertions(+), 1993 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 8332f3962c10..6733022574c8 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "cfc28768", - "webgpu/shader/execution/binary/af_logical.bin": "ae9b9ba0", - "webgpu/shader/execution/binary/af_division.bin": "30839274", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "e43d31a5", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "fbec81c8", - "webgpu/shader/execution/binary/af_multiplication.bin": "19681c0c", - "webgpu/shader/execution/binary/af_remainder.bin": "fb9c916", - "webgpu/shader/execution/binary/af_subtraction.bin": "6f448b72", - "webgpu/shader/execution/binary/f16_addition.bin": "83ac7e89", - "webgpu/shader/execution/binary/f16_logical.bin": "6c9cdfe8", - "webgpu/shader/execution/binary/f16_division.bin": "487729fe", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "54aa39b", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "3a4159f8", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "fceaaf1f", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "92151406", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "d920f7ec", - "webgpu/shader/execution/binary/f16_multiplication.bin": "fc8c2a0f", - "webgpu/shader/execution/binary/f16_remainder.bin": "6e60c5d0", - "webgpu/shader/execution/binary/f16_subtraction.bin": "4277226e", - "webgpu/shader/execution/binary/f32_addition.bin": "5b9c4270", - "webgpu/shader/execution/binary/f32_logical.bin": "32188047", - "webgpu/shader/execution/binary/f32_division.bin": "22edca65", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "62107e07", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "9e5677c9", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "d69476cb", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c429b29b", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "ded0240e", - "webgpu/shader/execution/binary/f32_multiplication.bin": "f088c036", - "webgpu/shader/execution/binary/f32_remainder.bin": "dcb44e72", - "webgpu/shader/execution/binary/f32_subtraction.bin": "dab334aa", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "2b48d275", - "webgpu/shader/execution/binary/i32_comparison.bin": "f2c3cd37", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "7ec7b66", - "webgpu/shader/execution/binary/u32_comparison.bin": "50ab2f61", - "webgpu/shader/execution/abs.bin": "e37fc25a", - "webgpu/shader/execution/acos.bin": "4dc16324", - "webgpu/shader/execution/acosh.bin": "1f999f3c", - "webgpu/shader/execution/asin.bin": "a653cf5b", - "webgpu/shader/execution/asinh.bin": "515e83f7", - "webgpu/shader/execution/atan.bin": "95006a05", - "webgpu/shader/execution/atan2.bin": "3cc4efac", - "webgpu/shader/execution/atanh.bin": "e2985906", - "webgpu/shader/execution/bitcast.bin": "bda67f6d", - "webgpu/shader/execution/ceil.bin": "5d285a4", - "webgpu/shader/execution/clamp.bin": "cd0271cf", - "webgpu/shader/execution/cos.bin": "dfd7be1f", - "webgpu/shader/execution/cosh.bin": "8a9b86a5", - "webgpu/shader/execution/cross.bin": "80fe94ce", - "webgpu/shader/execution/degrees.bin": "d9a6c350", - "webgpu/shader/execution/determinant.bin": "2e19c9bd", - "webgpu/shader/execution/distance.bin": "64daad1a", - "webgpu/shader/execution/dot.bin": "c78ce327", - "webgpu/shader/execution/exp.bin": "c1b5a4fd", - "webgpu/shader/execution/exp2.bin": "63ac077", - "webgpu/shader/execution/faceForward.bin": "92b334a6", - "webgpu/shader/execution/floor.bin": "7e1bf2c3", - "webgpu/shader/execution/fma.bin": "701a86c6", - "webgpu/shader/execution/fract.bin": "c29aadce", - "webgpu/shader/execution/frexp.bin": "5ae6ca4", - "webgpu/shader/execution/inverseSqrt.bin": "9563f18d", - "webgpu/shader/execution/ldexp.bin": "f005b4b5", - "webgpu/shader/execution/length.bin": "ee8e5573", - "webgpu/shader/execution/log.bin": "29c4187b", - "webgpu/shader/execution/log2.bin": "9ce20393", - "webgpu/shader/execution/max.bin": "7516dbff", - "webgpu/shader/execution/min.bin": "75e9d526", - "webgpu/shader/execution/mix.bin": "b224306a", - "webgpu/shader/execution/modf.bin": "ba63df78", - "webgpu/shader/execution/normalize.bin": "9842b756", - "webgpu/shader/execution/pack2x16float.bin": "5dee8700", - "webgpu/shader/execution/pow.bin": "81bac0a9", - "webgpu/shader/execution/quantizeToF16.bin": "6973d05d", - "webgpu/shader/execution/radians.bin": "128a0639", - "webgpu/shader/execution/reflect.bin": "5de2177e", - "webgpu/shader/execution/refract.bin": "de1e1ade", - "webgpu/shader/execution/round.bin": "70a9da36", - "webgpu/shader/execution/saturate.bin": "e8daa151", - "webgpu/shader/execution/sign.bin": "503847e9", - "webgpu/shader/execution/sin.bin": "51df6447", - "webgpu/shader/execution/sinh.bin": "dbc469f3", - "webgpu/shader/execution/smoothstep.bin": "16cdfbe9", - "webgpu/shader/execution/sqrt.bin": "b59d4606", - "webgpu/shader/execution/step.bin": "5df4b04b", - "webgpu/shader/execution/tan.bin": "f1535f0c", - "webgpu/shader/execution/tanh.bin": "6e653c6d", - "webgpu/shader/execution/transpose.bin": "41ff2823", - "webgpu/shader/execution/trunc.bin": "7c3baf7", - "webgpu/shader/execution/unpack2x16float.bin": "f994910", - "webgpu/shader/execution/unpack2x16snorm.bin": "df36ee6f", - "webgpu/shader/execution/unpack2x16unorm.bin": "ac195462", - "webgpu/shader/execution/unpack4x8snorm.bin": "39a4743a", - "webgpu/shader/execution/unpack4x8unorm.bin": "ecae6fe5", - "webgpu/shader/execution/unary/af_arithmetic.bin": "9800df7f", - "webgpu/shader/execution/unary/af_assignment.bin": "85c4d516", - "webgpu/shader/execution/unary/bool_conversion.bin": "85664e85", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "3e0dea1", - "webgpu/shader/execution/unary/f16_conversion.bin": "c1f37dd1", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "f2cd5bd2", - "webgpu/shader/execution/unary/f32_conversion.bin": "b4174de", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "b327298b", - "webgpu/shader/execution/unary/i32_conversion.bin": "ebdc408a", - "webgpu/shader/execution/unary/u32_conversion.bin": "aafe3d79", - "webgpu/shader/execution/unary/ai_assignment.bin": "ef0b469b", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "b22511b9", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "67c3d3b6", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "b72f2c53", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "7f328797", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "b34a4a2b" + "webgpu/shader/execution/binary/af_addition.bin": "45a00f48", + "webgpu/shader/execution/binary/af_logical.bin": "458eba4", + "webgpu/shader/execution/binary/af_division.bin": "ed6916d6", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "ddeaa1d3", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "bdc18f23", + "webgpu/shader/execution/binary/af_multiplication.bin": "e6e11b40", + "webgpu/shader/execution/binary/af_remainder.bin": "61849bd4", + "webgpu/shader/execution/binary/af_subtraction.bin": "6afd0c9a", + "webgpu/shader/execution/binary/f16_addition.bin": "118a69b1", + "webgpu/shader/execution/binary/f16_logical.bin": "53cbe093", + "webgpu/shader/execution/binary/f16_division.bin": "6cf5db74", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "37b3e5b1", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "f9f9c546", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "10c32980", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "4a992ee0", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "3969786a", + "webgpu/shader/execution/binary/f16_multiplication.bin": "27375c0a", + "webgpu/shader/execution/binary/f16_remainder.bin": "1e5d8fc7", + "webgpu/shader/execution/binary/f16_subtraction.bin": "daffd0ed", + "webgpu/shader/execution/binary/f32_addition.bin": "384766d0", + "webgpu/shader/execution/binary/f32_logical.bin": "d4f9fd6a", + "webgpu/shader/execution/binary/f32_division.bin": "760f650f", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "504aac15", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "740e31c4", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "3c5abc3c", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c41fee39", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "43bfea5a", + "webgpu/shader/execution/binary/f32_multiplication.bin": "d6b990a9", + "webgpu/shader/execution/binary/f32_remainder.bin": "53f7d8e9", + "webgpu/shader/execution/binary/f32_subtraction.bin": "f644082", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "8ce49cc7", + "webgpu/shader/execution/binary/i32_comparison.bin": "ac0e960f", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "68560dc0", + "webgpu/shader/execution/binary/u32_comparison.bin": "ec9ec4c6", + "webgpu/shader/execution/abs.bin": "d7043582", + "webgpu/shader/execution/acos.bin": "a7a01d03", + "webgpu/shader/execution/acosh.bin": "3bfd9ebc", + "webgpu/shader/execution/asin.bin": "f91850f1", + "webgpu/shader/execution/asinh.bin": "19169ea3", + "webgpu/shader/execution/atan.bin": "51a04ddb", + "webgpu/shader/execution/atan2.bin": "e732e242", + "webgpu/shader/execution/atanh.bin": "4763b613", + "webgpu/shader/execution/bitcast.bin": "195c00a7", + "webgpu/shader/execution/ceil.bin": "9856b786", + "webgpu/shader/execution/clamp.bin": "5a700a65", + "webgpu/shader/execution/cos.bin": "ff14c921", + "webgpu/shader/execution/cosh.bin": "21c587ad", + "webgpu/shader/execution/cross.bin": "c159771f", + "webgpu/shader/execution/degrees.bin": "b0de92be", + "webgpu/shader/execution/determinant.bin": "83d642d4", + "webgpu/shader/execution/distance.bin": "e65d0cb7", + "webgpu/shader/execution/dot.bin": "dc57a00c", + "webgpu/shader/execution/exp.bin": "f0c6b19", + "webgpu/shader/execution/exp2.bin": "5d3dd4e0", + "webgpu/shader/execution/faceForward.bin": "3979a4de", + "webgpu/shader/execution/floor.bin": "3fecf76d", + "webgpu/shader/execution/fma.bin": "e7fe86b8", + "webgpu/shader/execution/fract.bin": "71caa066", + "webgpu/shader/execution/frexp.bin": "ed72dcec", + "webgpu/shader/execution/inverseSqrt.bin": "383e6e9c", + "webgpu/shader/execution/ldexp.bin": "bedfc1d5", + "webgpu/shader/execution/length.bin": "38e35ab4", + "webgpu/shader/execution/log.bin": "2517404c", + "webgpu/shader/execution/log2.bin": "a833136", + "webgpu/shader/execution/max.bin": "8c2f7c51", + "webgpu/shader/execution/min.bin": "7f732adb", + "webgpu/shader/execution/mix.bin": "982c982c", + "webgpu/shader/execution/modf.bin": "743632fc", + "webgpu/shader/execution/normalize.bin": "52adf424", + "webgpu/shader/execution/pack2x16float.bin": "2c879955", + "webgpu/shader/execution/pow.bin": "ba686c94", + "webgpu/shader/execution/quantizeToF16.bin": "e704252d", + "webgpu/shader/execution/radians.bin": "afe57c6e", + "webgpu/shader/execution/reflect.bin": "332e001e", + "webgpu/shader/execution/refract.bin": "246f6c0b", + "webgpu/shader/execution/round.bin": "e555383f", + "webgpu/shader/execution/saturate.bin": "9dce4047", + "webgpu/shader/execution/sign.bin": "3ef39d2e", + "webgpu/shader/execution/sin.bin": "8546b36c", + "webgpu/shader/execution/sinh.bin": "72ae8d37", + "webgpu/shader/execution/smoothstep.bin": "79eca0b6", + "webgpu/shader/execution/sqrt.bin": "ac8f95e9", + "webgpu/shader/execution/step.bin": "34ce6432", + "webgpu/shader/execution/tan.bin": "928e0e2f", + "webgpu/shader/execution/tanh.bin": "be078de7", + "webgpu/shader/execution/transpose.bin": "2ce22a5b", + "webgpu/shader/execution/trunc.bin": "26115486", + "webgpu/shader/execution/unpack2x16float.bin": "d052cda6", + "webgpu/shader/execution/unpack2x16snorm.bin": "a3ab8e29", + "webgpu/shader/execution/unpack2x16unorm.bin": "f42b9498", + "webgpu/shader/execution/unpack4x8snorm.bin": "5c90b367", + "webgpu/shader/execution/unpack4x8unorm.bin": "ef24abbe", + "webgpu/shader/execution/unary/af_arithmetic.bin": "28b510fa", + "webgpu/shader/execution/unary/af_assignment.bin": "4f4d507a", + "webgpu/shader/execution/unary/bool_conversion.bin": "5cbbd5e2", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "55ff626f", + "webgpu/shader/execution/unary/f16_conversion.bin": "e16712e2", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "672609de", + "webgpu/shader/execution/unary/f32_conversion.bin": "daa3ffb8", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "eecbb027", + "webgpu/shader/execution/unary/i32_conversion.bin": "c3f19a9", + "webgpu/shader/execution/unary/u32_conversion.bin": "b58b1876", + "webgpu/shader/execution/unary/ai_assignment.bin": "326020c6", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "40123e00", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "37ffc69", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "35e08b61", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "6f31c22f", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "7b17ec2a" } \ No newline at end of file diff --git a/src/unittests/conversion.spec.ts b/src/unittests/conversion.spec.ts index 8606aa871794..e144f39288a2 100644 --- a/src/unittests/conversion.spec.ts +++ b/src/unittests/conversion.spec.ts @@ -18,7 +18,7 @@ import { i32, kFloat16Format, kFloat32Format, - Matrix, + MatrixValue, numbersApproximatelyEqual, pack2x16float, pack2x16snorm, @@ -26,14 +26,14 @@ import { pack4x8snorm, pack4x8unorm, packRGB9E5UFloat, - Scalar, + ScalarValue, toMatrix, u32, unpackRGB9E5UFloat, vec2, vec3, vec4, - Vector, + VectorValue, } from '../webgpu/util/conversion.js'; import { UnitTest } from './unit_test.js'; @@ -191,7 +191,7 @@ g.test('floatBitsToULPFromZero,32').fn(t => { }); g.test('scalarWGSL').fn(t => { - const cases: Array<[Scalar, string]> = [ + const cases: Array<[ScalarValue, string]> = [ [f32(0.0), '0.0f'], // The number -0.0 can be remapped to 0.0 when stored in a Scalar // object. It is not possible to guarantee that '-0.0f' will @@ -227,7 +227,7 @@ expect: ${expect}` }); g.test('vectorWGSL').fn(t => { - const cases: Array<[Vector, string]> = [ + const cases: Array<[VectorValue, string]> = [ [vec2(f32(42.0), f32(24.0)), 'vec2(42.0f, 24.0f)'], [vec2(f16Bits(0x5140), f16Bits(0x4e00)), 'vec2(42.0h, 24.0h)'], [vec2(u32(42), u32(24)), 'vec2(42u, 24u)'], @@ -261,7 +261,7 @@ expect: ${expect}` }); g.test('matrixWGSL').fn(t => { - const cases: Array<[Matrix, string]> = [ + const cases: Array<[MatrixValue, string]> = [ [ toMatrix( [ @@ -391,7 +391,7 @@ g.test('constructorMatrix') return [...Array(rows).keys()].map(r => scalar_builder(c * cols + r)); }); - const got = new Matrix(elements); + const got = new MatrixValue(elements); const got_type = got.type; t.expect( got_type.cols === cols, diff --git a/src/webgpu/api/operation/render_pipeline/sample_mask.spec.ts b/src/webgpu/api/operation/render_pipeline/sample_mask.spec.ts index ca4a2479658e..b28e1b381cad 100644 --- a/src/webgpu/api/operation/render_pipeline/sample_mask.spec.ts +++ b/src/webgpu/api/operation/render_pipeline/sample_mask.spec.ts @@ -21,7 +21,7 @@ import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { assert, range } from '../../../../common/util/util.js'; import { GPUTest, TextureTestMixin } from '../../../gpu_test.js'; import { checkElementsPassPredicate, checkElementsEqual } from '../../../util/check_contents.js'; -import { TypeF32, TypeU32 } from '../../../util/conversion.js'; +import { Type } from '../../../util/conversion.js'; import { TexelView } from '../../../util/texture/texel_view.js'; const kColors = [ @@ -438,7 +438,7 @@ class F extends TextureTestMixin(GPUTest) { fragmentShaderOutputMask: number ) { const buffer = this.copy2DTextureToBufferUsingComputePass( - TypeF32, // correspond to 'rgba8unorm' format + Type.f32, // correspond to 'rgba8unorm' format 4, texture.createView(), sampleCount @@ -464,7 +464,7 @@ class F extends TextureTestMixin(GPUTest) { const buffer = this.copy2DTextureToBufferUsingComputePass( // Use f32 as the scalar type for depth (depth24plus, depth32float) // Use u32 as the scalar type for stencil (stencil8) - aspect === 'depth-only' ? TypeF32 : TypeU32, + aspect === 'depth-only' ? Type.f32 : Type.u32, 1, depthStencilTexture.createView({ aspect }), sampleCount @@ -705,7 +705,7 @@ color' <= color. ); const colorBuffer = t.copy2DTextureToBufferUsingComputePass( - TypeF32, // correspond to 'rgba8unorm' format + Type.f32, // correspond to 'rgba8unorm' format 4, color.createView(), sampleCount @@ -717,7 +717,7 @@ color' <= color. colorResultPromises.push(colorResult); const depthBuffer = t.copy2DTextureToBufferUsingComputePass( - TypeF32, // correspond to 'depth24plus-stencil8' format + Type.f32, // correspond to 'depth24plus-stencil8' format 1, depthStencil.createView({ aspect: 'depth-only' }), sampleCount @@ -729,7 +729,7 @@ color' <= color. depthResultPromises.push(depthResult); const stencilBuffer = t.copy2DTextureToBufferUsingComputePass( - TypeU32, // correspond to 'depth24plus-stencil8' format + Type.u32, // correspond to 'depth24plus-stencil8' format 1, depthStencil.createView({ aspect: 'stencil-only' }), sampleCount diff --git a/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts index 3b14897c2227..52a07ff328f8 100644 --- a/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_addition.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for non-matrix AbstractFloat addition expression +Execution Tests for non-matrix abstract-float addition expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_addition.cache.js'; @@ -26,8 +26,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts index 648ea5c0b0b8..e0002664018a 100644 --- a/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/af_comparison.cache.ts @@ -1,5 +1,5 @@ import { anyOf } from '../../../../util/compare.js'; -import { abstractFloat, bool, Scalar } from '../../../../util/conversion.js'; +import { abstractFloat, bool, ScalarValue } from '../../../../util/conversion.js'; import { flushSubnormalNumberF64, vectorF64Range } from '../../../../util/math.js'; import { Case } from '../case.js'; import { makeCaseCache } from '../case_cache.js'; @@ -11,7 +11,7 @@ import { makeCaseCache } from '../case_cache.js'; function makeCase( lhs: number, rhs: number, - truthFunc: (lhs: Scalar, rhs: Scalar) => boolean + truthFunc: (lhs: ScalarValue, rhs: ScalarValue) => boolean ): Case { // Subnormal float values may be flushed at any time. // https://www.w3.org/TR/WGSL/#floating-point-evaluation @@ -19,7 +19,7 @@ function makeCase( const af_rhs = abstractFloat(rhs); const lhs_options = new Set([af_lhs, abstractFloat(flushSubnormalNumberF64(lhs))]); const rhs_options = new Set([af_rhs, abstractFloat(flushSubnormalNumberF64(rhs))]); - const expected: Array = []; + const expected: Array = []; lhs_options.forEach(l => { rhs_options.forEach(r => { const result = bool(truthFunc(l, r)); @@ -34,7 +34,7 @@ function makeCase( export const d = makeCaseCache('binary/af_logical', { equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -43,7 +43,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, not_equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -52,7 +52,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, less_than: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -61,7 +61,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, less_equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -70,7 +70,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, greater_than: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -79,7 +79,7 @@ export const d = makeCaseCache('binary/af_logical', { }); }, greater_equals: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; diff --git a/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts index cc03ee4367f2..3941e1253969 100644 --- a/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_comparison.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for the AbstractFloat comparison operations +Execution Tests for the abstract-float comparison operations `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeBool } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { d } from './af_comparison.cache.js'; @@ -27,7 +27,14 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('equals'); - await run(t, binary('=='), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run( + t, + binary('=='), + [Type.abstractFloat, Type.abstractFloat], + Type.bool, + t.params, + cases + ); }); g.test('not_equals') @@ -45,7 +52,14 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('not_equals'); - await run(t, binary('!='), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run( + t, + binary('!='), + [Type.abstractFloat, Type.abstractFloat], + Type.bool, + t.params, + cases + ); }); g.test('less_than') @@ -63,7 +77,7 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('less_than'); - await run(t, binary('<'), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.abstractFloat, Type.abstractFloat], Type.bool, t.params, cases); }); g.test('less_equals') @@ -81,7 +95,14 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('less_equals'); - await run(t, binary('<='), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run( + t, + binary('<='), + [Type.abstractFloat, Type.abstractFloat], + Type.bool, + t.params, + cases + ); }); g.test('greater_than') @@ -99,7 +120,7 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('greater_than'); - await run(t, binary('>'), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.abstractFloat, Type.abstractFloat], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -117,5 +138,12 @@ Accuracy: Correct result ) .fn(async t => { const cases = await d.get('greater_equals'); - await run(t, binary('>='), [TypeAbstractFloat, TypeAbstractFloat], TypeBool, t.params, cases); + await run( + t, + binary('>='), + [Type.abstractFloat, Type.abstractFloat], + Type.bool, + t.params, + cases + ); }); diff --git a/src/webgpu/shader/execution/expression/binary/af_division.spec.ts b/src/webgpu/shader/execution/expression/binary/af_division.spec.ts index 5080e62264d2..0ebe30b6ccc9 100644 --- a/src/webgpu/shader/execution/expression/binary/af_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_division.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for non-matrix AbstractFloat division expression +Execution Tests for non-matrix abstract-float division expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_division.cache.js'; @@ -26,8 +26,8 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] await run( t, abstractFloatBinary('/'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] await run( t, abstractFloatBinary('/'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('/'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('/'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts index 7f6019f53127..49c746c53e74 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_addition.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for matrix AbstractFloat addition expressions +Execution Tests for matrix abstract-float addition expressions `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_addition.cache.js'; @@ -33,8 +33,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('+'), - [TypeMat(cols, rows, TypeAbstractFloat), TypeMat(cols, rows, TypeAbstractFloat)], - TypeMat(cols, rows, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat), Type.mat(cols, rows, Type.abstractFloat)], + Type.mat(cols, rows, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts index bb7fd668c296..a1aa005d341c 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-matrix AbstractFloat multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_matrix_multiplication.cache.js'; @@ -40,8 +40,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeMat(x_cols, x_rows, TypeAbstractFloat), TypeMat(y_cols, y_rows, TypeAbstractFloat)], - TypeMat(y_cols, x_rows, TypeAbstractFloat), + [Type.mat(x_cols, x_rows, Type.abstractFloat), Type.mat(y_cols, y_rows, Type.abstractFloat)], + Type.mat(y_cols, x_rows, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts index 05afa31275d6..c6faabbc8453 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_scalar_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-scalar and scalar-matrix AbstractFloat multiplication import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_scalar_multiplication.cache.js'; @@ -33,8 +33,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeMat(cols, rows, TypeAbstractFloat), TypeAbstractFloat], - TypeMat(cols, rows, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat), Type.abstractFloat], + Type.mat(cols, rows, Type.abstractFloat), t.params, cases ); @@ -61,8 +61,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeAbstractFloat, TypeMat(cols, rows, TypeAbstractFloat)], - TypeMat(cols, rows, TypeAbstractFloat), + [Type.abstractFloat, Type.mat(cols, rows, Type.abstractFloat)], + Type.mat(cols, rows, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts index 4c6d1d423234..9b240fdee903 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_subtraction.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for matrix AbstractFloat subtraction expression +Execution Tests for matrix abstract-float subtraction expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_subtraction.cache.js'; @@ -33,8 +33,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeMat(cols, rows, TypeAbstractFloat), TypeMat(cols, rows, TypeAbstractFloat)], - TypeMat(cols, rows, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat), Type.mat(cols, rows, Type.abstractFloat)], + Type.mat(cols, rows, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts index c464c442aa85..5db78f8369f7 100644 --- a/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_matrix_vector_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-vector and vector-matrix AbstractFloat multiplication import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeMat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_matrix_vector_multiplication.cache.js'; @@ -33,8 +33,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeMat(cols, rows, TypeAbstractFloat), TypeVec(cols, TypeAbstractFloat)], - TypeVec(rows, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat), Type.vec(cols, Type.abstractFloat)], + Type.vec(rows, Type.abstractFloat), t.params, cases ); @@ -61,8 +61,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeVec(rows, TypeAbstractFloat), TypeMat(cols, rows, TypeAbstractFloat)], - TypeVec(cols, TypeAbstractFloat), + [Type.vec(rows, Type.abstractFloat), Type.mat(cols, rows, Type.abstractFloat)], + Type.vec(cols, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts index 57cf0dbc4768..405de758bd76 100644 --- a/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_multiplication.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for non-matrix AbstractFloat multiplication expression +Execution Tests for non-matrix abstract-float multiplication expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_multiplication.cache.js'; @@ -26,8 +26,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('*'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts b/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts index 31a0991e02c4..d743f85ed653 100644 --- a/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_remainder.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix abstract float remainder expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_remainder.cache.js'; @@ -26,8 +26,8 @@ Accuracy: Derived from x - y * trunc(x/y) await run( t, abstractFloatBinary('%'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: Derived from x - y * trunc(x/y) await run( t, abstractFloatBinary('%'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('%'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('%'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts index 72004cfb1cee..2874a744da2e 100644 --- a/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/af_subtraction.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for non-matrix AbstractFloat subtraction expression +Execution Tests for non-matrix abstract-float subtraction expression `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_subtraction.cache.js'; @@ -26,8 +26,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -49,8 +49,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeVec(dim, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(dim, TypeAbstractFloat), + [Type.vec(dim, Type.abstractFloat), Type.abstractFloat], + Type.vec(dim, Type.abstractFloat), t.params, cases ); @@ -93,8 +93,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatBinary('-'), - [TypeAbstractFloat, TypeVec(dim, TypeAbstractFloat)], - TypeVec(dim, TypeAbstractFloat), + [Type.abstractFloat, Type.vec(dim, Type.abstractFloat)], + Type.vec(dim, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts index ae8b6ccf483b..ef211af3ed4b 100644 --- a/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/ai_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the abstract int arithmetic binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractInt, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './ai_arithmetic.cache.js'; @@ -29,8 +29,8 @@ Expression: x + y await run( t, abstractIntBinary('+'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -48,9 +48,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`addition_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('+'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('+'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('addition_vector_scalar') @@ -65,9 +65,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('+'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('+'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); g.test('division') @@ -87,8 +87,8 @@ Expression: x / y await run( t, abstractIntBinary('/'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -106,9 +106,9 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`division_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('/'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('/'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('division_vector_scalar') @@ -123,9 +123,9 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`division_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('/'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('/'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); g.test('multiplication') @@ -145,8 +145,8 @@ Expression: x * y await run( t, abstractIntBinary('*'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -164,9 +164,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`multiplication_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('*'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('*'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar') @@ -181,9 +181,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('*'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('*'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); g.test('remainder') @@ -203,8 +203,8 @@ Expression: x % y await run( t, abstractIntBinary('%'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -222,9 +222,9 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`remainder_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('%'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('%'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('remainder_vector_scalar') @@ -239,9 +239,9 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`remainder_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('%'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('%'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); g.test('subtraction') @@ -261,8 +261,8 @@ Expression: x - y await run( t, abstractIntBinary('-'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -280,9 +280,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`subtraction_scalar_vector${vec_size}`); - await run(t, abstractIntBinary('-'), [TypeAbstractInt, vec_type], vec_type, t.params, cases); + await run(t, abstractIntBinary('-'), [Type.abstractInt, vec_type], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar') @@ -297,7 +297,7 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeAbstractInt); + const vec_type = Type.vec(vec_size, Type.abstractInt); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, abstractIntBinary('-'), [vec_type, TypeAbstractInt], vec_type, t.params, cases); + await run(t, abstractIntBinary('-'), [vec_type, Type.abstractInt], vec_type, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/ai_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/ai_comparison.spec.ts index 9d00d0769c18..899e651054ef 100644 --- a/src/webgpu/shader/execution/expression/binary/ai_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/ai_comparison.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for the abstract int comparison expressions +Execution Tests for the abstract-int comparison expressions `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeAbstractInt, bool, abstractInt } from '../../../../util/conversion.js'; +import { bool, abstractInt, Type } from '../../../../util/conversion.js'; import { vectorI64Range } from '../../../../util/math.js'; import { Case } from '../case.js'; import { onlyConstInputSource, run } from '../expression.js'; @@ -35,7 +35,7 @@ Expression: x == y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] === v[1])); - await run(t, binary('=='), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('not_equals') @@ -52,7 +52,7 @@ Expression: x != y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] !== v[1])); - await run(t, binary('!='), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('less_than') @@ -69,7 +69,7 @@ Expression: x < y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] < v[1])); - await run(t, binary('<'), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('less_equals') @@ -86,7 +86,7 @@ Expression: x <= y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] <= v[1])); - await run(t, binary('<='), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('greater_than') @@ -103,7 +103,7 @@ Expression: x > y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] > v[1])); - await run(t, binary('>'), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -120,5 +120,5 @@ Expression: x >= y ) .fn(async t => { const cases = vectorI64Range(2).map(v => makeCase(v[0], v[1], v[0] >= v[1])); - await run(t, binary('>='), [TypeAbstractInt, TypeAbstractInt], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.abstractInt, Type.abstractInt], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts b/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts index ead7258635c2..71744b45959b 100644 --- a/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/bitwise.spec.ts @@ -9,7 +9,7 @@ import { abstractIntBits, i32, i32Bits, - Scalar, + ScalarValue, scalarType, u32, u32Bits, @@ -27,27 +27,27 @@ export const g = makeTestGroup(GPUTest); interface ScalarImpl { // builder is a mostly a wrapper around type builders like 'i32Bits' that // handles the (number|bigint) type check. - builder: (bits: bigint | number) => Scalar; + builder: (bits: bigint | number) => ScalarValue; size: 32 | 64; } const kScalarImpls = { i32: { - builder: (bits: bigint | number): Scalar => { + builder: (bits: bigint | number): ScalarValue => { assert(typeof bits === 'number'); return i32Bits(bits); }, size: 32, } as ScalarImpl, u32: { - builder: (bits: bigint | number): Scalar => { + builder: (bits: bigint | number): ScalarValue => { assert(typeof bits === 'number'); return u32Bits(bits); }, size: 32, } as ScalarImpl, 'abstract-int': { - builder: (bits: bigint | number): Scalar => { + builder: (bits: bigint | number): ScalarValue => { assert(typeof bits === 'bigint'); return abstractIntBits(bits); }, diff --git a/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts b/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts index cea6b86c602a..e2ed29d3c2ff 100644 --- a/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/bitwise_shift.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the bitwise shift binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { i32, scalarType, ScalarType, TypeU32, u32 } from '../../../../util/conversion.js'; +import { i32, scalarType, ScalarType, Type, u32 } from '../../../../util/conversion.js'; import { Case } from '../case.js'; import { allInputSources, run } from '../expression.js'; @@ -194,7 +194,7 @@ Shift left (shifted value is concrete) .fn(async t => { const type = scalarType(t.params.type); const cases = makeShiftLeftConcreteCases(t.params.type, t.params.inputSource, type); - await run(t, binary('<<'), [type, TypeU32], type, t.params, cases); + await run(t, binary('<<'), [type, Type.u32], type, t.params, cases); }); g.test('shift_left_concrete_compound') @@ -215,7 +215,7 @@ Shift left (shifted value is concrete) .fn(async t => { const type = scalarType(t.params.type); const cases = makeShiftLeftConcreteCases(t.params.type, t.params.inputSource, type); - await run(t, compoundBinary('<<='), [type, TypeU32], type, t.params, cases); + await run(t, compoundBinary('<<='), [type, Type.u32], type, t.params, cases); }); function makeShiftRightConcreteCases(inputType: string, inputSource: string, type: ScalarType) { @@ -319,7 +319,7 @@ Shift right (shifted value is concrete) .fn(async t => { const type = scalarType(t.params.type); const cases = makeShiftRightConcreteCases(t.params.type, t.params.inputSource, type); - await run(t, binary('>>'), [type, TypeU32], type, t.params, cases); + await run(t, binary('>>'), [type, Type.u32], type, t.params, cases); }); g.test('shift_right_concrete_compound') @@ -340,5 +340,5 @@ Shift right (shifted value is concrete) .fn(async t => { const type = scalarType(t.params.type); const cases = makeShiftRightConcreteCases(t.params.type, t.params.inputSource, type); - await run(t, compoundBinary('>>='), [type, TypeU32], type, t.params, cases); + await run(t, compoundBinary('>>='), [type, Type.u32], type, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts b/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts index e3aa448fe3c4..0e76f508240c 100644 --- a/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/bool_logical.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the boolean binary logical expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { bool, TypeBool } from '../../../../util/conversion.js'; +import { bool, Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -33,7 +33,7 @@ Logical "and". Component-wise when T is a vector. Evaluates both e1 and e2. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('&'), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('&'), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('and_compound') @@ -55,7 +55,7 @@ Logical "and". Component-wise when T is a vector. Evaluates both e1 and e2. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, compoundBinary('&='), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, compoundBinary('&='), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('and_short_circuit') @@ -75,7 +75,7 @@ short_circuiting "and". Yields true if both e1 and e2 are true; evaluates e2 onl { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('&&'), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('&&'), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('or') @@ -97,7 +97,7 @@ Logical "or". Component-wise when T is a vector. Evaluates both e1 and e2. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('|'), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('|'), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('or_compound') @@ -119,7 +119,7 @@ Logical "or". Component-wise when T is a vector. Evaluates both e1 and e2. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, compoundBinary('|='), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, compoundBinary('|='), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('or_short_circuit') @@ -139,7 +139,7 @@ short_circuiting "and". Yields true if both e1 and e2 are true; evaluates e2 onl { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('||'), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('||'), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('equals') @@ -161,7 +161,7 @@ Equality. Component-wise when T is a vector. { input: [bool(true), bool(true)], expected: bool(true) }, ]; - await run(t, binary('=='), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.bool, Type.bool], Type.bool, t.params, cases); }); g.test('not_equals') @@ -183,5 +183,5 @@ Equality. Component-wise when T is a vector. { input: [bool(true), bool(true)], expected: bool(false) }, ]; - await run(t, binary('!='), [TypeBool, TypeBool], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.bool, Type.bool], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts index 7f94c7e307a9..d9aa44bba0e2 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_addition.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('+'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('+'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('+'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('+'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('+='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('+='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('+='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts index 92d926a412b9..c0c0d4f8a4a6 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_comparison.cache.ts @@ -1,5 +1,5 @@ import { anyOf } from '../../../../util/compare.js'; -import { bool, f16, Scalar } from '../../../../util/conversion.js'; +import { bool, f16, ScalarValue } from '../../../../util/conversion.js'; import { flushSubnormalNumberF16, vectorF16Range } from '../../../../util/math.js'; import { Case } from '../case.js'; import { makeCaseCache } from '../case_cache.js'; @@ -11,7 +11,7 @@ import { makeCaseCache } from '../case_cache.js'; function makeCase( lhs: number, rhs: number, - truthFunc: (lhs: Scalar, rhs: Scalar) => boolean + truthFunc: (lhs: ScalarValue, rhs: ScalarValue) => boolean ): Case { // Subnormal float values may be flushed at any time. // https://www.w3.org/TR/WGSL/#floating-point-evaluation @@ -19,7 +19,7 @@ function makeCase( const f16_rhs = f16(rhs); const lhs_options = new Set([f16_lhs, f16(flushSubnormalNumberF16(lhs))]); const rhs_options = new Set([f16_rhs, f16(flushSubnormalNumberF16(rhs))]); - const expected: Array = []; + const expected: Array = []; lhs_options.forEach(l => { rhs_options.forEach(r => { const result = bool(truthFunc(l, r)); @@ -34,7 +34,7 @@ function makeCase( export const d = makeCaseCache('binary/f16_logical', { equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -43,7 +43,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -52,7 +52,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, not_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -61,7 +61,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, not_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -70,7 +70,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, less_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -79,7 +79,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, less_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -88,7 +88,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, less_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -97,7 +97,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, less_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -106,7 +106,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, greater_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -115,7 +115,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, greater_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -124,7 +124,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, greater_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; @@ -133,7 +133,7 @@ export const d = makeCaseCache('binary/f16_logical', { }); }, greater_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; diff --git a/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts index c84080983de5..b978cd3c99fe 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_comparison.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the f16 comparison operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF16 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; @@ -30,7 +30,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'equals_const' : 'equals_non_const' ); - await run(t, binary('=='), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('not_equals') @@ -51,7 +51,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'not_equals_const' : 'not_equals_non_const' ); - await run(t, binary('!='), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('less_than') @@ -72,7 +72,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'less_than_const' : 'less_than_non_const' ); - await run(t, binary('<'), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('less_equals') @@ -93,7 +93,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'less_equals_const' : 'less_equals_non_const' ); - await run(t, binary('<='), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('greater_than') @@ -114,7 +114,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'greater_than_const' : 'greater_than_non_const' ); - await run(t, binary('>'), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.f16, Type.f16], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -135,5 +135,5 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'greater_equals_const' : 'greater_equals_non_const' ); - await run(t, binary('>='), [TypeF16, TypeF16], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.f16, Type.f16], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts index 7dfc13f81a2f..8a155024db98 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_division.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 division expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('/'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('/'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('/'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('/'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('/='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('/='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('/'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('/='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('/'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts index daf768a5365c..7c34b0cadd87 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_addition.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix f16 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -38,8 +38,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeMat(cols, rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('+='), - [TypeMat(cols, rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts index 69c56c60c368..80ca78f7f5bd 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_matrix_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-matrix f16 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -44,8 +44,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(x_cols, x_rows, TypeF16), TypeMat(y_cols, y_rows, TypeF16)], - TypeMat(y_cols, x_rows, TypeF16), + [Type.mat(x_cols, x_rows, Type.f16), Type.mat(y_cols, y_rows, Type.f16)], + Type.mat(y_cols, x_rows, Type.f16), t.params, cases ); @@ -82,8 +82,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeMat(x_cols, x_rows, TypeF16), TypeMat(y_cols, y_rows, TypeF16)], - TypeMat(y_cols, x_rows, TypeF16), + [Type.mat(x_cols, x_rows, Type.f16), Type.mat(y_cols, y_rows, Type.f16)], + Type.mat(y_cols, x_rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts index 338d6d021bb5..aa7087738a5e 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_scalar_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-scalar and scalar-matrix f16 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -40,8 +40,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(cols, rows, TypeF16), TypeF16], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.f16], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -75,8 +75,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeMat(cols, rows, TypeF16), TypeF16], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.f16], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -110,8 +110,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeF16, TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.f16, Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts index 442c31ef82e9..e8e13d902a0e 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_subtraction.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix f16 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -38,8 +38,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeMat(cols, rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -71,8 +71,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('-='), - [TypeMat(cols, rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts index 9715fe681e35..557a7cead847 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_matrix_vector_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-vector and vector-matrix f16 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeMat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -40,8 +40,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(cols, rows, TypeF16), TypeVec(cols, TypeF16)], - TypeVec(rows, TypeF16), + [Type.mat(cols, rows, Type.f16), Type.vec(cols, Type.f16)], + Type.vec(rows, Type.f16), t.params, cases ); @@ -75,8 +75,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeVec(rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeVec(cols, TypeF16), + [Type.vec(rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.vec(cols, Type.f16), t.params, cases ); @@ -105,8 +105,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeVec(rows, TypeF16), TypeMat(cols, rows, TypeF16)], - TypeVec(cols, TypeF16), + [Type.vec(rows, Type.f16), Type.mat(cols, rows, Type.f16)], + Type.vec(cols, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts index 005a9a1e64af..81339d9266b5 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('*'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('*'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('*'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('*'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('*='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('*='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts index c2f5cd7af11f..0fe1cc53c662 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_remainder.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 remainder expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('%'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('%'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('%'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('%'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('%='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('%='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('%'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('%='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('%'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts index a458885ae8ce..6b29aad6ad78 100644 --- a/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f16_subtraction.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f16 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -28,7 +28,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('-'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('-'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector') @@ -47,7 +47,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('-'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, binary('-'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('scalar_compound') @@ -68,7 +68,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('-='), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, compoundBinary('-='), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('vector_scalar') @@ -91,8 +91,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -118,8 +118,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('-='), - [TypeVec(dim, TypeF16), TypeF16], - TypeVec(dim, TypeF16), + [Type.vec(dim, Type.f16), Type.f16], + Type.vec(dim, Type.f16), t.params, cases ); @@ -145,8 +145,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeF16, TypeVec(dim, TypeF16)], - TypeVec(dim, TypeF16), + [Type.f16, Type.vec(dim, Type.f16)], + Type.vec(dim, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts index 6bd8b0e7bdb7..9a502ae67719 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_addition.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('+'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('+'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('+'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('+'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('+='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('+='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('+='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts b/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts index 4c16fc55f0e5..28fb22e8202a 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_comparison.cache.ts @@ -1,5 +1,5 @@ import { anyOf } from '../../../../util/compare.js'; -import { bool, f32, Scalar } from '../../../../util/conversion.js'; +import { bool, f32, ScalarValue } from '../../../../util/conversion.js'; import { flushSubnormalNumberF32, vectorF32Range } from '../../../../util/math.js'; import { Case } from '../case.js'; import { makeCaseCache } from '../case_cache.js'; @@ -11,7 +11,7 @@ import { makeCaseCache } from '../case_cache.js'; function makeCase( lhs: number, rhs: number, - truthFunc: (lhs: Scalar, rhs: Scalar) => boolean + truthFunc: (lhs: ScalarValue, rhs: ScalarValue) => boolean ): Case { // Subnormal float values may be flushed at any time. // https://www.w3.org/TR/WGSL/#floating-point-evaluation @@ -19,7 +19,7 @@ function makeCase( const f32_rhs = f32(rhs); const lhs_options = new Set([f32_lhs, f32(flushSubnormalNumberF32(lhs))]); const rhs_options = new Set([f32_rhs, f32(flushSubnormalNumberF32(rhs))]); - const expected: Array = []; + const expected: Array = []; lhs_options.forEach(l => { rhs_options.forEach(r => { const result = bool(truthFunc(l, r)); @@ -34,7 +34,7 @@ function makeCase( export const d = makeCaseCache('binary/f32_logical', { equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -43,7 +43,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) === (rhs.value as number); }; @@ -52,7 +52,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, not_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -61,7 +61,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, not_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) !== (rhs.value as number); }; @@ -70,7 +70,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, less_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -79,7 +79,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, less_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) < (rhs.value as number); }; @@ -88,7 +88,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, less_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -97,7 +97,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, less_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) <= (rhs.value as number); }; @@ -106,7 +106,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, greater_than_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -115,7 +115,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, greater_than_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) > (rhs.value as number); }; @@ -124,7 +124,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, greater_equals_non_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; @@ -133,7 +133,7 @@ export const d = makeCaseCache('binary/f32_logical', { }); }, greater_equals_const: () => { - const truthFunc = (lhs: Scalar, rhs: Scalar): boolean => { + const truthFunc = (lhs: ScalarValue, rhs: ScalarValue): boolean => { return (lhs.value as number) >= (rhs.value as number); }; diff --git a/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts index e5833a0f9f33..42eb8934a440 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_comparison.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the f32 comparison operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; @@ -27,7 +27,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'equals_const' : 'equals_non_const' ); - await run(t, binary('=='), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('not_equals') @@ -45,7 +45,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'not_equals_const' : 'not_equals_non_const' ); - await run(t, binary('!='), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('less_than') @@ -63,7 +63,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'less_than_const' : 'less_than_non_const' ); - await run(t, binary('<'), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('less_equals') @@ -81,7 +81,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'less_equals_const' : 'less_equals_non_const' ); - await run(t, binary('<='), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('greater_than') @@ -99,7 +99,7 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'greater_than_const' : 'greater_than_non_const' ); - await run(t, binary('>'), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.f32, Type.f32], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -117,5 +117,5 @@ Accuracy: Correct result const cases = await d.get( t.params.inputSource === 'const' ? 'greater_equals_const' : 'greater_equals_non_const' ); - await run(t, binary('>='), [TypeF32, TypeF32], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.f32, Type.f32], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts index 0c8dd293f57d..bdd71e69eb52 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_division.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 division expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('/'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('/'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('/'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('/'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: 2.5 ULP for |y| in the range [2^-126, 2^126] const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('/='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('/='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('/'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('/='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('/'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts index 4fcb4d08927d..06a4ac47cc5b 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_addition.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix f32 addition expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -35,8 +35,8 @@ Accuracy: Correctly rounded await run( t, binary('+'), - [TypeMat(cols, rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -65,8 +65,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('+='), - [TypeMat(cols, rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts index ad34d642d26e..1ff7799f4625 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_matrix_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-matrix f32 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -41,8 +41,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(x_cols, x_rows, TypeF32), TypeMat(y_cols, y_rows, TypeF32)], - TypeMat(y_cols, x_rows, TypeF32), + [Type.mat(x_cols, x_rows, Type.f32), Type.mat(y_cols, y_rows, Type.f32)], + Type.mat(y_cols, x_rows, Type.f32), t.params, cases ); @@ -76,8 +76,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeMat(x_cols, x_rows, TypeF32), TypeMat(y_cols, y_rows, TypeF32)], - TypeMat(y_cols, x_rows, TypeF32), + [Type.mat(x_cols, x_rows, Type.f32), Type.mat(y_cols, y_rows, Type.f32)], + Type.mat(y_cols, x_rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts index 0558862d065d..e8771d19eb5d 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_scalar_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-scalar and scalar-matrix f32 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -37,8 +37,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(cols, rows, TypeF32), TypeF32], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.f32], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -69,8 +69,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeMat(cols, rows, TypeF32), TypeF32], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.f32], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -101,8 +101,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeF32, TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.f32, Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts index 8bcf9a47895c..31565ba598d5 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_subtraction.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix f32 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -35,8 +35,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeMat(cols, rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -65,8 +65,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('-='), - [TypeMat(cols, rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts index a04a28cfeff3..8cd7ed49fe0a 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_matrix_vector_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for matrix-vector and vector-matrix f32 multiplication expressio import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeMat, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -37,8 +37,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeMat(cols, rows, TypeF32), TypeVec(cols, TypeF32)], - TypeVec(rows, TypeF32), + [Type.mat(cols, rows, Type.f32), Type.vec(cols, Type.f32)], + Type.vec(rows, Type.f32), t.params, cases ); @@ -69,8 +69,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeVec(rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeVec(cols, TypeF32), + [Type.vec(rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.vec(cols, Type.f32), t.params, cases ); @@ -96,8 +96,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeVec(rows, TypeF32), TypeMat(cols, rows, TypeF32)], - TypeVec(cols, TypeF32), + [Type.vec(rows, Type.f32), Type.mat(cols, rows, Type.f32)], + Type.vec(cols, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts index 77e670795d2d..478ca71ef0fc 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_multiplication.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 multiplication expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('*'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('*'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('*'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('*'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('*='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('*='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('*='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('*'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts index b61d3ecc633a..3a9acb02e095 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_remainder.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 remainder expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('%'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('%'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('%'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('%'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: Derived from x - y * trunc(x/y) const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('%='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('%='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('%'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('%='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('%'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts b/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts index e873c6ebc3b1..55097390e935 100644 --- a/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/f32_subtraction.spec.ts @@ -4,7 +4,7 @@ Execution Tests for non-matrix f32 subtraction expression import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -25,7 +25,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, binary('-'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('-'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector') @@ -41,7 +41,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' // Using vectorize to generate vector cases based on scalar cases ); - await run(t, binary('-'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, binary('-'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('scalar_compound') @@ -59,7 +59,7 @@ Accuracy: Correctly rounded const cases = await d.get( t.params.inputSource === 'const' ? 'scalar_const' : 'scalar_non_const' ); - await run(t, compoundBinary('-='), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, compoundBinary('-='), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('vector_scalar') @@ -79,8 +79,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -103,8 +103,8 @@ Accuracy: Correctly rounded await run( t, compoundBinary('-='), - [TypeVec(dim, TypeF32), TypeF32], - TypeVec(dim, TypeF32), + [Type.vec(dim, Type.f32), Type.f32], + Type.vec(dim, Type.f32), t.params, cases ); @@ -127,8 +127,8 @@ Accuracy: Correctly rounded await run( t, binary('-'), - [TypeF32, TypeVec(dim, TypeF32)], - TypeVec(dim, TypeF32), + [Type.f32, Type.vec(dim, Type.f32)], + Type.vec(dim, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts index ef201f66689f..ef8ea0044782 100644 --- a/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/i32_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 arithmetic binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeI32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -24,7 +24,7 @@ Expression: x + y ) .fn(async t => { const cases = await d.get('addition'); - await run(t, binary('+'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('+'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('addition_compound') @@ -39,7 +39,7 @@ Expression: x += y ) .fn(async t => { const cases = await d.get('addition'); - await run(t, compoundBinary('+='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('+='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('subtraction') @@ -54,7 +54,7 @@ Expression: x - y ) .fn(async t => { const cases = await d.get('subtraction'); - await run(t, binary('-'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('-'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('subtraction_compound') @@ -69,7 +69,7 @@ Expression: x -= y ) .fn(async t => { const cases = await d.get('subtraction'); - await run(t, compoundBinary('-='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('-='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('multiplication') @@ -84,7 +84,7 @@ Expression: x * y ) .fn(async t => { const cases = await d.get('multiplication'); - await run(t, binary('*'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('*'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('multiplication_compound') @@ -99,7 +99,7 @@ Expression: x *= y ) .fn(async t => { const cases = await d.get('multiplication'); - await run(t, compoundBinary('*='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('*='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('division') @@ -116,7 +116,7 @@ Expression: x / y const cases = await d.get( t.params.inputSource === 'const' ? 'division_const' : 'division_non_const' ); - await run(t, binary('/'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('/'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('division_compound') @@ -133,7 +133,7 @@ Expression: x /= y const cases = await d.get( t.params.inputSource === 'const' ? 'division_const' : 'division_non_const' ); - await run(t, compoundBinary('/='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('/='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('remainder') @@ -150,7 +150,7 @@ Expression: x % y const cases = await d.get( t.params.inputSource === 'const' ? 'remainder_const' : 'remainder_non_const' ); - await run(t, binary('%'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, binary('%'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('remainder_compound') @@ -167,7 +167,7 @@ Expression: x %= y const cases = await d.get( t.params.inputSource === 'const' ? 'remainder_const' : 'remainder_non_const' ); - await run(t, compoundBinary('%='), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, compoundBinary('%='), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('addition_scalar_vector') @@ -182,9 +182,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`addition_scalar_vector${vec_size}`); - await run(t, binary('+'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('+'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('addition_vector_scalar') @@ -199,9 +199,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, binary('+'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('+'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('addition_vector_scalar_compound') @@ -216,9 +216,9 @@ Expression: x += y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, compoundBinary('+='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('+='), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('subtraction_scalar_vector') @@ -233,9 +233,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`subtraction_scalar_vector${vec_size}`); - await run(t, binary('-'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('-'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar') @@ -250,9 +250,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, binary('-'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('-'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar_compound') @@ -267,9 +267,9 @@ Expression: x -= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, compoundBinary('-='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('-='), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('multiplication_scalar_vector') @@ -284,9 +284,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`multiplication_scalar_vector${vec_size}`); - await run(t, binary('*'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('*'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar') @@ -301,9 +301,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, binary('*'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('*'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar_compound') @@ -318,9 +318,9 @@ Expression: x *= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, compoundBinary('*='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('*='), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('division_scalar_vector') @@ -335,10 +335,10 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_scalar_vector${vec_size}_${source}`); - await run(t, binary('/'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('/'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('division_vector_scalar') @@ -353,10 +353,10 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_vector${vec_size}_scalar_${source}`); - await run(t, binary('/'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('/'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('division_vector_scalar_compound') @@ -371,10 +371,10 @@ Expression: x /= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_vector${vec_size}_scalar_${source}`); - await run(t, compoundBinary('/='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('/='), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('remainder_scalar_vector') @@ -389,10 +389,10 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_scalar_vector${vec_size}_${source}`); - await run(t, binary('%'), [TypeI32, vec_type], vec_type, t.params, cases); + await run(t, binary('%'), [Type.i32, vec_type], vec_type, t.params, cases); }); g.test('remainder_vector_scalar') @@ -407,10 +407,10 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_vector${vec_size}_scalar_${source}`); - await run(t, binary('%'), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, binary('%'), [vec_type, Type.i32], vec_type, t.params, cases); }); g.test('remainder_vector_scalar_compound') @@ -425,8 +425,8 @@ Expression: x %= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeI32); + const vec_type = Type.vec(vec_size, Type.i32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_vector${vec_size}_scalar_${source}`); - await run(t, compoundBinary('%='), [vec_type, TypeI32], vec_type, t.params, cases); + await run(t, compoundBinary('%='), [vec_type, Type.i32], vec_type, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts index c07d09a058ed..9b6566c9a4a8 100644 --- a/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/i32_comparison.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 comparison expressions import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeI32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; @@ -24,7 +24,7 @@ Expression: x == y ) .fn(async t => { const cases = await d.get('equals'); - await run(t, binary('=='), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('not_equals') @@ -39,7 +39,7 @@ Expression: x != y ) .fn(async t => { const cases = await d.get('not_equals'); - await run(t, binary('!='), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('less_than') @@ -54,7 +54,7 @@ Expression: x < y ) .fn(async t => { const cases = await d.get('less_than'); - await run(t, binary('<'), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('less_equals') @@ -69,7 +69,7 @@ Expression: x <= y ) .fn(async t => { const cases = await d.get('less_equal'); - await run(t, binary('<='), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('greater_than') @@ -84,7 +84,7 @@ Expression: x > y ) .fn(async t => { const cases = await d.get('greater_than'); - await run(t, binary('>'), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.i32, Type.i32], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -99,5 +99,5 @@ Expression: x >= y ) .fn(async t => { const cases = await d.get('greater_equal'); - await run(t, binary('>='), [TypeI32, TypeI32], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.i32, Type.i32], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts index 28c25fef8dc9..6df16f303c7f 100644 --- a/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/u32_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the u32 arithmetic binary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeU32, TypeVec } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary, compoundBinary } from './binary.js'; @@ -24,7 +24,7 @@ Expression: x + y ) .fn(async t => { const cases = await d.get('addition'); - await run(t, binary('+'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('+'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('addition_compound') @@ -39,7 +39,7 @@ Expression: x += y ) .fn(async t => { const cases = await d.get('addition'); - await run(t, compoundBinary('+='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('+='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('subtraction') @@ -54,7 +54,7 @@ Expression: x - y ) .fn(async t => { const cases = await d.get('subtraction'); - await run(t, binary('-'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('-'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('subtraction_compound') @@ -69,7 +69,7 @@ Expression: x -= y ) .fn(async t => { const cases = await d.get('subtraction'); - await run(t, compoundBinary('-='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('-='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('multiplication') @@ -84,7 +84,7 @@ Expression: x * y ) .fn(async t => { const cases = await d.get('multiplication'); - await run(t, binary('*'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('*'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('multiplication_compound') @@ -99,7 +99,7 @@ Expression: x *= y ) .fn(async t => { const cases = await d.get('multiplication'); - await run(t, compoundBinary('*='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('*='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('division') @@ -116,7 +116,7 @@ Expression: x / y const cases = await d.get( t.params.inputSource === 'const' ? 'division_const' : 'division_non_const' ); - await run(t, binary('/'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('/'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('division_compound') @@ -133,7 +133,7 @@ Expression: x /= y const cases = await d.get( t.params.inputSource === 'const' ? 'division_const' : 'division_non_const' ); - await run(t, compoundBinary('/='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('/='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('remainder') @@ -150,7 +150,7 @@ Expression: x % y const cases = await d.get( t.params.inputSource === 'const' ? 'remainder_const' : 'remainder_non_const' ); - await run(t, binary('%'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, binary('%'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('remainder_compound') @@ -167,7 +167,7 @@ Expression: x %= y const cases = await d.get( t.params.inputSource === 'const' ? 'remainder_const' : 'remainder_non_const' ); - await run(t, compoundBinary('%='), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, compoundBinary('%='), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('addition_scalar_vector') @@ -182,9 +182,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`addition_scalar_vector${vec_size}`); - await run(t, binary('+'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('+'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('addition_vector_scalar') @@ -199,9 +199,9 @@ Expression: x + y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, binary('+'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('+'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('addition_vector_scalar_compound') @@ -216,9 +216,9 @@ Expression: x += y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`addition_vector${vec_size}_scalar`); - await run(t, compoundBinary('+='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('+='), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('subtraction_scalar_vector') @@ -233,9 +233,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`subtraction_scalar_vector${vec_size}`); - await run(t, binary('-'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('-'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar') @@ -250,9 +250,9 @@ Expression: x - y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, binary('-'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('-'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('subtraction_vector_scalar_compound') @@ -267,9 +267,9 @@ Expression: x -= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`subtraction_vector${vec_size}_scalar`); - await run(t, compoundBinary('-='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('-='), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('multiplication_scalar_vector') @@ -284,9 +284,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`multiplication_scalar_vector${vec_size}`); - await run(t, binary('*'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('*'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar') @@ -301,9 +301,9 @@ Expression: x * y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, binary('*'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('*'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('multiplication_vector_scalar_compound') @@ -318,9 +318,9 @@ Expression: x *= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const cases = await d.get(`multiplication_vector${vec_size}_scalar`); - await run(t, compoundBinary('*='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('*='), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('division_scalar_vector') @@ -335,10 +335,10 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_scalar_vector${vec_size}_${source}`); - await run(t, binary('/'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('/'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('division_vector_scalar') @@ -353,10 +353,10 @@ Expression: x / y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_vector${vec_size}_scalar_${source}`); - await run(t, binary('/'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('/'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('division_vector_scalar_compound') @@ -371,10 +371,10 @@ Expression: x /= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`division_vector${vec_size}_scalar_${source}`); - await run(t, compoundBinary('/='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('/='), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('remainder_scalar_vector') @@ -389,10 +389,10 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_rhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_scalar_vector${vec_size}_${source}`); - await run(t, binary('%'), [TypeU32, vec_type], vec_type, t.params, cases); + await run(t, binary('%'), [Type.u32, vec_type], vec_type, t.params, cases); }); g.test('remainder_vector_scalar') @@ -407,10 +407,10 @@ Expression: x % y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_vector${vec_size}_scalar_${source}`); - await run(t, binary('%'), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, binary('%'), [vec_type, Type.u32], vec_type, t.params, cases); }); g.test('remainder_vector_scalar_compound') @@ -425,8 +425,8 @@ Expression: x %= y ) .fn(async t => { const vec_size = t.params.vectorize_lhs; - const vec_type = TypeVec(vec_size, TypeU32); + const vec_type = Type.vec(vec_size, Type.u32); const source = t.params.inputSource === 'const' ? 'const' : 'non_const'; const cases = await d.get(`remainder_vector${vec_size}_scalar_${source}`); - await run(t, compoundBinary('%='), [vec_type, TypeU32], vec_type, t.params, cases); + await run(t, compoundBinary('%='), [vec_type, Type.u32], vec_type, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts b/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts index fbf34b510394..5bb6767b01e0 100644 --- a/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts +++ b/src/webgpu/shader/execution/expression/binary/u32_comparison.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the u32 comparison expressions import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { binary } from './binary.js'; @@ -24,7 +24,7 @@ Expression: x == y ) .fn(async t => { const cases = await d.get('equals'); - await run(t, binary('=='), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('=='), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('not_equals') @@ -39,7 +39,7 @@ Expression: x != y ) .fn(async t => { const cases = await d.get('not_equals'); - await run(t, binary('!='), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('!='), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('less_than') @@ -54,7 +54,7 @@ Expression: x < y ) .fn(async t => { const cases = await d.get('less_than'); - await run(t, binary('<'), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('<'), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('less_equals') @@ -69,7 +69,7 @@ Expression: x <= y ) .fn(async t => { const cases = await d.get('less_equal'); - await run(t, binary('<='), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('<='), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('greater_than') @@ -84,7 +84,7 @@ Expression: x > y ) .fn(async t => { const cases = await d.get('greater_than'); - await run(t, binary('>'), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('>'), [Type.u32, Type.u32], Type.bool, t.params, cases); }); g.test('greater_equals') @@ -99,5 +99,5 @@ Expression: x >= y ) .fn(async t => { const cases = await d.get('greater_equal'); - await run(t, binary('>='), [TypeU32, TypeU32], TypeBool, t.params, cases); + await run(t, binary('>='), [Type.u32, Type.u32], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts index c7f6d1d57cae..75d41ab16377 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts @@ -1,14 +1,14 @@ export const description = ` Execution tests for the 'abs' builtin function -S is AbstractInt, i32, or u32 +S is abstract-int, i32, or u32 T is S or vecN @const fn abs(e: T ) -> T The absolute value of e. Component-wise when T is a vector. If e is a signed integral scalar type and evaluates to the largest negative value, then the result is e. If e is an unsigned integral type, then the result is e. -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn abs(e: T ) -> T Returns the absolute value of e (e.g. e with a positive sign bit). @@ -18,16 +18,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { kBit } from '../../../../../util/constants.js'; -import { - i32Bits, - TypeF32, - TypeF16, - TypeI32, - TypeU32, - u32Bits, - TypeAbstractFloat, - TypeAbstractInt, -} from '../../../../../util/conversion.js'; +import { Type, i32Bits, u32Bits } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { d } from './abs.cache.js'; @@ -45,7 +36,7 @@ g.test('abstract_int') ) .fn(async t => { const cases = await d.get('abstract_int'); - await run(t, abstractIntBuiltin('abs'), [TypeAbstractInt], TypeAbstractInt, t.params, cases); + await run(t, abstractIntBuiltin('abs'), [Type.abstractInt], Type.abstractInt, t.params, cases); }); g.test('u32') @@ -55,7 +46,7 @@ g.test('u32') u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) ) .fn(async t => { - await run(t, builtin('abs'), [TypeU32], TypeU32, t.params, [ + await run(t, builtin('abs'), [Type.u32], Type.u32, t.params, [ // Min and Max u32 { input: u32Bits(kBit.u32.min), expected: u32Bits(kBit.u32.min) }, { input: u32Bits(kBit.u32.max), expected: u32Bits(kBit.u32.max) }, @@ -102,7 +93,7 @@ g.test('i32') u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const) ) .fn(async t => { - await run(t, builtin('abs'), [TypeI32], TypeI32, t.params, [ + await run(t, builtin('abs'), [Type.i32], Type.i32, t.params, [ // Min and max i32 // If e evaluates to the largest negative value, then the result is e. { input: i32Bits(kBit.i32.negative.min), expected: i32Bits(kBit.i32.negative.min) }, @@ -158,8 +149,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('abs'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -173,7 +164,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('abs'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('abs'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -187,5 +178,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('abs'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('abs'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts index 1d489e1191b8..7d6b224579a9 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'acos' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn acos(e: T ) -> T Returns the arc cosine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the arc cosine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeF16 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './acos.cache.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('acos'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('acos'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('acos'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('acos'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts index 737bd5677241..ed34d326de1a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/acosh.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'acosh' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn acosh(e: T ) -> T Returns the hyperbolic arc cosine of e. The result is 0 when e < 1. @@ -13,7 +13,7 @@ Note: The result is not mathematically meaningful when e < 1. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './acosh.cache.js'; @@ -37,7 +37,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('acosh'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('acosh'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -51,5 +51,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('acosh'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('acosh'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts index 9a2938c1d500..74e072703d94 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/all.spec.ts @@ -10,15 +10,7 @@ Returns true if each component of e is true if e is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - False, - True, - TypeBool, - TypeVec, - vec2, - vec3, - vec4, -} from '../../../../../util/conversion.js'; +import { False, True, Type, vec2, vec3, vec4 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -36,14 +28,14 @@ g.test('bool') .fn(async t => { const overloads = { scalar: { - type: TypeBool, + type: Type.bool, cases: [ { input: False, expected: False }, { input: True, expected: True }, ], }, vec2: { - type: TypeVec(2, TypeBool), + type: Type.vec(2, Type.bool), cases: [ { input: vec2(False, False), expected: False }, { input: vec2(True, False), expected: False }, @@ -52,7 +44,7 @@ g.test('bool') ], }, vec3: { - type: TypeVec(3, TypeBool), + type: Type.vec(3, Type.bool), cases: [ { input: vec3(False, False, False), expected: False }, { input: vec3(True, False, False), expected: False }, @@ -65,7 +57,7 @@ g.test('bool') ], }, vec4: { - type: TypeVec(4, TypeBool), + type: Type.vec(4, Type.bool), cases: [ { input: vec4(False, False, False, False), expected: False }, { input: vec4(False, True, False, False), expected: False }, @@ -88,5 +80,5 @@ g.test('bool') }; const overload = overloads[t.params.overload]; - await run(t, builtin('all'), [overload.type], TypeBool, t.params, overload.cases); + await run(t, builtin('all'), [overload.type], Type.bool, t.params, overload.cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts index 19ed7d186f7b..43c599e2aa4b 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/any.spec.ts @@ -10,15 +10,7 @@ Returns true if any component of e is true if e is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - False, - True, - TypeBool, - TypeVec, - vec2, - vec3, - vec4, -} from '../../../../../util/conversion.js'; +import { False, True, Type, vec2, vec3, vec4 } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -36,14 +28,14 @@ g.test('bool') .fn(async t => { const overloads = { scalar: { - type: TypeBool, + type: Type.bool, cases: [ { input: False, expected: False }, { input: True, expected: True }, ], }, vec2: { - type: TypeVec(2, TypeBool), + type: Type.vec(2, Type.bool), cases: [ { input: vec2(False, False), expected: False }, { input: vec2(True, False), expected: True }, @@ -52,7 +44,7 @@ g.test('bool') ], }, vec3: { - type: TypeVec(3, TypeBool), + type: Type.vec(3, Type.bool), cases: [ { input: vec3(False, False, False), expected: False }, { input: vec3(True, False, False), expected: True }, @@ -65,7 +57,7 @@ g.test('bool') ], }, vec4: { - type: TypeVec(4, TypeBool), + type: Type.vec(4, Type.bool), cases: [ { input: vec4(False, False, False, False), expected: False }, { input: vec4(False, True, False, False), expected: True }, @@ -88,5 +80,5 @@ g.test('bool') }; const overload = overloads[t.params.overload]; - await run(t, builtin('any'), [overload.type], TypeBool, t.params, overload.cases); + await run(t, builtin('any'), [overload.type], Type.bool, t.params, overload.cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts index 8a7934936030..5ff882d28a34 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/asin.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'asin' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn asin(e: T ) -> T Returns the arc sine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the arc sine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './asin.cache.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('asin'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('asin'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('asin'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('asin'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts index b5f362066b85..21f2584cac98 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/asinh.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'sinh' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn asinh(e: T ) -> T Returns the hyperbolic arc sine of e. @@ -12,7 +12,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './asinh.cache.js'; @@ -36,7 +36,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('asinh'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('asinh'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -50,5 +50,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('asinh'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('asinh'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts index 173fb0a0010e..355529eb8dfe 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'atan' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn atan(e: T ) -> T Returns the arc tangent of e. Component-wise when T is a vector. @@ -10,7 +10,7 @@ Returns the arc tangent of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './atan.cache.js'; @@ -40,7 +40,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1] ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('atan'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('atan'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -54,5 +54,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('atan'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('atan'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts index dff1d442f56b..707820c2abdb 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'atan2' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn atan2(e1: T ,e2: T ) -> T Returns the arc tangent of e1 over e2. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the arc tangent of e1 over e2. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './atan2.cache.js'; @@ -39,7 +39,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1] ) .fn(async t => { const cases = await d.get(`f32_${t.params.inputSource === 'const' ? 'const' : 'non_const'}`); - await run(t, builtin('atan2'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('atan2'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -53,5 +53,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(`f16_${t.params.inputSource === 'const' ? 'const' : 'non_const'}`); - await run(t, builtin('atan2'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('atan2'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts index 31c14110b662..fe43c2161336 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/atanh.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'atanh' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn atanh(e: T ) -> T Returns the hyperbolic arc tangent of e. The result is 0 when abs(e) ≥ 1. @@ -12,7 +12,7 @@ Note: The result is not mathematically meaningful when abs(e) >= 1. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { d } from './atanh.cache.js'; @@ -36,7 +36,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('atanh'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('atanh'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -50,5 +50,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('atanh'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('atanh'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts index a3f2ee35b29b..b64d3692121e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/bitcast.cache.ts @@ -2,8 +2,8 @@ import { assert } from '../../../../../../common/util/util.js'; import { Comparator, alwaysPass, anyOf } from '../../../../../util/compare.js'; import { kBit, kValue } from '../../../../../util/constants.js'; import { - Scalar, - Vector, + ScalarValue, + VectorValue, f16, f32, i32, @@ -63,7 +63,7 @@ const f32ZerosInterval: FPInterval = new FPInterval('f32', -0.0, 0.0); const f32FiniteRange: number[] = [...scalarF32Range(), kValue.f32.negative.zero]; const f32RangeWithInfAndNaN: number[] = [...f32FiniteRange, ...f32InfAndNaNInF32]; -// F16 values, finite, Inf/NaN, and zeros. Represented in float and u16. +// Type.f16 values, finite, Inf/NaN, and zeros. Represented in float and u16. const f16FiniteInF16: number[] = [...scalarF16Range(), kValue.f16.negative.zero]; const f16FiniteInU16: number[] = f16FiniteInF16.map(u => reinterpretF16AsU16(u)); @@ -118,7 +118,7 @@ function u32ToU16x2(u32: number): number[] { /** * @returns a vec2 from an array of two u16, each reinterpreted as f16. */ -function u16x2ToVec2F16(u16x2: number[]): Vector { +function u16x2ToVec2F16(u16x2: number[]): VectorValue { assert(u16x2.length === 2); return toVector(u16x2.map(reinterpretU16AsF16), f16); } @@ -126,7 +126,7 @@ function u16x2ToVec2F16(u16x2: number[]): Vector { /** * @returns a vec4 from an array of four u16, each reinterpreted as f16. */ -function u16x4ToVec4F16(u16x4: number[]): Vector { +function u16x4ToVec4F16(u16x4: number[]): VectorValue { assert(u16x4.length === 4); return toVector(u16x4.map(reinterpretU16AsF16), f16); } @@ -433,7 +433,7 @@ function bitcastVec2F32ToVec4F16Comparator(f32x2: number[]): Comparator { interface ExpectionFor32BitsScalarFromF16x2 { // possibleExpectations is Scalar array if the expectation is for i32/u32 and FPInterval array for // f32. Note that if the expectation for i32/u32 is unbound, possibleExpectations is meaningless. - possibleExpectations: (Scalar | FPInterval)[]; + possibleExpectations: (ScalarValue | FPInterval)[]; isUnbounded: boolean; } @@ -458,8 +458,8 @@ function possible32BitScalarIntervalsFromF16x2( ): ExpectionFor32BitsScalarFromF16x2 { assert(f16x2InU16x2.length === 2); let reinterpretFromU32: (x: number) => number; - let expectationsForValue: (x: number) => Scalar[] | FPInterval[]; - let unboundedExpectations: FPInterval[] | Scalar[]; + let expectationsForValue: (x: number) => ScalarValue[] | FPInterval[]; + let unboundedExpectations: FPInterval[] | ScalarValue[]; if (type === 'u32') { reinterpretFromU32 = (x: number) => x; expectationsForValue = x => [u32(x)]; @@ -498,12 +498,12 @@ function possible32BitScalarIntervalsFromF16x2( return { possibleExpectations: unboundedExpectations, isUnbounded: true }; } const possibleU16Bits = f16x2InU16x2.map(possibleBitsInU16FromFiniteF16InU16); - const possibleExpectations = cartesianProduct(...possibleU16Bits).flatMap( - (possibleBitsU16x2: readonly number[]) => { - assert(possibleBitsU16x2.length === 2); - return expectationsForValue(reinterpretFromU32(u16x2ToU32(possibleBitsU16x2))); - } - ); + const possibleExpectations = cartesianProduct(...possibleU16Bits).flatMap< + ScalarValue | FPInterval + >((possibleBitsU16x2: readonly number[]) => { + assert(possibleBitsU16x2.length === 2); + return expectationsForValue(reinterpretFromU32(u16x2ToU32(possibleBitsU16x2))); + }); return { possibleExpectations, isUnbounded: false }; } @@ -566,7 +566,7 @@ function bitcastVec4F16ToVec2U32Comparator(vec4F16InU16x4: number[]): Comparator } return anyOf( ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map( - e => new Vector(e as Scalar[]) + e => new VectorValue(e as ScalarValue[]) ) ); } @@ -588,7 +588,7 @@ function bitcastVec4F16ToVec2I32Comparator(vec4F16InU16x4: number[]): Comparator } return anyOf( ...cartesianProduct(...expectationsPerElement.map(e => e.possibleExpectations)).map( - e => new Vector(e as Scalar[]) + e => new VectorValue(e as ScalarValue[]) ) ); } diff --git a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts index 4c1a8aa618e5..0ead5af05a5b 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/bitcast.spec.ts @@ -11,8 +11,8 @@ S is i32, u32, f32 T is i32, u32, f32, and T is not S Reinterpretation of bits. Beware non-normal f32 values. -@const @must_use fn bitcast(e : AbstractInt) -> T -@const @must_use fn bitcast>(e : vecN) -> T +@const @must_use fn bitcast(e : Type.abstractInt) -> T +@const @must_use fn bitcast>(e : vecN) -> T @const @must_use fn bitcast(e: vec2 ) -> T @const @must_use fn bitcast>(e: vec4 ) -> vec2 @@ -26,18 +26,13 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { GPUTest } from '../../../../../gpu_test.js'; import { anyOf } from '../../../../../util/compare.js'; import { - TypeF16, - TypeF32, - TypeI32, - TypeU32, - TypeVec, - TypeAbstractFloat, f32, u32, i32, abstractFloat, uint32ToFloat32, u32Bits, + Type, } from '../../../../../util/conversion.js'; import { FP } from '../../../../../util/floating_point.js'; import { scalarF32Range } from '../../../../../util/math.js'; @@ -76,7 +71,7 @@ g.test('i32_to_i32') ) .fn(async t => { const cases = await d.get('i32_to_i32'); - await run(t, bitcastBuilder('i32', t.params), [TypeI32], TypeI32, t.params, cases); + await run(t, bitcastBuilder('i32', t.params), [Type.i32], Type.i32, t.params, cases); }); g.test('u32_to_u32') @@ -90,7 +85,7 @@ g.test('u32_to_u32') ) .fn(async t => { const cases = await d.get('u32_to_u32'); - await run(t, bitcastBuilder('u32', t.params), [TypeU32], TypeU32, t.params, cases); + await run(t, bitcastBuilder('u32', t.params), [Type.u32], Type.u32, t.params, cases); }); g.test('f32_to_f32') @@ -107,7 +102,7 @@ g.test('f32_to_f32') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'f32_to_f32' : 'f32_inf_nan_to_f32' ); - await run(t, bitcastBuilder('f32', t.params), [TypeF32], TypeF32, t.params, cases); + await run(t, bitcastBuilder('f32', t.params), [Type.f32], Type.f32, t.params, cases); }); // To i32 from u32, f32 @@ -122,7 +117,7 @@ g.test('u32_to_i32') ) .fn(async t => { const cases = await d.get('u32_to_i32'); - await run(t, bitcastBuilder('i32', t.params), [TypeU32], TypeI32, t.params, cases); + await run(t, bitcastBuilder('i32', t.params), [Type.u32], Type.i32, t.params, cases); }); g.test('f32_to_i32') @@ -139,7 +134,7 @@ g.test('f32_to_i32') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'f32_to_i32' : 'f32_inf_nan_to_i32' ); - await run(t, bitcastBuilder('i32', t.params), [TypeF32], TypeI32, t.params, cases); + await run(t, bitcastBuilder('i32', t.params), [Type.f32], Type.i32, t.params, cases); }); // To u32 from i32, f32 @@ -154,7 +149,7 @@ g.test('i32_to_u32') ) .fn(async t => { const cases = await d.get('i32_to_u32'); - await run(t, bitcastBuilder('u32', t.params), [TypeI32], TypeU32, t.params, cases); + await run(t, bitcastBuilder('u32', t.params), [Type.i32], Type.u32, t.params, cases); }); g.test('f32_to_u32') @@ -171,7 +166,7 @@ g.test('f32_to_u32') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'f32_to_u32' : 'f32_inf_nan_to_u32' ); - await run(t, bitcastBuilder('u32', t.params), [TypeF32], TypeU32, t.params, cases); + await run(t, bitcastBuilder('u32', t.params), [Type.f32], Type.u32, t.params, cases); }); // To f32 from i32, u32 @@ -189,7 +184,7 @@ g.test('i32_to_f32') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'i32_to_f32' : 'i32_to_f32_inf_nan' ); - await run(t, bitcastBuilder('f32', t.params), [TypeI32], TypeF32, t.params, cases); + await run(t, bitcastBuilder('f32', t.params), [Type.i32], Type.f32, t.params, cases); }); g.test('u32_to_f32') @@ -206,7 +201,7 @@ g.test('u32_to_f32') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'u32_to_f32' : 'u32_to_f32_inf_nan' ); - await run(t, bitcastBuilder('f32', t.params), [TypeU32], TypeF32, t.params, cases); + await run(t, bitcastBuilder('f32', t.params), [Type.u32], Type.f32, t.params, cases); }); // 16 bit types @@ -231,7 +226,7 @@ g.test('f16_to_f16') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'f16_to_f16' : 'f16_inf_nan_to_f16' ); - await run(t, bitcastBuilder('f16', t.params), [TypeF16], TypeF16, t.params, cases); + await run(t, bitcastBuilder('f16', t.params), [Type.f16], Type.f16, t.params, cases); }); // f16: 32-bit scalar numeric to vec2 @@ -247,14 +242,7 @@ g.test('i32_to_vec2h') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'i32_to_vec2_f16' : 'i32_to_vec2_f16_inf_nan' ); - await run( - t, - bitcastBuilder('vec2', t.params), - [TypeI32], - TypeVec(2, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.i32], Type.vec2h, t.params, cases); }); g.test('u32_to_vec2h') @@ -269,14 +257,7 @@ g.test('u32_to_vec2h') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'u32_to_vec2_f16' : 'u32_to_vec2_f16_inf_nan' ); - await run( - t, - bitcastBuilder('vec2', t.params), - [TypeU32], - TypeVec(2, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.u32], Type.vec2h, t.params, cases); }); g.test('f32_to_vec2h') @@ -291,14 +272,7 @@ g.test('f32_to_vec2h') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'f32_to_vec2_f16' : 'f32_inf_nan_to_vec2_f16_inf_nan' ); - await run( - t, - bitcastBuilder('vec2', t.params), - [TypeF32], - TypeVec(2, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.f32], Type.vec2h, t.params, cases); }); // f16: vec2<32-bit scalar numeric> to vec4 @@ -314,14 +288,7 @@ g.test('vec2i_to_vec4h') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'vec2_i32_to_vec4_f16' : 'vec2_i32_to_vec4_f16_inf_nan' ); - await run( - t, - bitcastBuilder('vec4', t.params), - [TypeVec(2, TypeI32)], - TypeVec(4, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec4', t.params), [Type.vec2i], Type.vec4h, t.params, cases); }); g.test('vec2u_to_vec4h') @@ -336,14 +303,7 @@ g.test('vec2u_to_vec4h') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'vec2_u32_to_vec4_f16' : 'vec2_u32_to_vec4_f16_inf_nan' ); - await run( - t, - bitcastBuilder('vec4', t.params), - [TypeVec(2, TypeU32)], - TypeVec(4, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec4', t.params), [Type.vec2u], Type.vec4h, t.params, cases); }); g.test('vec2f_to_vec4h') @@ -360,14 +320,7 @@ g.test('vec2f_to_vec4h') ? 'vec2_f32_to_vec4_f16' : 'vec2_f32_inf_nan_to_vec4_f16_inf_nan' ); - await run( - t, - bitcastBuilder('vec4', t.params), - [TypeVec(2, TypeF32)], - TypeVec(4, TypeF16), - t.params, - cases - ); + await run(t, bitcastBuilder('vec4', t.params), [Type.vec2f], Type.vec4h, t.params, cases); }); // f16: vec2 to 32-bit scalar numeric @@ -383,7 +336,7 @@ g.test('vec2h_to_i32') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'vec2_f16_to_i32' : 'vec2_f16_inf_nan_to_i32' ); - await run(t, bitcastBuilder('i32', t.params), [TypeVec(2, TypeF16)], TypeI32, t.params, cases); + await run(t, bitcastBuilder('i32', t.params), [Type.vec2h], Type.i32, t.params, cases); }); g.test('vec2h_to_u32') @@ -398,7 +351,7 @@ g.test('vec2h_to_u32') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'vec2_f16_to_u32' : 'vec2_f16_inf_nan_to_u32' ); - await run(t, bitcastBuilder('u32', t.params), [TypeVec(2, TypeF16)], TypeU32, t.params, cases); + await run(t, bitcastBuilder('u32', t.params), [Type.vec2h], Type.u32, t.params, cases); }); g.test('vec2h_to_f32') @@ -413,7 +366,7 @@ g.test('vec2h_to_f32') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'vec2_f16_to_f32_finite' : 'vec2_f16_inf_nan_to_f32' ); - await run(t, bitcastBuilder('f32', t.params), [TypeVec(2, TypeF16)], TypeF32, t.params, cases); + await run(t, bitcastBuilder('f32', t.params), [Type.vec2h], Type.f32, t.params, cases); }); // f16: vec4 to vec2<32-bit scalar numeric> @@ -429,14 +382,7 @@ g.test('vec4h_to_vec2i') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'vec4_f16_to_vec2_i32' : 'vec4_f16_inf_nan_to_vec2_i32' ); - await run( - t, - bitcastBuilder('vec2', t.params), - [TypeVec(4, TypeF16)], - TypeVec(2, TypeI32), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.vec4h], Type.vec2i, t.params, cases); }); g.test('vec4h_to_vec2u') @@ -451,14 +397,7 @@ g.test('vec4h_to_vec2u') // Infinities and NaNs are errors in const-eval. t.params.inputSource === 'const' ? 'vec4_f16_to_vec2_u32' : 'vec4_f16_inf_nan_to_vec2_u32' ); - await run( - t, - bitcastBuilder('vec2', t.params), - [TypeVec(4, TypeF16)], - TypeVec(2, TypeU32), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.vec4h], Type.vec2u, t.params, cases); }); g.test('vec4h_to_vec2f') @@ -475,14 +414,7 @@ g.test('vec4h_to_vec2f') ? 'vec4_f16_to_vec2_f32_finite' : 'vec4_f16_inf_nan_to_vec2_f32' ); - await run( - t, - bitcastBuilder('vec2', t.params), - [TypeVec(4, TypeF16)], - TypeVec(2, TypeF32), - t.params, - cases - ); + await run(t, bitcastBuilder('vec2', t.params), [Type.vec4h], Type.vec2f, t.params, cases); }); // Abstract Float @@ -505,7 +437,7 @@ g.test('af_to_f32') }; }); - await run(t, bitcastBuilder('f32', t.params), [TypeAbstractFloat], TypeF32, t.params, cases); + await run(t, bitcastBuilder('f32', t.params), [Type.abstractFloat], Type.f32, t.params, cases); }); g.test('af_to_i32') @@ -549,7 +481,7 @@ g.test('af_to_i32') }; }); - await run(t, bitcastBuilder('i32', t.params), [TypeAbstractFloat], TypeI32, t.params, cases); + await run(t, bitcastBuilder('i32', t.params), [Type.abstractFloat], Type.i32, t.params, cases); }); g.test('af_to_u32') @@ -593,7 +525,7 @@ g.test('af_to_u32') }; }); - await run(t, bitcastBuilder('u32', t.params), [TypeAbstractFloat], TypeU32, t.params, cases); + await run(t, bitcastBuilder('u32', t.params), [Type.abstractFloat], Type.u32, t.params, cases); }); g.test('af_to_vec2f16') @@ -609,8 +541,8 @@ g.test('af_to_vec2f16') await run( t, bitcastBuilder('vec2', t.params), - [TypeAbstractFloat], - TypeVec(2, TypeF16), + [Type.abstractFloat], + Type.vec2h, t.params, cases ); @@ -629,8 +561,8 @@ g.test('vec2af_to_vec4f16') await run( t, bitcastBuilder('vec4', t.params), - [TypeVec(2, TypeAbstractFloat)], - TypeVec(4, TypeF16), + [Type.vec(2, Type.abstractFloat)], + Type.vec4h, t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts index 18509efdffb9..5f6236149e53 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'ceil' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn ceil(e: T ) -> T Returns the ceiling of e. Component-wise when T is a vector. @@ -10,7 +10,7 @@ Returns the ceiling of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -34,7 +34,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('ceil'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('ceil'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -48,5 +48,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('ceil'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('ceil'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts index ef63fb385ba5..909d15e7e752 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/clamp.cache.ts @@ -1,5 +1,5 @@ import { kValue } from '../../../../../util/constants.js'; -import { ScalarType, TypeAbstractInt, TypeI32, TypeU32 } from '../../../../../util/conversion.js'; +import { ScalarType, Type } from '../../../../../util/conversion.js'; import { FP } from '../../../../../util/floating_point.js'; import { maxBigInt, minBigInt } from '../../../../../util/math.js'; import { Case } from '../../case.js'; @@ -59,11 +59,11 @@ function generateAbstractIntegerTestCases(test_values: Array): Array ({ input: [ - TypeAbstractInt.create(e), - TypeAbstractInt.create(low), - TypeAbstractInt.create(high), + Type.abstractInt.create(e), + Type.abstractInt.create(low), + Type.abstractInt.create(high), ], - expected: TypeAbstractInt.create(minBigInt(maxBigInt(e, low), high)), + expected: Type.abstractInt.create(minBigInt(maxBigInt(e, low), high)), })) ) ); @@ -113,16 +113,16 @@ const fp_cases = (['f32', 'f16', 'abstract'] as const) export const d = makeCaseCache('clamp', { u32_non_const: () => { - return generateConcreteIntegerTestCases(u32Values, TypeU32, 'non_const'); + return generateConcreteIntegerTestCases(u32Values, Type.u32, 'non_const'); }, u32_const: () => { - return generateConcreteIntegerTestCases(u32Values, TypeU32, 'const'); + return generateConcreteIntegerTestCases(u32Values, Type.u32, 'const'); }, i32_non_const: () => { - return generateConcreteIntegerTestCases(i32Values, TypeI32, 'non_const'); + return generateConcreteIntegerTestCases(i32Values, Type.i32, 'non_const'); }, i32_const: () => { - return generateConcreteIntegerTestCases(i32Values, TypeI32, 'const'); + return generateConcreteIntegerTestCases(i32Values, Type.i32, 'const'); }, abstract_int: () => { return generateAbstractIntegerTestCases(abstractFloatValues); diff --git a/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts index cc29ceba03f7..0b524bccf0d3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts @@ -1,12 +1,12 @@ export const description = ` Execution tests for the 'clamp' builtin function -S is AbstractInt, i32, or u32 +S is abstract-int, i32, or u32 T is S or vecN @const fn clamp(e: T , low: T, high: T) -> T Returns min(max(e,low),high). Component-wise when T is a vector. -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const clamp(e: T , low: T , high: T) -> T Returns either min(max(e,low),high), or the median of the three values e, low, high. @@ -15,14 +15,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - TypeAbstractFloat, - TypeAbstractInt, - TypeF16, - TypeF32, - TypeI32, - TypeU32, -} from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js'; @@ -43,8 +36,8 @@ g.test('abstract_int') await run( t, abstractIntBuiltin('clamp'), - [TypeAbstractInt, TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -58,7 +51,7 @@ g.test('u32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); - await run(t, builtin('clamp'), [TypeU32, TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, builtin('clamp'), [Type.u32, Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('i32') @@ -69,7 +62,7 @@ g.test('i32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'i32_const' : 'i32_non_const'); - await run(t, builtin('clamp'), [TypeI32, TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, builtin('clamp'), [Type.i32, Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('abstract_float') @@ -85,8 +78,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('clamp'), - [TypeAbstractFloat, TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -100,7 +93,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('clamp'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('clamp'), [Type.f32, Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -114,5 +107,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('clamp'), [TypeF16, TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('clamp'), [Type.f16, Type.f16, Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts index 5eb9081928d1..4e3756834def 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'cos' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn cos(e: T ) -> T Returns the cosine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the cosine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -39,7 +39,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1] ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('cos'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('cos'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -53,5 +53,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('cos'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('cos'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts index aba7212a06e3..70713a392751 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'cosh' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn cosh(e: T ) -> T Returns the hyperbolic cosine of e. Component-wise when T is a vector @@ -9,7 +9,7 @@ Returns the hyperbolic cosine of e. Component-wise when T is a vector import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('cosh'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('cosh'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('cosh'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('cosh'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts index cfae4bb6e03b..ea0c38ae58d9 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts @@ -12,7 +12,7 @@ Also known as "clz" in some languages. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, u32Bits, u32, TypeI32, i32Bits, i32 } from '../../../../../util/conversion.js'; +import { Type, u32Bits, u32, i32Bits, i32 } from '../../../../../util/conversion.js'; import { allInputSources, Config, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -27,7 +27,7 @@ g.test('u32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('countLeadingZeros'), [TypeU32], TypeU32, cfg, [ + await run(t, builtin('countLeadingZeros'), [Type.u32], Type.u32, cfg, [ // Zero { input: u32Bits(0b00000000000000000000000000000000), expected: u32(32) }, @@ -142,7 +142,7 @@ g.test('i32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('countLeadingZeros'), [TypeI32], TypeI32, cfg, [ + await run(t, builtin('countLeadingZeros'), [Type.i32], Type.i32, cfg, [ // Zero { input: i32Bits(0b00000000000000000000000000000000), expected: i32(32) }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts index f0be9162856a..1937e0428362 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts @@ -11,7 +11,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, u32Bits, u32, TypeI32, i32Bits, i32 } from '../../../../../util/conversion.js'; +import { Type, u32Bits, u32, i32Bits, i32 } from '../../../../../util/conversion.js'; import { allInputSources, Config, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -26,7 +26,7 @@ g.test('u32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('countOneBits'), [TypeU32], TypeU32, cfg, [ + await run(t, builtin('countOneBits'), [Type.u32], Type.u32, cfg, [ // Zero { input: u32Bits(0b00000000000000000000000000000000), expected: u32(0) }, @@ -141,7 +141,7 @@ g.test('i32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('countOneBits'), [TypeI32], TypeI32, cfg, [ + await run(t, builtin('countOneBits'), [Type.i32], Type.i32, cfg, [ // Zero { input: i32Bits(0b00000000000000000000000000000000), expected: i32(0) }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts index d0b3198f49c4..3392a47810cf 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts @@ -12,7 +12,7 @@ Also known as "ctz" in some languages. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js'; +import { i32, i32Bits, Type, u32, u32Bits } from '../../../../../util/conversion.js'; import { allInputSources, Config, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -27,7 +27,7 @@ g.test('u32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('countTrailingZeros'), [TypeU32], TypeU32, cfg, [ + await run(t, builtin('countTrailingZeros'), [Type.u32], Type.u32, cfg, [ // Zero { input: u32Bits(0b00000000000000000000000000000000), expected: u32(32) }, @@ -142,7 +142,7 @@ g.test('i32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('countTrailingZeros'), [TypeI32], TypeI32, cfg, [ + await run(t, builtin('countTrailingZeros'), [Type.i32], Type.i32, cfg, [ // Zero { input: i32Bits(0b00000000000000000000000000000000), expected: i32(32) }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts index 95b39f45c5b3..cc32537076d1 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts @@ -1,14 +1,14 @@ export const description = ` Execution tests for the 'cross' builtin function -T is AbstractFloat, f32, or f16 +T is abstract-float, f32, or f16 @const fn cross(e1: vec3 ,e2: vec3) -> vec3 Returns the cross product of e1 and e2. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -25,8 +25,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('cross'), - [TypeVec(3, TypeAbstractFloat), TypeVec(3, TypeAbstractFloat)], - TypeVec(3, TypeAbstractFloat), + [Type.vec(3, Type.abstractFloat), Type.vec(3, Type.abstractFloat)], + Type.vec(3, Type.abstractFloat), t.params, cases ); @@ -38,14 +38,7 @@ g.test('f32') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run( - t, - builtin('cross'), - [TypeVec(3, TypeF32), TypeVec(3, TypeF32)], - TypeVec(3, TypeF32), - t.params, - cases - ); + await run(t, builtin('cross'), [Type.vec3f, Type.vec3f], Type.vec3f, t.params, cases); }); g.test('f16') @@ -57,12 +50,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run( - t, - builtin('cross'), - [TypeVec(3, TypeF16), TypeVec(3, TypeF16)], - TypeVec(3, TypeF16), - t.params, - cases - ); + await run(t, builtin('cross'), [Type.vec3h, Type.vec3h], Type.vec3h, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts index f69a92cf5f45..e8589c99332d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/degrees.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'degrees' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn degrees(e1: T ) -> T Converts radians to degrees, approximating e1 × 180 ÷ π. Component-wise when T is a vector @@ -9,7 +9,7 @@ Converts radians to degrees, approximating e1 × 180 ÷ π. Component-wise when import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -30,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('degrees'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -45,7 +45,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('degrees'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('degrees'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -59,5 +59,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('degrees'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('degrees'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts index c628623f4378..8b259eedb33e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/determinant.spec.ts @@ -1,14 +1,14 @@ export const description = ` Execution tests for the 'determinant' builtin function -T is AbstractFloat, f32, or f16 +T is abstract-float, f32, or f16 @const determinant(e: matCxC ) -> T Returns the determinant of e. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeMat } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ? `f32_mat${dim}x${dim}_const` : `f32_mat${dim}x${dim}_non_const` ); - await run(t, builtin('determinant'), [TypeMat(dim, dim, TypeF32)], TypeF32, t.params, cases); + await run(t, builtin('determinant'), [Type.mat(dim, dim, Type.f32)], Type.f32, t.params, cases); }); g.test('f16') @@ -50,5 +50,5 @@ g.test('f16') ? `f16_mat${dim}x${dim}_const` : `f16_mat${dim}x${dim}_non_const` ); - await run(t, builtin('determinant'), [TypeMat(dim, dim, TypeF16)], TypeF16, t.params, cases); + await run(t, builtin('determinant'), [Type.mat(dim, dim, Type.f16)], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts index ab63029ca227..0f50cd30d37a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/distance.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'distance' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn distance(e1: T ,e2: T ) -> f32 Returns the distance between e1 and e2 (e.g. length(e1-e2)). @@ -10,7 +10,7 @@ Returns the distance between e1 and e2 (e.g. length(e1-e2)). import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -32,7 +32,7 @@ g.test('f32') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('distance'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('distance'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f32_vec2') @@ -43,14 +43,7 @@ g.test('f32_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' ); - await run( - t, - builtin('distance'), - [TypeVec(2, TypeF32), TypeVec(2, TypeF32)], - TypeF32, - t.params, - cases - ); + await run(t, builtin('distance'), [Type.vec2f, Type.vec2f], Type.f32, t.params, cases); }); g.test('f32_vec3') @@ -61,14 +54,7 @@ g.test('f32_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' ); - await run( - t, - builtin('distance'), - [TypeVec(3, TypeF32), TypeVec(3, TypeF32)], - TypeF32, - t.params, - cases - ); + await run(t, builtin('distance'), [Type.vec3f, Type.vec3f], Type.f32, t.params, cases); }); g.test('f32_vec4') @@ -79,14 +65,7 @@ g.test('f32_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' ); - await run( - t, - builtin('distance'), - [TypeVec(4, TypeF32), TypeVec(4, TypeF32)], - TypeF32, - t.params, - cases - ); + await run(t, builtin('distance'), [Type.vec4f, Type.vec4f], Type.f32, t.params, cases); }); g.test('f16') @@ -98,7 +77,7 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('distance'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('distance'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('f16_vec2') @@ -112,14 +91,7 @@ g.test('f16_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const' ); - await run( - t, - builtin('distance'), - [TypeVec(2, TypeF16), TypeVec(2, TypeF16)], - TypeF16, - t.params, - cases - ); + await run(t, builtin('distance'), [Type.vec2h, Type.vec2h], Type.f16, t.params, cases); }); g.test('f16_vec3') @@ -133,14 +105,7 @@ g.test('f16_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const' ); - await run( - t, - builtin('distance'), - [TypeVec(3, TypeF16), TypeVec(3, TypeF16)], - TypeF16, - t.params, - cases - ); + await run(t, builtin('distance'), [Type.vec3h, Type.vec3h], Type.f16, t.params, cases); }); g.test('f16_vec4') @@ -154,12 +119,5 @@ g.test('f16_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const' ); - await run( - t, - builtin('distance'), - [TypeVec(4, TypeF16), TypeVec(4, TypeF16)], - TypeF16, - t.params, - cases - ); + await run(t, builtin('distance'), [Type.vec4h, Type.vec4h], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts index 5d27e5044dea..7d39aa0fbe25 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/dot.spec.ts @@ -1,21 +1,14 @@ export const description = ` Execution tests for the 'dot' builtin function -T is AbstractInt, AbstractFloat, i32, u32, f32, or f16 +T is Type.abstractInt, Type.abstractFloat, i32, u32, f32, or f16 @const fn dot(e1: vecN,e2: vecN) -> T Returns the dot product of e1 and e2. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - TypeAbstractInt, - TypeF16, - TypeF32, - TypeI32, - TypeU32, - TypeVec, -} from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractIntBuiltin, builtin } from './builtin.js'; @@ -32,8 +25,8 @@ g.test('abstract_int_vec2') await run( t, abstractIntBuiltin('dot'), - [TypeVec(2, TypeAbstractInt), TypeVec(2, TypeAbstractInt)], - TypeAbstractInt, + [Type.vec(2, Type.abstractInt), Type.vec(2, Type.abstractInt)], + Type.abstractInt, t.params, cases ); @@ -48,8 +41,8 @@ g.test('abstract_int_vec3') await run( t, abstractIntBuiltin('dot'), - [TypeVec(3, TypeAbstractInt), TypeVec(3, TypeAbstractInt)], - TypeAbstractInt, + [Type.vec(3, Type.abstractInt), Type.vec(3, Type.abstractInt)], + Type.abstractInt, t.params, cases ); @@ -64,8 +57,8 @@ g.test('abstract_int_vec4') await run( t, abstractIntBuiltin('dot'), - [TypeVec(4, TypeAbstractInt), TypeVec(4, TypeAbstractInt)], - TypeAbstractInt, + [Type.vec(4, Type.abstractInt), Type.vec(4, Type.abstractInt)], + Type.abstractInt, t.params, cases ); @@ -77,14 +70,7 @@ g.test('i32_vec2') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('i32_vec2'); - await run( - t, - builtin('dot'), - [TypeVec(2, TypeI32), TypeVec(2, TypeI32)], - TypeI32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec2i, Type.vec2i], Type.i32, t.params, cases); }); g.test('i32_vec3') @@ -93,14 +79,7 @@ g.test('i32_vec3') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('i32_vec3'); - await run( - t, - builtin('dot'), - [TypeVec(3, TypeI32), TypeVec(3, TypeI32)], - TypeI32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec3i, Type.vec3i], Type.i32, t.params, cases); }); g.test('i32_vec4') @@ -109,14 +88,7 @@ g.test('i32_vec4') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('i32_vec4'); - await run( - t, - builtin('dot'), - [TypeVec(4, TypeI32), TypeVec(4, TypeI32)], - TypeI32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec4i, Type.vec4i], Type.i32, t.params, cases); }); g.test('u32_vec2') @@ -125,14 +97,7 @@ g.test('u32_vec2') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('u32_vec2'); - await run( - t, - builtin('dot'), - [TypeVec(2, TypeU32), TypeVec(2, TypeU32)], - TypeU32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec2u, Type.vec2u], Type.u32, t.params, cases); }); g.test('u32_vec3') @@ -141,14 +106,7 @@ g.test('u32_vec3') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('u32_vec3'); - await run( - t, - builtin('dot'), - [TypeVec(3, TypeU32), TypeVec(3, TypeU32)], - TypeU32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec3u, Type.vec3u], Type.u32, t.params, cases); }); g.test('u32_vec4') @@ -157,14 +115,7 @@ g.test('u32_vec4') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('u32_vec4'); - await run( - t, - builtin('dot'), - [TypeVec(4, TypeU32), TypeVec(4, TypeU32)], - TypeU32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec4u, Type.vec4u], Type.u32, t.params, cases); }); g.test('abstract_float') @@ -181,14 +132,7 @@ g.test('f32_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' ); - await run( - t, - builtin('dot'), - [TypeVec(2, TypeF32), TypeVec(2, TypeF32)], - TypeF32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec2f, Type.vec2f], Type.f32, t.params, cases); }); g.test('f32_vec3') @@ -199,14 +143,7 @@ g.test('f32_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' ); - await run( - t, - builtin('dot'), - [TypeVec(3, TypeF32), TypeVec(3, TypeF32)], - TypeF32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec3f, Type.vec3f], Type.f32, t.params, cases); }); g.test('f32_vec4') @@ -217,14 +154,7 @@ g.test('f32_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' ); - await run( - t, - builtin('dot'), - [TypeVec(4, TypeF32), TypeVec(4, TypeF32)], - TypeF32, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec4f, Type.vec4f], Type.f32, t.params, cases); }); g.test('f16_vec2') @@ -238,14 +168,7 @@ g.test('f16_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const' ); - await run( - t, - builtin('dot'), - [TypeVec(2, TypeF16), TypeVec(2, TypeF16)], - TypeF16, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec2h, Type.vec2h], Type.f16, t.params, cases); }); g.test('f16_vec3') @@ -259,14 +182,7 @@ g.test('f16_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const' ); - await run( - t, - builtin('dot'), - [TypeVec(3, TypeF16), TypeVec(3, TypeF16)], - TypeF16, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec3h, Type.vec3h], Type.f16, t.params, cases); }); g.test('f16_vec4') @@ -280,12 +196,5 @@ g.test('f16_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const' ); - await run( - t, - builtin('dot'), - [TypeVec(4, TypeF16), TypeVec(4, TypeF16)], - TypeF16, - t.params, - cases - ); + await run(t, builtin('dot'), [Type.vec4h, Type.vec4h], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts index dee5290281a8..de537c473e64 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/dot4I8Packed.spec.ts @@ -9,7 +9,7 @@ the multiply, and then the add operations are done in WGSL i32 with wrapping beh import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeI32, TypeU32, i32, u32 } from '../../../../../util/conversion.js'; +import { Type, i32, u32 } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -70,5 +70,5 @@ g.test('basic') return [makeCase(...(v as [number, number]))]; }); - await run(t, builtin('dot4I8Packed'), [TypeU32, TypeU32], TypeI32, cfg, cases); + await run(t, builtin('dot4I8Packed'), [Type.u32, Type.u32], Type.i32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts index f0dd6fc5081b..a12a3d012302 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/dot4U8Packed.spec.ts @@ -8,7 +8,7 @@ unsigned integer dot product of these two vectors. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, u32 } from '../../../../../util/conversion.js'; +import { Type, u32 } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -55,5 +55,5 @@ g.test('basic') return [makeCase(...(v as [number, number]))]; }); - await run(t, builtin('dot4U8Packed'), [TypeU32, TypeU32], TypeU32, cfg, cases); + await run(t, builtin('dot4U8Packed'), [Type.u32, Type.u32], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts index ad381df01690..09759ea190ef 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'exp' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn exp(e1: T ) -> T Returns the natural exponentiation of e1 (e.g. e^e1). Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the natural exponentiation of e1 (e.g. e^e1). Component-wise when T is a import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('exp'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('exp'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('exp'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('exp'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts index cf0e3c3cbe81..f6b00c47ecca 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'exp2' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn exp2(e: T ) -> T Returns 2 raised to the power e (e.g. 2^e). Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns 2 raised to the power e (e.g. 2^e). Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('exp2'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('exp2'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('exp2'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('exp2'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts index d535bf5d746a..ef04b661bddf 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts @@ -33,17 +33,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - i32Bits, - TypeI32, - u32, - TypeU32, - u32Bits, - vec2, - vec3, - vec4, - TypeVec, -} from '../../../../../util/conversion.js'; +import { i32Bits, Type, u32, u32Bits, vec2, vec3, vec4 } from '../../../../../util/conversion.js'; import { allInputSources, Config, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -57,7 +47,7 @@ g.test('u32') .fn(async t => { const cfg: Config = t.params; - const T = t.params.width === 1 ? TypeU32 : TypeVec(t.params.width, TypeU32); + const T = t.params.width === 1 ? Type.u32 : Type.vec(t.params.width, Type.u32); const V = (x: number, y?: number, z?: number, w?: number) => { y = y === undefined ? x : y; @@ -193,7 +183,7 @@ g.test('u32') ); } - await run(t, builtin('extractBits'), [T, TypeU32, TypeU32], T, cfg, cases); + await run(t, builtin('extractBits'), [T, Type.u32, Type.u32], T, cfg, cases); }); g.test('i32') @@ -203,7 +193,7 @@ g.test('i32') .fn(async t => { const cfg: Config = t.params; - const T = t.params.width === 1 ? TypeI32 : TypeVec(t.params.width, TypeI32); + const T = t.params.width === 1 ? Type.i32 : Type.vec(t.params.width, Type.i32); const V = (x: number, y?: number, z?: number, w?: number) => { y = y === undefined ? x : y; @@ -333,5 +323,5 @@ g.test('i32') ); } - await run(t, builtin('extractBits'), [T, TypeU32, TypeU32], T, cfg, cases); + await run(t, builtin('extractBits'), [T, Type.u32, Type.u32], T, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts index c21541ab8839..fae851439fc9 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts @@ -1,14 +1,14 @@ export const description = ` Execution tests for the 'faceForward' builtin function -T is vecN, vecN, or vecN +T is vecN, vecN, or vecN @const fn faceForward(e1: T ,e2: T ,e3: T ) -> T Returns e1 if dot(e2,e3) is negative, and -e1 otherwise. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,8 +33,8 @@ g.test('f32_vec2') await run( t, builtin('faceForward'), - [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeVec(2, TypeF32)], - TypeVec(2, TypeF32), + [Type.vec2f, Type.vec2f, Type.vec2f], + Type.vec2f, t.params, cases ); @@ -51,8 +51,8 @@ g.test('f32_vec3') await run( t, builtin('faceForward'), - [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeVec(3, TypeF32)], - TypeVec(3, TypeF32), + [Type.vec3f, Type.vec3f, Type.vec3f], + Type.vec3f, t.params, cases ); @@ -69,8 +69,8 @@ g.test('f32_vec4') await run( t, builtin('faceForward'), - [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeVec(4, TypeF32)], - TypeVec(4, TypeF32), + [Type.vec4f, Type.vec4f, Type.vec4f], + Type.vec4f, t.params, cases ); @@ -90,8 +90,8 @@ g.test('f16_vec2') await run( t, builtin('faceForward'), - [TypeVec(2, TypeF16), TypeVec(2, TypeF16), TypeVec(2, TypeF16)], - TypeVec(2, TypeF16), + [Type.vec2h, Type.vec2h, Type.vec2h], + Type.vec2h, t.params, cases ); @@ -111,8 +111,8 @@ g.test('f16_vec3') await run( t, builtin('faceForward'), - [TypeVec(3, TypeF16), TypeVec(3, TypeF16), TypeVec(3, TypeF16)], - TypeVec(3, TypeF16), + [Type.vec3h, Type.vec3h, Type.vec3h], + Type.vec3h, t.params, cases ); @@ -132,8 +132,8 @@ g.test('f16_vec4') await run( t, builtin('faceForward'), - [TypeVec(4, TypeF16), TypeVec(4, TypeF16), TypeVec(4, TypeF16)], - TypeVec(4, TypeF16), + [Type.vec4h, Type.vec4h, Type.vec4h], + Type.vec4h, t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts index 26216563cd04..9248b1e2bf5c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts @@ -16,7 +16,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js'; +import { i32, i32Bits, Type, u32, u32Bits } from '../../../../../util/conversion.js'; import { allInputSources, Config, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -31,7 +31,7 @@ g.test('u32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('firstLeadingBit'), [TypeU32], TypeU32, cfg, [ + await run(t, builtin('firstLeadingBit'), [Type.u32], Type.u32, cfg, [ // Zero { input: u32Bits(0b00000000000000000000000000000000), expected: u32(-1) }, @@ -146,7 +146,7 @@ g.test('i32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('firstLeadingBit'), [TypeI32], TypeI32, cfg, [ + await run(t, builtin('firstLeadingBit'), [Type.i32], Type.i32, cfg, [ // Zero { input: i32Bits(0b00000000000000000000000000000000), expected: i32(-1) }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts index 5c65f59d2813..a8dd27ee87b2 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts @@ -12,7 +12,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { i32, i32Bits, TypeI32, u32, TypeU32, u32Bits } from '../../../../../util/conversion.js'; +import { i32, i32Bits, Type, u32, u32Bits } from '../../../../../util/conversion.js'; import { allInputSources, Config, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -27,7 +27,7 @@ g.test('u32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('firstTrailingBit'), [TypeU32], TypeU32, cfg, [ + await run(t, builtin('firstTrailingBit'), [Type.u32], Type.u32, cfg, [ // Zero { input: u32Bits(0b00000000000000000000000000000000), expected: u32(-1) }, @@ -142,7 +142,7 @@ g.test('i32') ) .fn(async t => { const cfg: Config = t.params; - await run(t, builtin('firstTrailingBit'), [TypeI32], TypeI32, cfg, [ + await run(t, builtin('firstTrailingBit'), [Type.i32], Type.i32, cfg, [ // Zero { input: i32Bits(0b00000000000000000000000000000000), expected: i32(-1) }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts index 63b0a138a045..26cffe5d10da 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'floor' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn floor(e: T ) -> T Returns the floor of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the floor of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -30,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('floor'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -45,7 +45,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('floor'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('floor'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -59,5 +59,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('floor'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('floor'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts index 2488603d8077..620792d42007 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'fma' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn fma(e1: T ,e2: T ,e3: T ) -> T Returns e1 * e2 + e3. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns e1 * e2 + e3. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -30,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('fma'), - [TypeAbstractFloat, TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -45,7 +45,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('fma'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('fma'), [Type.f32, Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -59,5 +59,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('fma'), [TypeF16, TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('fma'), [Type.f16, Type.f16, Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts index df9e1845273e..d840122b3978 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'fract' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn fract(e: T ) -> T Returns the fractional part of e, computed as e - floor(e). @@ -10,7 +10,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -34,7 +34,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('fract'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('fract'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -48,5 +48,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('fract'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('fract'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts b/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts index 7c107eb729a7..05f8de7f8936 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/frexp.cache.ts @@ -1,5 +1,5 @@ import { skipUndefined } from '../../../../../util/compare.js'; -import { Scalar, Vector, i32, toVector } from '../../../../../util/conversion.js'; +import { ScalarValue, VectorValue, i32, toVector } from '../../../../../util/conversion.js'; import { FP } from '../../../../../util/floating_point.js'; import { frexp } from '../../../../../util/math.js'; import { Case } from '../../case.js'; @@ -8,8 +8,8 @@ import { makeCaseCache } from '../../case_cache.js'; /* @returns a fract Case for a given scalar or vector input */ function makeCaseFract(v: number | readonly number[], trait: 'f32' | 'f16'): Case { const fp = FP[trait]; - let toInput: (n: readonly number[]) => Scalar | Vector; - let toOutput: (n: readonly number[]) => Scalar | Vector; + let toInput: (n: readonly number[]) => ScalarValue | VectorValue; + let toOutput: (n: readonly number[]) => ScalarValue | VectorValue; if (v instanceof Array) { // Input is vector toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder); @@ -36,8 +36,8 @@ function makeCaseFract(v: number | readonly number[], trait: 'f32' | 'f16'): Cas /* @returns an exp Case for a given scalar or vector input */ function makeCaseExp(v: number | readonly number[], trait: 'f32' | 'f16'): Case { const fp = FP[trait]; - let toInput: (n: readonly number[]) => Scalar | Vector; - let toOutput: (n: readonly number[]) => Scalar | Vector; + let toInput: (n: readonly number[]) => ScalarValue | VectorValue; + let toOutput: (n: readonly number[]) => ScalarValue | VectorValue; if (v instanceof Array) { // Input is vector toInput = (n: readonly number[]) => toVector(n, fp.scalarBuilder); diff --git a/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts index 663e345f499a..ee506decd9d0 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts @@ -15,7 +15,7 @@ The magnitude of the significand is in the range of [0.5, 1.0) or 0. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeI32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, basicExpressionBuilder, run } from '../../expression.js'; import { d } from './frexp.cache.js'; @@ -46,7 +46,7 @@ struct __frexp_result_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_fract'); - await run(t, fractBuilder(), [TypeF32], TypeF32, t.params, cases); + await run(t, fractBuilder(), [Type.f32], Type.f32, t.params, cases); }); g.test('f32_exp') @@ -64,7 +64,7 @@ struct __frexp_result_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_exp'); - await run(t, expBuilder(), [TypeF32], TypeI32, t.params, cases); + await run(t, expBuilder(), [Type.f32], Type.i32, t.params, cases); }); g.test('f32_vec2_fract') @@ -82,7 +82,7 @@ struct __frexp_result_vec2_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec2_fract'); - await run(t, fractBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases); + await run(t, fractBuilder(), [Type.vec2f], Type.vec2f, t.params, cases); }); g.test('f32_vec2_exp') @@ -100,7 +100,7 @@ struct __frexp_result_vec2_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec2_exp'); - await run(t, expBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeI32), t.params, cases); + await run(t, expBuilder(), [Type.vec2f], Type.vec2i, t.params, cases); }); g.test('f32_vec3_fract') @@ -118,7 +118,7 @@ struct __frexp_result_vec3_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec3_fract'); - await run(t, fractBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases); + await run(t, fractBuilder(), [Type.vec3f], Type.vec3f, t.params, cases); }); g.test('f32_vec3_exp') @@ -136,7 +136,7 @@ struct __frexp_result_vec3_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec3_exp'); - await run(t, expBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeI32), t.params, cases); + await run(t, expBuilder(), [Type.vec3f], Type.vec3i, t.params, cases); }); g.test('f32_vec4_fract') @@ -154,7 +154,7 @@ struct __frexp_result_vec4_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec4_fract'); - await run(t, fractBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases); + await run(t, fractBuilder(), [Type.vec4f], Type.vec4f, t.params, cases); }); g.test('f32_vec4_exp') @@ -172,7 +172,7 @@ struct __frexp_result_vec4_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec4_exp'); - await run(t, expBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeI32), t.params, cases); + await run(t, expBuilder(), [Type.vec4f], Type.vec4i, t.params, cases); }); g.test('f16_fract') @@ -193,7 +193,7 @@ struct __frexp_result_f16 { }) .fn(async t => { const cases = await d.get('f16_fract'); - await run(t, fractBuilder(), [TypeF16], TypeF16, t.params, cases); + await run(t, fractBuilder(), [Type.f16], Type.f16, t.params, cases); }); g.test('f16_exp') @@ -214,7 +214,7 @@ struct __frexp_result_f16 { }) .fn(async t => { const cases = await d.get('f16_exp'); - await run(t, expBuilder(), [TypeF16], TypeI32, t.params, cases); + await run(t, expBuilder(), [Type.f16], Type.i32, t.params, cases); }); g.test('f16_vec2_fract') @@ -235,7 +235,7 @@ struct __frexp_result_vec2_f16 { }) .fn(async t => { const cases = await d.get('f16_vec2_fract'); - await run(t, fractBuilder(), [TypeVec(2, TypeF16)], TypeVec(2, TypeF16), t.params, cases); + await run(t, fractBuilder(), [Type.vec2h], Type.vec2h, t.params, cases); }); g.test('f16_vec2_exp') @@ -256,7 +256,7 @@ struct __frexp_result_vec2_f16 { }) .fn(async t => { const cases = await d.get('f16_vec2_exp'); - await run(t, expBuilder(), [TypeVec(2, TypeF16)], TypeVec(2, TypeI32), t.params, cases); + await run(t, expBuilder(), [Type.vec2h], Type.vec2i, t.params, cases); }); g.test('f16_vec3_fract') @@ -277,7 +277,7 @@ struct __frexp_result_vec3_f16 { }) .fn(async t => { const cases = await d.get('f16_vec3_fract'); - await run(t, fractBuilder(), [TypeVec(3, TypeF16)], TypeVec(3, TypeF16), t.params, cases); + await run(t, fractBuilder(), [Type.vec3h], Type.vec3h, t.params, cases); }); g.test('f16_vec3_exp') @@ -298,7 +298,7 @@ struct __frexp_result_vec3_f16 { }) .fn(async t => { const cases = await d.get('f16_vec3_exp'); - await run(t, expBuilder(), [TypeVec(3, TypeF16)], TypeVec(3, TypeI32), t.params, cases); + await run(t, expBuilder(), [Type.vec3h], Type.vec3i, t.params, cases); }); g.test('f16_vec4_fract') @@ -319,7 +319,7 @@ struct __frexp_result_vec4_f16 { }) .fn(async t => { const cases = await d.get('f16_vec4_fract'); - await run(t, fractBuilder(), [TypeVec(4, TypeF16)], TypeVec(4, TypeF16), t.params, cases); + await run(t, fractBuilder(), [Type.vec4h], Type.vec4h, t.params, cases); }); g.test('f16_vec4_exp') @@ -340,5 +340,5 @@ struct __frexp_result_vec4_f16 { }) .fn(async t => { const cases = await d.get('f16_vec4_exp'); - await run(t, expBuilder(), [TypeVec(4, TypeF16)], TypeVec(4, TypeI32), t.params, cases); + await run(t, expBuilder(), [Type.vec4h], Type.vec4i, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts index 1068e76252c2..b3eb65781d59 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts @@ -18,17 +18,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - i32Bits, - TypeI32, - u32, - TypeU32, - u32Bits, - vec2, - vec3, - vec4, - TypeVec, -} from '../../../../../util/conversion.js'; +import { i32Bits, Type, u32, u32Bits, vec2, vec3, vec4 } from '../../../../../util/conversion.js'; import { allInputSources, Config, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -46,8 +36,8 @@ g.test('integer') ) .fn(async t => { const cfg: Config = t.params; - const scalarType = t.params.signed ? TypeI32 : TypeU32; - const T = t.params.width === 1 ? scalarType : TypeVec(t.params.width, scalarType); + const scalarType = t.params.signed ? Type.i32 : Type.u32; + const T = t.params.width === 1 ? scalarType : Type.vec(t.params.width, scalarType); const V = (x: number, y?: number, z?: number, w?: number) => { y = y === undefined ? x : y; @@ -382,5 +372,5 @@ g.test('integer') ); } - await run(t, builtin('insertBits'), [T, T, TypeU32, TypeU32], T, cfg, cases); + await run(t, builtin('insertBits'), [T, T, Type.u32, Type.u32], T, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts index fb5248ec8ad6..c27c56d4b5dd 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'inverseSqrt' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn inverseSqrt(e: T ) -> T Returns the reciprocal of sqrt(e). Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the reciprocal of sqrt(e). Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('inverseSqrt'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('inverseSqrt'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('inverseSqrt'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('inverseSqrt'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts index e2eba8232d43..b06a3ef55086 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts @@ -1,10 +1,10 @@ export const description = ` Execution tests for the 'ldexp' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN -K is AbstractInt, i32 +K is Type.abstractInt, i32 I is K or vecN, where I is a scalar if T is a scalar, or a vector when T is a vector @@ -14,7 +14,7 @@ Returns e1 * 2^e2. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeI32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -41,7 +41,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('ldexp'), [TypeF32, TypeI32], TypeF32, t.params, cases); + await run(t, builtin('ldexp'), [Type.f32, Type.i32], Type.f32, t.params, cases); }); g.test('f16') @@ -55,5 +55,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('ldexp'), [TypeF16, TypeI32], TypeF16, t.params, cases); + await run(t, builtin('ldexp'), [Type.f16, Type.i32], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts index e91d5dcdafe5..690bc6152b37 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/length.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'length' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn length(e: T ) -> f32 Returns the length of e (e.g. abs(e) if T is a scalar, or sqrt(e[0]^2 + e[1]^2 + ...) if T is a vector). @@ -9,7 +9,7 @@ Returns the length of e (e.g. abs(e) if T is a scalar, or sqrt(e[0]^2 + e[1]^2 + import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -31,7 +31,7 @@ g.test('f32') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('length'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('length'), [Type.f32], Type.f32, t.params, cases); }); g.test('f32_vec2') @@ -42,7 +42,7 @@ g.test('f32_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' ); - await run(t, builtin('length'), [TypeVec(2, TypeF32)], TypeF32, t.params, cases); + await run(t, builtin('length'), [Type.vec2f], Type.f32, t.params, cases); }); g.test('f32_vec3') @@ -53,7 +53,7 @@ g.test('f32_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' ); - await run(t, builtin('length'), [TypeVec(3, TypeF32)], TypeF32, t.params, cases); + await run(t, builtin('length'), [Type.vec3f], Type.f32, t.params, cases); }); g.test('f32_vec4') @@ -64,7 +64,7 @@ g.test('f32_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' ); - await run(t, builtin('length'), [TypeVec(4, TypeF32)], TypeF32, t.params, cases); + await run(t, builtin('length'), [Type.vec4f], Type.f32, t.params, cases); }); g.test('f16') @@ -76,7 +76,7 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('length'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('length'), [Type.f16], Type.f16, t.params, cases); }); g.test('f16_vec2') @@ -90,7 +90,7 @@ g.test('f16_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const' ); - await run(t, builtin('length'), [TypeVec(2, TypeF16)], TypeF16, t.params, cases); + await run(t, builtin('length'), [Type.vec2h], Type.f16, t.params, cases); }); g.test('f16_vec3') @@ -104,7 +104,7 @@ g.test('f16_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const' ); - await run(t, builtin('length'), [TypeVec(3, TypeF16)], TypeF16, t.params, cases); + await run(t, builtin('length'), [Type.vec3h], Type.f16, t.params, cases); }); g.test('f16_vec4') @@ -118,5 +118,5 @@ g.test('f16_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const' ); - await run(t, builtin('length'), [TypeVec(4, TypeF16)], TypeF16, t.params, cases); + await run(t, builtin('length'), [Type.vec4h], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts index 387705320ff9..1d9814679002 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/log.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'log' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn log(e: T ) -> T Returns the natural logarithm of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the natural logarithm of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -39,7 +39,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1] ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('log'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('log'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -53,5 +53,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('log'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('log'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts index 86a38a60246a..497c66676cc3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'log2' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn log2(e: T ) -> T Returns the base-2 logarithm of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the base-2 logarithm of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -39,7 +39,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1] ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('log2'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('log2'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -53,5 +53,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('log2'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('log2'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts index 63e3fda3f2b6..ee7cb0d674f5 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/max.spec.ts @@ -1,12 +1,12 @@ export const description = ` Execution tests for the 'max' builtin function -S is AbstractInt, i32, or u32 +S is abstract-int, i32, or u32 T is S or vecN @const fn max(e1: T ,e2: T) -> T Returns e2 if e1 is less than e2, and e1 otherwise. Component-wise when T is a vector. -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is vecN @const fn max(e1: T ,e2: T) -> T Returns e2 if e1 is less than e2, and e1 otherwise. @@ -18,17 +18,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - TypeAbstractFloat, - TypeF16, - TypeF32, - TypeI32, - TypeU32, - i32, - u32, - abstractInt, - TypeAbstractInt, -} from '../../../../../util/conversion.js'; +import { Type, i32, u32, abstractInt } from '../../../../../util/conversion.js'; import { maxBigInt } from '../../../../../util/math.js'; import { Case } from '../../case.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; @@ -66,8 +56,8 @@ g.test('abstract_int') await run( t, abstractIntBuiltin('max'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -87,7 +77,7 @@ g.test('u32') const test_values: number[] = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff]; const cases = generateTestCases(test_values, makeCase); - await run(t, builtin('max'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, builtin('max'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('i32') @@ -104,7 +94,7 @@ g.test('i32') const test_values: number[] = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000]; const cases = generateTestCases(test_values, makeCase); - await run(t, builtin('max'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, builtin('max'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('abstract_float') @@ -120,8 +110,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('max'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -135,7 +125,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('max'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('max'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -149,5 +139,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('max'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('max'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts index 30f411196047..ac636413997d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/min.spec.ts @@ -1,12 +1,12 @@ export const description = ` Execution tests for the 'min' builtin function -S is AbstractInt, i32, or u32 +S is abstract-int, i32, or u32 T is S or vecN @const fn min(e1: T ,e2: T) -> T Returns e1 if e1 is less than e2, and e2 otherwise. Component-wise when T is a vector. -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn min(e1: T ,e2: T) -> T Returns e2 if e2 is less than e1, and e1 otherwise. @@ -17,17 +17,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - TypeAbstractFloat, - TypeF16, - TypeF32, - TypeI32, - TypeU32, - i32, - u32, - abstractInt, - TypeAbstractInt, -} from '../../../../../util/conversion.js'; +import { Type, i32, u32, abstractInt } from '../../../../../util/conversion.js'; import { minBigInt } from '../../../../../util/math.js'; import { Case } from '../../case.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; @@ -65,8 +55,8 @@ g.test('abstract_int') await run( t, abstractIntBuiltin('min'), - [TypeAbstractInt, TypeAbstractInt], - TypeAbstractInt, + [Type.abstractInt, Type.abstractInt], + Type.abstractInt, t.params, cases ); @@ -86,7 +76,7 @@ g.test('u32') const test_values: number[] = [0, 1, 2, 0x70000000, 0x80000000, 0xffffffff]; const cases = generateTestCases(test_values, makeCase); - await run(t, builtin('min'), [TypeU32, TypeU32], TypeU32, t.params, cases); + await run(t, builtin('min'), [Type.u32, Type.u32], Type.u32, t.params, cases); }); g.test('i32') @@ -103,7 +93,7 @@ g.test('i32') const test_values: number[] = [-0x70000000, -2, -1, 0, 1, 2, 0x70000000]; const cases = generateTestCases(test_values, makeCase); - await run(t, builtin('min'), [TypeI32, TypeI32], TypeI32, t.params, cases); + await run(t, builtin('min'), [Type.i32, Type.i32], Type.i32, t.params, cases); }); g.test('abstract_float') @@ -119,8 +109,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('min'), - [TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -134,7 +124,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('min'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('min'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -148,5 +138,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('min'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('min'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts index d502c639620b..0005ab5c0eeb 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/mix.spec.ts @@ -1,12 +1,12 @@ export const description = ` Execution tests for the 'mix' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn mix(e1: T, e2: T, e3: T) -> T Returns the linear blend of e1 and e2 (e.g. e1*(1-e3)+e2*e3). Component-wise when T is a vector. -T is AbstractFloat, f32, or f16 +T is abstract-float, f32, or f16 T2 is vecN @const fn mix(e1: T2, e2: T2, e3: T) -> T2 Returns the component-wise linear blend of e1 and e2, using scalar blending factor e3 for each component. @@ -16,7 +16,7 @@ Same as mix(e1,e2,T2(e3)). import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -37,8 +37,8 @@ g.test('abstract_float_matching') await run( t, abstractFloatBuiltin('mix'), - [TypeAbstractFloat, TypeAbstractFloat, TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat, Type.abstractFloat, Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -53,8 +53,8 @@ g.test('abstract_float_nonmatching_vec2') await run( t, abstractFloatBuiltin('mix'), - [TypeVec(2, TypeAbstractFloat), TypeVec(2, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(2, TypeAbstractFloat), + [Type.vec(2, Type.abstractFloat), Type.vec(2, Type.abstractFloat), Type.abstractFloat], + Type.vec(2, Type.abstractFloat), t.params, cases ); @@ -69,8 +69,8 @@ g.test('abstract_float_nonmatching_vec3') await run( t, abstractFloatBuiltin('mix'), - [TypeVec(3, TypeAbstractFloat), TypeVec(3, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(3, TypeAbstractFloat), + [Type.vec(3, Type.abstractFloat), Type.vec(3, Type.abstractFloat), Type.abstractFloat], + Type.vec(3, Type.abstractFloat), t.params, cases ); @@ -85,8 +85,8 @@ g.test('abstract_float_nonmatching_vec4') await run( t, abstractFloatBuiltin('mix'), - [TypeVec(4, TypeAbstractFloat), TypeVec(4, TypeAbstractFloat), TypeAbstractFloat], - TypeVec(4, TypeAbstractFloat), + [Type.vec(4, Type.abstractFloat), Type.vec(4, Type.abstractFloat), Type.abstractFloat], + Type.vec(4, Type.abstractFloat), t.params, cases ); @@ -100,7 +100,7 @@ g.test('f32_matching') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('mix'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('mix'), [Type.f32, Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f32_nonmatching_vec2') @@ -111,14 +111,7 @@ g.test('f32_nonmatching_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec2_scalar_const' : 'f32_vec2_scalar_non_const' ); - await run( - t, - builtin('mix'), - [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeF32], - TypeVec(2, TypeF32), - t.params, - cases - ); + await run(t, builtin('mix'), [Type.vec2f, Type.vec2f, Type.f32], Type.vec2f, t.params, cases); }); g.test('f32_nonmatching_vec3') @@ -129,14 +122,7 @@ g.test('f32_nonmatching_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec3_scalar_const' : 'f32_vec3_scalar_non_const' ); - await run( - t, - builtin('mix'), - [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeF32], - TypeVec(3, TypeF32), - t.params, - cases - ); + await run(t, builtin('mix'), [Type.vec3f, Type.vec3f, Type.f32], Type.vec3f, t.params, cases); }); g.test('f32_nonmatching_vec4') @@ -147,14 +133,7 @@ g.test('f32_nonmatching_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec4_scalar_const' : 'f32_vec4_scalar_non_const' ); - await run( - t, - builtin('mix'), - [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeF32], - TypeVec(4, TypeF32), - t.params, - cases - ); + await run(t, builtin('mix'), [Type.vec4f, Type.vec4f, Type.f32], Type.vec4f, t.params, cases); }); g.test('f16_matching') @@ -168,7 +147,7 @@ g.test('f16_matching') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('mix'), [TypeF16, TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('mix'), [Type.f16, Type.f16, Type.f16], Type.f16, t.params, cases); }); g.test('f16_nonmatching_vec2') @@ -182,14 +161,7 @@ g.test('f16_nonmatching_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec2_scalar_const' : 'f16_vec2_scalar_non_const' ); - await run( - t, - builtin('mix'), - [TypeVec(2, TypeF16), TypeVec(2, TypeF16), TypeF16], - TypeVec(2, TypeF16), - t.params, - cases - ); + await run(t, builtin('mix'), [Type.vec2h, Type.vec2h, Type.f16], Type.vec2h, t.params, cases); }); g.test('f16_nonmatching_vec3') @@ -203,14 +175,7 @@ g.test('f16_nonmatching_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec3_scalar_const' : 'f16_vec3_scalar_non_const' ); - await run( - t, - builtin('mix'), - [TypeVec(3, TypeF16), TypeVec(3, TypeF16), TypeF16], - TypeVec(3, TypeF16), - t.params, - cases - ); + await run(t, builtin('mix'), [Type.vec3h, Type.vec3h, Type.f16], Type.vec3h, t.params, cases); }); g.test('f16_nonmatching_vec4') @@ -224,12 +189,5 @@ g.test('f16_nonmatching_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec4_scalar_const' : 'f16_vec4_scalar_non_const' ); - await run( - t, - builtin('mix'), - [TypeVec(4, TypeF16), TypeVec(4, TypeF16), TypeF16], - TypeVec(4, TypeF16), - t.params, - cases - ); + await run(t, builtin('mix'), [Type.vec4h, Type.vec4h, Type.f16], Type.vec4h, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts index 3e12d4f8696d..6c988008f825 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/modf.spec.ts @@ -1,13 +1,13 @@ export const description = ` Execution tests for the 'modf' builtin function -T is f32 or f16 or AbstractFloat +T is f32 or f16 or Type.abstractFloat @const fn modf(e:T) -> result_struct Splits |e| into fractional and whole number parts. The whole part is (|e| % 1.0), and the fractional part is |e| minus the whole part. Returns the result_struct for the given type. -S is f32 or f16 or AbstractFloat +S is f32 or f16 or Type.abstractFloat T is vecN @const fn modf(e:T) -> result_struct Splits the components of |e| into fractional and whole number parts. @@ -18,7 +18,7 @@ Returns the result_struct for the given type. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { abstractFloatShaderBuilder, allInputSources, @@ -67,7 +67,7 @@ struct __modf_result_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_fract'); - await run(t, fractBuilder(), [TypeF32], TypeF32, t.params, cases); + await run(t, fractBuilder(), [Type.f32], Type.f32, t.params, cases); }); g.test('f32_whole') @@ -85,7 +85,7 @@ struct __modf_result_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_whole'); - await run(t, wholeBuilder(), [TypeF32], TypeF32, t.params, cases); + await run(t, wholeBuilder(), [Type.f32], Type.f32, t.params, cases); }); g.test('f32_vec2_fract') @@ -103,7 +103,7 @@ struct __modf_result_vec2_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec2_fract'); - await run(t, fractBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases); + await run(t, fractBuilder(), [Type.vec2f], Type.vec2f, t.params, cases); }); g.test('f32_vec2_whole') @@ -121,7 +121,7 @@ struct __modf_result_vec2_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec2_whole'); - await run(t, wholeBuilder(), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases); + await run(t, wholeBuilder(), [Type.vec2f], Type.vec2f, t.params, cases); }); g.test('f32_vec3_fract') @@ -139,7 +139,7 @@ struct __modf_result_vec3_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec3_fract'); - await run(t, fractBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases); + await run(t, fractBuilder(), [Type.vec3f], Type.vec3f, t.params, cases); }); g.test('f32_vec3_whole') @@ -157,7 +157,7 @@ struct __modf_result_vec3_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec3_whole'); - await run(t, wholeBuilder(), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases); + await run(t, wholeBuilder(), [Type.vec3f], Type.vec3f, t.params, cases); }); g.test('f32_vec4_fract') @@ -175,7 +175,7 @@ struct __modf_result_vec4_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec4_fract'); - await run(t, fractBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases); + await run(t, fractBuilder(), [Type.vec4f], Type.vec4f, t.params, cases); }); g.test('f32_vec4_whole') @@ -193,7 +193,7 @@ struct __modf_result_vec4_f32 { .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get('f32_vec4_whole'); - await run(t, wholeBuilder(), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases); + await run(t, wholeBuilder(), [Type.vec4f], Type.vec4f, t.params, cases); }); g.test('f16_fract') @@ -214,7 +214,7 @@ struct __modf_result_f16 { }) .fn(async t => { const cases = await d.get('f16_fract'); - await run(t, fractBuilder(), [TypeF16], TypeF16, t.params, cases); + await run(t, fractBuilder(), [Type.f16], Type.f16, t.params, cases); }); g.test('f16_whole') @@ -235,7 +235,7 @@ struct __modf_result_f16 { }) .fn(async t => { const cases = await d.get('f16_whole'); - await run(t, wholeBuilder(), [TypeF16], TypeF16, t.params, cases); + await run(t, wholeBuilder(), [Type.f16], Type.f16, t.params, cases); }); g.test('f16_vec2_fract') @@ -256,7 +256,7 @@ struct __modf_result_vec2_f16 { }) .fn(async t => { const cases = await d.get('f16_vec2_fract'); - await run(t, fractBuilder(), [TypeVec(2, TypeF16)], TypeVec(2, TypeF16), t.params, cases); + await run(t, fractBuilder(), [Type.vec2h], Type.vec2h, t.params, cases); }); g.test('f16_vec2_whole') @@ -277,7 +277,7 @@ struct __modf_result_vec2_f16 { }) .fn(async t => { const cases = await d.get('f16_vec2_whole'); - await run(t, wholeBuilder(), [TypeVec(2, TypeF16)], TypeVec(2, TypeF16), t.params, cases); + await run(t, wholeBuilder(), [Type.vec2h], Type.vec2h, t.params, cases); }); g.test('f16_vec3_fract') @@ -298,7 +298,7 @@ struct __modf_result_vec3_f16 { }) .fn(async t => { const cases = await d.get('f16_vec3_fract'); - await run(t, fractBuilder(), [TypeVec(3, TypeF16)], TypeVec(3, TypeF16), t.params, cases); + await run(t, fractBuilder(), [Type.vec3h], Type.vec3h, t.params, cases); }); g.test('f16_vec3_whole') @@ -319,7 +319,7 @@ struct __modf_result_vec3_f16 { }) .fn(async t => { const cases = await d.get('f16_vec3_whole'); - await run(t, wholeBuilder(), [TypeVec(3, TypeF16)], TypeVec(3, TypeF16), t.params, cases); + await run(t, wholeBuilder(), [Type.vec3h], Type.vec3h, t.params, cases); }); g.test('f16_vec4_fract') @@ -340,7 +340,7 @@ struct __modf_result_vec4_f16 { }) .fn(async t => { const cases = await d.get('f16_vec4_fract'); - await run(t, fractBuilder(), [TypeVec(4, TypeF16)], TypeVec(4, TypeF16), t.params, cases); + await run(t, fractBuilder(), [Type.vec4h], Type.vec4h, t.params, cases); }); g.test('f16_vec4_whole') @@ -361,43 +361,43 @@ struct __modf_result_vec4_f16 { }) .fn(async t => { const cases = await d.get('f16_vec4_whole'); - await run(t, wholeBuilder(), [TypeVec(4, TypeF16)], TypeVec(4, TypeF16), t.params, cases); + await run(t, wholeBuilder(), [Type.vec4h], Type.vec4h, t.params, cases); }); g.test('abstract_fract') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc( ` -T is AbstractFloat +T is abstract-float struct __modf_result_abstract { - fract : AbstractFloat, // fractional part - whole : AbstractFloat // whole part + fract : Type.abstractFloat, // fractional part + whole : Type.abstractFloat // whole part } ` ) .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('abstract_fract'); - await run(t, abstractFractBuilder(), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases); + await run(t, abstractFractBuilder(), [Type.abstractFloat], Type.abstractFloat, t.params, cases); }); g.test('abstract_whole') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc( ` -T is AbstractFloat +T is abstract-float struct __modf_result_abstract { - fract : AbstractFloat, // fractional part - whole : AbstractFloat // whole part + fract : Type.abstractFloat, // fractional part + whole : Type.abstractFloat // whole part } ` ) .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('abstract_whole'); - await run(t, abstractWholeBuilder(), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases); + await run(t, abstractWholeBuilder(), [Type.abstractFloat], Type.abstractFloat, t.params, cases); }); g.test('abstract_vec2_fract') @@ -418,8 +418,8 @@ struct __modf_result_vec2_abstract { await run( t, abstractFractBuilder(), - [TypeVec(2, TypeAbstractFloat)], - TypeVec(2, TypeAbstractFloat), + [Type.vec(2, Type.abstractFloat)], + Type.vec(2, Type.abstractFloat), t.params, cases ); @@ -443,8 +443,8 @@ struct __modf_result_vec2_abstract { await run( t, abstractWholeBuilder(), - [TypeVec(2, TypeAbstractFloat)], - TypeVec(2, TypeAbstractFloat), + [Type.vec(2, Type.abstractFloat)], + Type.vec(2, Type.abstractFloat), t.params, cases ); @@ -468,8 +468,8 @@ struct __modf_result_vec3_abstract { await run( t, abstractFractBuilder(), - [TypeVec(3, TypeAbstractFloat)], - TypeVec(3, TypeAbstractFloat), + [Type.vec(3, Type.abstractFloat)], + Type.vec(3, Type.abstractFloat), t.params, cases ); @@ -493,8 +493,8 @@ struct __modf_result_vec3_abstract { await run( t, abstractWholeBuilder(), - [TypeVec(3, TypeAbstractFloat)], - TypeVec(3, TypeAbstractFloat), + [Type.vec(3, Type.abstractFloat)], + Type.vec(3, Type.abstractFloat), t.params, cases ); @@ -518,8 +518,8 @@ struct __modf_result_vec4_abstract { await run( t, abstractFractBuilder(), - [TypeVec(4, TypeAbstractFloat)], - TypeVec(4, TypeAbstractFloat), + [Type.vec(4, Type.abstractFloat)], + Type.vec(4, Type.abstractFloat), t.params, cases ); @@ -543,8 +543,8 @@ struct __modf_result_vec4_abstract { await run( t, abstractWholeBuilder(), - [TypeVec(4, TypeAbstractFloat)], - TypeVec(4, TypeAbstractFloat), + [Type.vec(4, Type.abstractFloat)], + Type.vec(4, Type.abstractFloat), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts index 4820f5884d69..7aca252464da 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/normalize.spec.ts @@ -1,14 +1,14 @@ export const description = ` Execution tests for the 'normalize' builtin function -T is AbstractFloat, f32, or f16 +T is abstract-float, f32, or f16 @const fn normalize(e: vecN ) -> vecN Returns a unit vector in the same direction as e. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -32,7 +32,7 @@ g.test('f32_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' ); - await run(t, builtin('normalize'), [TypeVec(2, TypeF32)], TypeVec(2, TypeF32), t.params, cases); + await run(t, builtin('normalize'), [Type.vec2f], Type.vec2f, t.params, cases); }); g.test('f32_vec3') @@ -43,7 +43,7 @@ g.test('f32_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' ); - await run(t, builtin('normalize'), [TypeVec(3, TypeF32)], TypeVec(3, TypeF32), t.params, cases); + await run(t, builtin('normalize'), [Type.vec3f], Type.vec3f, t.params, cases); }); g.test('f32_vec4') @@ -54,7 +54,7 @@ g.test('f32_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' ); - await run(t, builtin('normalize'), [TypeVec(4, TypeF32)], TypeVec(4, TypeF32), t.params, cases); + await run(t, builtin('normalize'), [Type.vec4f], Type.vec4f, t.params, cases); }); g.test('f16_vec2') @@ -68,7 +68,7 @@ g.test('f16_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const' ); - await run(t, builtin('normalize'), [TypeVec(2, TypeF16)], TypeVec(2, TypeF16), t.params, cases); + await run(t, builtin('normalize'), [Type.vec2h], Type.vec2h, t.params, cases); }); g.test('f16_vec3') @@ -82,7 +82,7 @@ g.test('f16_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const' ); - await run(t, builtin('normalize'), [TypeVec(3, TypeF16)], TypeVec(3, TypeF16), t.params, cases); + await run(t, builtin('normalize'), [Type.vec3h], Type.vec3h, t.params, cases); }); g.test('f16_vec4') @@ -96,5 +96,5 @@ g.test('f16_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const' ); - await run(t, builtin('normalize'), [TypeVec(4, TypeF16)], TypeVec(4, TypeF16), t.params, cases); + await run(t, builtin('normalize'), [Type.vec4h], Type.vec4h, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts index efa4041259eb..5ba69934279f 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16float.spec.ts @@ -6,7 +6,7 @@ which is then placed in bits 16 × i through 16 × i + 15 of the result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -24,5 +24,5 @@ g.test('pack') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('pack2x16float'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases); + await run(t, builtin('pack2x16float'), [Type.vec2f], Type.u32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts index 6479fe7ce070..1bcca2f73fe2 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16snorm.spec.ts @@ -8,15 +8,7 @@ bits 16 × i through 16 × i + 15 of the result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { kValue } from '../../../../../util/constants.js'; -import { - f32, - pack2x16snorm, - TypeF32, - TypeU32, - TypeVec, - u32, - vec2, -} from '../../../../../util/conversion.js'; +import { f32, pack2x16snorm, u32, vec2, Type } from '../../../../../util/conversion.js'; import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; import { Case } from '../../case.js'; import { allInputSources, run } from '../../expression.js'; @@ -52,5 +44,5 @@ g.test('pack') ]; }); - await run(t, builtin('pack2x16snorm'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases); + await run(t, builtin('pack2x16snorm'), [Type.vec2f], Type.u32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts index 303c555c1daf..334d1064824c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack2x16unorm.spec.ts @@ -8,15 +8,7 @@ bits 16 × i through 16 × i + 15 of the result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { kValue } from '../../../../../util/constants.js'; -import { - f32, - pack2x16unorm, - TypeF32, - TypeU32, - TypeVec, - u32, - vec2, -} from '../../../../../util/conversion.js'; +import { f32, pack2x16unorm, u32, vec2, Type } from '../../../../../util/conversion.js'; import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; import { Case } from '../../case.js'; import { allInputSources, run } from '../../expression.js'; @@ -52,5 +44,5 @@ g.test('pack') ]; }); - await run(t, builtin('pack2x16unorm'), [TypeVec(2, TypeF32)], TypeU32, t.params, cases); + await run(t, builtin('pack2x16unorm'), [Type.vec2f], Type.u32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts index b1c2607c940f..fbe362ea45e8 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4x8snorm.spec.ts @@ -8,16 +8,7 @@ bits 8 × i through 8 × i + 7 of the result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { kValue } from '../../../../../util/constants.js'; -import { - f32, - pack4x8snorm, - Scalar, - TypeF32, - TypeU32, - TypeVec, - u32, - vec4, -} from '../../../../../util/conversion.js'; +import { f32, pack4x8snorm, ScalarValue, u32, vec4, Type } from '../../../../../util/conversion.js'; import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; import { Case } from '../../case.js'; import { allInputSources, run } from '../../expression.js'; @@ -36,7 +27,12 @@ g.test('pack') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const makeCase = (vals: [number, number, number, number]): Case => { - const vals_f32 = new Array(4) as [Scalar, Scalar, Scalar, Scalar]; + const vals_f32 = new Array(4) as [ + ScalarValue, + ScalarValue, + ScalarValue, + ScalarValue, + ]; for (const idx in vals) { vals[idx] = quantizeToF32(vals[idx]); vals_f32[idx] = f32(vals[idx]); @@ -57,5 +53,5 @@ g.test('pack') ]; }); - await run(t, builtin('pack4x8snorm'), [TypeVec(4, TypeF32)], TypeU32, t.params, cases); + await run(t, builtin('pack4x8snorm'), [Type.vec4f], Type.u32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts index fa67e8f4ae3d..c7d62e722b24 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4x8unorm.spec.ts @@ -8,16 +8,7 @@ bits 8 × i through 8 × i + 7 of the result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; import { kValue } from '../../../../../util/constants.js'; -import { - f32, - pack4x8unorm, - Scalar, - TypeF32, - TypeU32, - TypeVec, - u32, - vec4, -} from '../../../../../util/conversion.js'; +import { f32, pack4x8unorm, ScalarValue, u32, vec4, Type } from '../../../../../util/conversion.js'; import { quantizeToF32, vectorF32Range } from '../../../../../util/math.js'; import { Case } from '../../case.js'; import { allInputSources, run } from '../../expression.js'; @@ -36,7 +27,12 @@ g.test('pack') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const makeCase = (vals: [number, number, number, number]): Case => { - const vals_f32 = new Array(4) as [Scalar, Scalar, Scalar, Scalar]; + const vals_f32 = new Array(4) as [ + ScalarValue, + ScalarValue, + ScalarValue, + ScalarValue, + ]; for (const idx in vals) { vals[idx] = quantizeToF32(vals[idx]); vals_f32[idx] = f32(vals[idx]); @@ -57,5 +53,5 @@ g.test('pack') ]; }); - await run(t, builtin('pack4x8unorm'), [TypeVec(4, TypeF32)], TypeU32, t.params, cases); + await run(t, builtin('pack4x8unorm'), [Type.vec4f], Type.u32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts index 8caae09eba7c..94aa67f0ae85 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4xI8.spec.ts @@ -8,7 +8,7 @@ Component e[i] of the input is mapped to bits (8 * i) through (8 * (i + 7)) of t import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeI32, TypeU32, TypeVec, u32, toVector, i32 } from '../../../../../util/conversion.js'; +import { u32, toVector, i32, Type } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -65,5 +65,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('pack4xI8'), [TypeVec(4, TypeI32)], TypeU32, cfg, cases); + await run(t, builtin('pack4xI8'), [Type.vec4i], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts index 0ab63d89beef..4968ed1e04de 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4xI8Clamp.spec.ts @@ -9,7 +9,7 @@ result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeI32, TypeU32, TypeVec, u32, toVector, i32 } from '../../../../../util/conversion.js'; +import { u32, toVector, i32, Type } from '../../../../../util/conversion.js'; import { clamp } from '../../../../../util/math.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -69,5 +69,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('pack4xI8Clamp'), [TypeVec(4, TypeI32)], TypeU32, cfg, cases); + await run(t, builtin('pack4xI8Clamp'), [Type.vec4i], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts index 0d4d2223f05d..9d08e88d4432 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4xU8.spec.ts @@ -8,7 +8,7 @@ Component e[i] of the input is mapped to bits (8 * i) through (8 * (i + 7)) of t import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, TypeVec, u32, toVector } from '../../../../../util/conversion.js'; +import { u32, toVector, Type } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -50,5 +50,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('pack4xU8'), [TypeVec(4, TypeU32)], TypeU32, cfg, cases); + await run(t, builtin('pack4xU8'), [Type.vec4u], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts index 0f70cb6f8c87..ffecf9b8779c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pack4xU8Clamp.spec.ts @@ -9,7 +9,7 @@ result. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, TypeVec, u32, toVector } from '../../../../../util/conversion.js'; +import { u32, toVector, Type } from '../../../../../util/conversion.js'; import { clamp } from '../../../../../util/math.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -53,5 +53,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('pack4xU8Clamp'), [TypeVec(4, TypeU32)], TypeU32, cfg, cases); + await run(t, builtin('pack4xU8Clamp'), [Type.vec4u], Type.u32, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts index deea077e7a31..476c591a358e 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/pow.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'pow' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn pow(e1: T ,e2: T ) -> T Returns e1 raised to the power e2. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns e1 raised to the power e2. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('pow'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('pow'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('pow'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('pow'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts index ee34ddd17479..0aa9669e93b3 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts @@ -10,7 +10,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -26,5 +26,5 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('quantizeToF16'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('quantizeToF16'), [Type.f32], Type.f32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts index afc7da6d8cdb..a405807ec044 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/radians.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'radians' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn radians(e1: T ) -> T Converts degrees to radians, approximating e1 * π / 180. @@ -10,7 +10,7 @@ Component-wise when T is a vector import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -31,8 +31,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('radians'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -46,7 +46,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('radians'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('radians'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -60,5 +60,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('radians'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('radians'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts index 0908bf04be5c..9c1a25f22a60 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/reflect.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'reflect' builtin function -T is vecN, vecN, or vecN +T is vecN, vecN, or vecN @const fn reflect(e1: T, e2: T ) -> T For the incident vector e1 and surface orientation e2, returns the reflection direction e1-2*dot(e2,e1)*e2. @@ -9,7 +9,7 @@ direction e1-2*dot(e2,e1)*e2. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -31,14 +31,7 @@ g.test('f32_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec2_const' : 'f32_vec2_non_const' ); - await run( - t, - builtin('reflect'), - [TypeVec(2, TypeF32), TypeVec(2, TypeF32)], - TypeVec(2, TypeF32), - t.params, - cases - ); + await run(t, builtin('reflect'), [Type.vec2f, Type.vec2f], Type.vec2f, t.params, cases); }); g.test('f32_vec3') @@ -49,14 +42,7 @@ g.test('f32_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec3_const' : 'f32_vec3_non_const' ); - await run( - t, - builtin('reflect'), - [TypeVec(3, TypeF32), TypeVec(3, TypeF32)], - TypeVec(3, TypeF32), - t.params, - cases - ); + await run(t, builtin('reflect'), [Type.vec3f, Type.vec3f], Type.vec3f, t.params, cases); }); g.test('f32_vec4') @@ -67,14 +53,7 @@ g.test('f32_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f32_vec4_const' : 'f32_vec4_non_const' ); - await run( - t, - builtin('reflect'), - [TypeVec(4, TypeF32), TypeVec(4, TypeF32)], - TypeVec(4, TypeF32), - t.params, - cases - ); + await run(t, builtin('reflect'), [Type.vec4f, Type.vec4f], Type.vec4f, t.params, cases); }); g.test('f16_vec2') @@ -88,14 +67,7 @@ g.test('f16_vec2') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec2_const' : 'f16_vec2_non_const' ); - await run( - t, - builtin('reflect'), - [TypeVec(2, TypeF16), TypeVec(2, TypeF16)], - TypeVec(2, TypeF16), - t.params, - cases - ); + await run(t, builtin('reflect'), [Type.vec2h, Type.vec2h], Type.vec2h, t.params, cases); }); g.test('f16_vec3') @@ -109,14 +81,7 @@ g.test('f16_vec3') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec3_const' : 'f16_vec3_non_const' ); - await run( - t, - builtin('reflect'), - [TypeVec(3, TypeF16), TypeVec(3, TypeF16)], - TypeVec(3, TypeF16), - t.params, - cases - ); + await run(t, builtin('reflect'), [Type.vec3h, Type.vec3h], Type.vec3h, t.params, cases); }); g.test('f16_vec4') @@ -130,12 +95,5 @@ g.test('f16_vec4') const cases = await d.get( t.params.inputSource === 'const' ? 'f16_vec4_const' : 'f16_vec4_non_const' ); - await run( - t, - builtin('reflect'), - [TypeVec(4, TypeF16), TypeVec(4, TypeF16)], - TypeVec(4, TypeF16), - t.params, - cases - ); + await run(t, builtin('reflect'), [Type.vec4h, Type.vec4h], Type.vec4h, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts index 67eb8ac94658..466c9be7d922 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/refract.spec.ts @@ -2,7 +2,7 @@ export const description = ` Execution tests for the 'refract' builtin function T is vecN -I is AbstractFloat, f32, or f16 +I is abstract-float, f32, or f16 @const fn refract(e1: T ,e2: T ,e3: I ) -> T For the incident vector e1 and surface normal e2, and the ratio of indices of refraction e3, let k = 1.0 -e3*e3* (1.0 - dot(e2,e1) * dot(e2,e1)). @@ -12,7 +12,7 @@ vector e3*e1- (e3* dot(e2,e1) + sqrt(k)) *e2. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -37,8 +37,8 @@ g.test('f32_vec2') await run( t, builtin('refract'), - [TypeVec(2, TypeF32), TypeVec(2, TypeF32), TypeF32], - TypeVec(2, TypeF32), + [Type.vec2f, Type.vec2f, Type.f32], + Type.vec2f, t.params, cases ); @@ -55,8 +55,8 @@ g.test('f32_vec3') await run( t, builtin('refract'), - [TypeVec(3, TypeF32), TypeVec(3, TypeF32), TypeF32], - TypeVec(3, TypeF32), + [Type.vec3f, Type.vec3f, Type.f32], + Type.vec3f, t.params, cases ); @@ -73,8 +73,8 @@ g.test('f32_vec4') await run( t, builtin('refract'), - [TypeVec(4, TypeF32), TypeVec(4, TypeF32), TypeF32], - TypeVec(4, TypeF32), + [Type.vec4f, Type.vec4f, Type.f32], + Type.vec4f, t.params, cases ); @@ -94,8 +94,8 @@ g.test('f16_vec2') await run( t, builtin('refract'), - [TypeVec(2, TypeF16), TypeVec(2, TypeF16), TypeF16], - TypeVec(2, TypeF16), + [Type.vec2h, Type.vec2h, Type.f16], + Type.vec2h, t.params, cases ); @@ -115,8 +115,8 @@ g.test('f16_vec3') await run( t, builtin('refract'), - [TypeVec(3, TypeF16), TypeVec(3, TypeF16), TypeF16], - TypeVec(3, TypeF16), + [Type.vec3h, Type.vec3h, Type.f16], + Type.vec3h, t.params, cases ); @@ -136,8 +136,8 @@ g.test('f16_vec4') await run( t, builtin('refract'), - [TypeVec(4, TypeF16), TypeVec(4, TypeF16), TypeF16], - TypeVec(4, TypeF16), + [Type.vec4h, Type.vec4h, Type.f16], + Type.vec4h, t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts index 6acb35982216..e235e62a5284 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts @@ -10,7 +10,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, u32Bits, TypeI32, i32Bits } from '../../../../../util/conversion.js'; +import { u32Bits, i32Bits, Type } from '../../../../../util/conversion.js'; import { allInputSources, Config, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -26,7 +26,7 @@ g.test('u32') .fn(async t => { const cfg: Config = t.params; // prettier-ignore - await run(t, builtin('reverseBits'), [TypeU32], TypeU32, cfg, [ + await run(t, builtin('reverseBits'), [Type.u32], Type.u32, cfg, [ // Zero { input: u32Bits(0b00000000000000000000000000000000), expected: u32Bits(0b00000000000000000000000000000000) }, @@ -142,7 +142,7 @@ g.test('i32') .fn(async t => { const cfg: Config = t.params; // prettier-ignore - await run(t, builtin('reverseBits'), [TypeI32], TypeI32, cfg, [ + await run(t, builtin('reverseBits'), [Type.i32], Type.i32, cfg, [ // Zero { input: i32Bits(0b00000000000000000000000000000000), expected: i32Bits(0b00000000000000000000000000000000) }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts index 8d215b952d59..eeaf41b38185 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/round.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'round' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn round(e: T) -> T Result is the integer k nearest to e, as a floating point value. @@ -12,7 +12,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -33,8 +33,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('round'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -48,7 +48,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('round'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('round'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -62,5 +62,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('round'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('round'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts index 216ffb734578..79c61e4eec6c 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/saturate.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'saturate' builtin function -S is AbstractFloat, f32, or f16 +S is abstract-float, f32, or f16 T is S or vecN @const fn saturate(e: T) -> T Returns clamp(e, 0.0, 1.0). Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns clamp(e, 0.0, 1.0). Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -30,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('saturate'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -44,7 +44,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('saturate'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('saturate'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -58,5 +58,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('saturate'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('saturate'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts index aa5ee45e84e2..63accbc2d466 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/select.spec.ts @@ -14,12 +14,6 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { GPUTest } from '../../../../../gpu_test.js'; import { VectorType, - TypeVec, - TypeBool, - TypeF32, - TypeF16, - TypeI32, - TypeU32, f32, f16, i32, @@ -31,10 +25,9 @@ import { vec3, vec4, abstractFloat, - TypeAbstractFloat, - TypeAbstractInt, abstractInt, - Scalar, + ScalarValue, + Type, } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { run, allInputSources } from '../../expression.js'; @@ -51,41 +44,41 @@ type scalarKind = 'b' | 'af' | 'f' | 'h' | 'ai' | 'i' | 'u'; const dataType = { b: { - type: TypeBool, + type: Type.bool, scalar_builder: makeBool, shader_builder: builtin('select'), }, af: { - type: TypeAbstractFloat, + type: Type.abstractFloat, scalar_builder: abstractFloat, shader_builder: abstractFloatBuiltin('select'), }, f: { - type: TypeF32, + type: Type.f32, scalar_builder: f32, shader_builder: builtin('select'), }, h: { - type: TypeF16, + type: Type.f16, scalar_builder: f16, shader_builder: builtin('select'), }, ai: { - type: TypeAbstractInt, + type: Type.abstractInt, // Only ints are used in the tests below, so the conversion to bigint will // be safe. If a non-int is passed in this will Error. - scalar_builder: (v: number): Scalar => { + scalar_builder: (v: number): ScalarValue => { return abstractInt(BigInt(v)); }, shader_builder: abstractIntBuiltin('select'), }, i: { - type: TypeI32, + type: Type.i32, scalar_builder: i32, shader_builder: builtin('select'), }, u: { - type: TypeU32, + type: Type.u32, scalar_builder: u32, shader_builder: builtin('select'), }, @@ -136,21 +129,21 @@ g.test('scalar') ], }, vec2: { - type: TypeVec(2, componentType), + type: Type.vec(2, componentType), cases: [ { input: [v2a, v2b, False], expected: v2a }, { input: [v2a, v2b, True], expected: v2b }, ], }, vec3: { - type: TypeVec(3, componentType), + type: Type.vec(3, componentType), cases: [ { input: [v3a, v3b, False], expected: v3a }, { input: [v3a, v3b, True], expected: v3b }, ], }, vec4: { - type: TypeVec(4, componentType), + type: Type.vec(4, componentType), cases: [ { input: [v4a, v4b, False], expected: v4a }, { input: [v4a, v4b, True], expected: v4b }, @@ -162,7 +155,7 @@ g.test('scalar') await run( t, dataType[t.params.component as scalarKind].shader_builder, - [overload.type, overload.type, TypeBool], + [overload.type, overload.type, Type.bool], overload.type, t.params, overload.cases @@ -205,8 +198,8 @@ g.test('vector') const a = vec2(scalars[0], scalars[1]); const b = vec2(scalars[4], scalars[5]); tests = { - dataType: TypeVec(2, componentType), - boolType: TypeVec(2, TypeBool), + dataType: Type.vec(2, componentType), + boolType: Type.vec(2, Type.bool), cases: [ { input: [a, b, vec2(F, F)], expected: vec2(a.x, a.y) }, { input: [a, b, vec2(F, T)], expected: vec2(a.x, b.y) }, @@ -220,8 +213,8 @@ g.test('vector') const a = vec3(scalars[0], scalars[1], scalars[2]); const b = vec3(scalars[4], scalars[5], scalars[6]); tests = { - dataType: TypeVec(3, componentType), - boolType: TypeVec(3, TypeBool), + dataType: Type.vec(3, componentType), + boolType: Type.vec(3, Type.bool), cases: [ { input: [a, b, vec3(F, F, F)], expected: vec3(a.x, a.y, a.z) }, { input: [a, b, vec3(F, F, T)], expected: vec3(a.x, a.y, b.z) }, @@ -239,8 +232,8 @@ g.test('vector') const a = vec4(scalars[0], scalars[1], scalars[2], scalars[3]); const b = vec4(scalars[4], scalars[5], scalars[6], scalars[7]); tests = { - dataType: TypeVec(4, componentType), - boolType: TypeVec(4, TypeBool), + dataType: Type.vec(4, componentType), + boolType: Type.vec(4, Type.bool), cases: [ { input: [a, b, vec4(F, F, F, F)], expected: vec4(a.x, a.y, a.z, a.w) }, { input: [a, b, vec4(F, F, F, T)], expected: vec4(a.x, a.y, a.z, b.w) }, diff --git a/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts index 7872377dc42c..f7d2e3ccfd5a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/sign.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'sign' builtin function -S is AbstractFloat, AbstractInt, i32, f32, f16 +S is abstract-float, Type.abstractInt, i32, f32, f16 T is S or vecN @const fn sign(e: T ) -> T Returns the sign of e. Component-wise when T is a vector. @@ -9,13 +9,7 @@ Returns the sign of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { - TypeAbstractFloat, - TypeAbstractInt, - TypeF16, - TypeF32, - TypeI32, -} from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, abstractIntBuiltin, builtin } from './builtin.js'; @@ -36,8 +30,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('sign'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -53,7 +47,7 @@ g.test('abstract_int') ) .fn(async t => { const cases = await d.get('abstract_int'); - await run(t, abstractIntBuiltin('sign'), [TypeAbstractInt], TypeAbstractInt, t.params, cases); + await run(t, abstractIntBuiltin('sign'), [Type.abstractInt], Type.abstractInt, t.params, cases); }); g.test('i32') @@ -64,7 +58,7 @@ g.test('i32') ) .fn(async t => { const cases = await d.get('i32'); - await run(t, builtin('sign'), [TypeI32], TypeI32, t.params, cases); + await run(t, builtin('sign'), [Type.i32], Type.i32, t.params, cases); }); g.test('f32') @@ -75,7 +69,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('sign'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('sign'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -89,5 +83,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('sign'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('sign'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts index e9420fc0880d..4d649b4db5eb 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'sin' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn sin(e: T ) -> T Returns the sine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the sine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -39,7 +39,7 @@ TODO(#792): Decide what the ground-truth is for these tests. [1] ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('sin'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('sin'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -53,5 +53,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('sin'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('sin'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts index 0070befa47fc..f59616d9af12 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/sinh.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'sinh' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn sinh(e: T ) -> T Returns the hyperbolic sine of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the hyperbolic sine of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('sinh'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('sinh'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('sinh'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('sinh'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts index a32c112b8a94..1404061d02f7 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'smoothstep' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn smoothstep(low: T , high: T , x: T ) -> T Returns the smooth Hermite interpolation between 0 and 1. @@ -11,7 +11,7 @@ For scalar T, the result is t * t * (3.0 - 2.0 * t), where t = clamp((x - low) / import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -35,7 +35,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('smoothstep'), [TypeF32, TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('smoothstep'), [Type.f32, Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -49,5 +49,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('smoothstep'), [TypeF16, TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('smoothstep'), [Type.f16, Type.f16, Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts index cfd379fea660..ee4197d70f7d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/sqrt.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'sqrt' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn sqrt(e: T ) -> T Returns the square root of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the square root of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, builtin('sqrt'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('sqrt'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f16_const' : 'f16_non_const'); - await run(t, builtin('sqrt'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('sqrt'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts index c55a48812e9c..caf75e6f623a 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/step.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'step' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn step(edge: T ,x: T ) -> T Returns 1.0 if edge ≤ x, and 0.0 otherwise. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns 1.0 if edge ≤ x, and 0.0 otherwise. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('step'), [TypeF32, TypeF32], TypeF32, t.params, cases); + await run(t, builtin('step'), [Type.f32, Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('step'), [TypeF16, TypeF16], TypeF16, t.params, cases); + await run(t, builtin('step'), [Type.f16, Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts index f86b3c959a16..4ddef19fe168 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/tan.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'tan' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn tan(e: T ) -> T Returns the tangent of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the tangent of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('tan'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('tan'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('tan'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('tan'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts index 496c7bcf1b11..19f4ff55318b 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/tanh.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'tanh' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn tanh(e: T ) -> T Returns the hyperbolic tangent of e. Component-wise when T is a vector. @@ -9,7 +9,7 @@ Returns the hyperbolic tangent of e. Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -33,7 +33,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('tanh'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('tanh'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -47,5 +47,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('tanh'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('tanh'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts index 90fa226a01e3..7a96906cfa71 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/transpose.spec.ts @@ -1,14 +1,14 @@ export const description = ` Execution tests for the 'transpose' builtin function -T is AbstractFloat, f32, or f16 +T is abstract-float, f32, or f16 @const transpose(e: matRxC ) -> matCxR Returns the transpose of e. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32, TypeMat } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -32,8 +32,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('transpose'), - [TypeMat(cols, rows, TypeAbstractFloat)], - TypeMat(rows, cols, TypeAbstractFloat), + [Type.mat(cols, rows, Type.abstractFloat)], + Type.mat(rows, cols, Type.abstractFloat), t.params, cases ); @@ -59,8 +59,8 @@ g.test('f32') await run( t, builtin('transpose'), - [TypeMat(cols, rows, TypeF32)], - TypeMat(rows, cols, TypeF32), + [Type.mat(cols, rows, Type.f32)], + Type.mat(rows, cols, Type.f32), t.params, cases ); @@ -89,8 +89,8 @@ g.test('f16') await run( t, builtin('transpose'), - [TypeMat(cols, rows, TypeF16)], - TypeMat(rows, cols, TypeF16), + [Type.mat(cols, rows, Type.f16)], + Type.mat(rows, cols, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts index 022248ae5b97..9d05709fc686 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/trunc.spec.ts @@ -1,7 +1,7 @@ export const description = ` Execution tests for the 'trunc' builtin function -S is AbstractFloat, f32, f16 +S is abstract-float, f32, f16 T is S or vecN @const fn trunc(e: T ) -> T Returns the nearest whole number whose absolute value is less than or equal to e. @@ -10,7 +10,7 @@ Component-wise when T is a vector. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -31,8 +31,8 @@ g.test('abstract_float') await run( t, abstractFloatBuiltin('trunc'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases ); @@ -46,7 +46,7 @@ g.test('f32') ) .fn(async t => { const cases = await d.get('f32'); - await run(t, builtin('trunc'), [TypeF32], TypeF32, t.params, cases); + await run(t, builtin('trunc'), [Type.f32], Type.f32, t.params, cases); }); g.test('f16') @@ -60,5 +60,5 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, builtin('trunc'), [TypeF16], TypeF16, t.params, cases); + await run(t, builtin('trunc'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts index da1819f8ef6e..e145afca50be 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16float.spec.ts @@ -7,7 +7,7 @@ interpretation of bits 16×i through 16×i+15 of e as an IEEE-754 binary16 value import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,5 +25,5 @@ g.test('unpack') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); - await run(t, builtin('unpack2x16float'), [TypeU32], TypeVec(2, TypeF32), t.params, cases); + await run(t, builtin('unpack2x16float'), [Type.u32], Type.vec2f, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts index a88702bcce70..059a5664f9e4 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16snorm.spec.ts @@ -7,7 +7,7 @@ of bits 16×i through 16×i+15 of e as a twos-complement signed integer. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,5 +25,5 @@ g.test('unpack') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); - await run(t, builtin('unpack2x16snorm'), [TypeU32], TypeVec(2, TypeF32), t.params, cases); + await run(t, builtin('unpack2x16snorm'), [Type.u32], Type.vec2f, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts index 325e0a9735b5..971f384023ef 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack2x16unorm.spec.ts @@ -7,7 +7,7 @@ Component i of the result is v ÷ 65535, where v is the interpretation of bits import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,5 +25,5 @@ g.test('unpack') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); - await run(t, builtin('unpack2x16unorm'), [TypeU32], TypeVec(2, TypeF32), t.params, cases); + await run(t, builtin('unpack2x16unorm'), [Type.u32], Type.vec2f, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts index 3b48cca4539f..d6e533621b14 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8snorm.spec.ts @@ -7,7 +7,7 @@ bits 8×i through 8×i+7 of e as a twos-complement signed integer. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,5 +25,5 @@ g.test('unpack') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); - await run(t, builtin('unpack4x8snorm'), [TypeU32], TypeVec(4, TypeF32), t.params, cases); + await run(t, builtin('unpack4x8snorm'), [Type.u32], Type.vec4f, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts index 3e57f2592ee5..026043da1a97 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4x8unorm.spec.ts @@ -7,7 +7,7 @@ through 8×i+7 of e as an unsigned integer. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeF32, TypeU32, TypeVec } from '../../../../../util/conversion.js'; +import { Type } from '../../../../../util/conversion.js'; import { allInputSources, run } from '../../expression.js'; import { builtin } from './builtin.js'; @@ -25,5 +25,5 @@ g.test('unpack') .params(u => u.combine('inputSource', allInputSources)) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); - await run(t, builtin('unpack4x8unorm'), [TypeU32], TypeVec(4, TypeF32), t.params, cases); + await run(t, builtin('unpack4x8unorm'), [Type.u32], Type.vec4f, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts index 6253ad7bebca..4f7e4ed3a70d 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4xI8.spec.ts @@ -8,7 +8,7 @@ with sign extension. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeI32, TypeU32, TypeVec, u32, toVector, i32 } from '../../../../../util/conversion.js'; +import { u32, toVector, i32, Type } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -52,5 +52,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('unpack4xI8'), [TypeU32], TypeVec(4, TypeI32), cfg, cases); + await run(t, builtin('unpack4xI8'), [Type.u32], Type.vec4i, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts b/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts index 3c13f32fecc6..09849030eb10 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/unpack4xU8.spec.ts @@ -8,7 +8,7 @@ with zero extension. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { TypeU32, TypeVec, u32, toVector } from '../../../../../util/conversion.js'; +import { u32, toVector, Type } from '../../../../../util/conversion.js'; import { Case } from '../../case.js'; import { allInputSources, Config, run } from '../../expression.js'; @@ -44,5 +44,5 @@ g.test('basic') return [makeCase(v)]; }); - await run(t, builtin('unpack4xU8'), [TypeU32], TypeVec(4, TypeU32), cfg, cases); + await run(t, builtin('unpack4xU8'), [Type.u32], Type.vec4u, cfg, cases); }); diff --git a/src/webgpu/shader/execution/expression/case.ts b/src/webgpu/shader/execution/expression/case.ts index 436f0a6964ad..063b58da4267 100644 --- a/src/webgpu/shader/execution/expression/case.ts +++ b/src/webgpu/shader/execution/expression/case.ts @@ -1,7 +1,14 @@ import { crc32 } from '../../../../common/util/crc32.js'; import { ROArrayArray } from '../../../../common/util/types.js'; import { assert } from '../../../../common/util/util.js'; -import { abstractInt, i32, ScalarBuilder, u32, Value, Vector } from '../../../util/conversion.js'; +import { + abstractInt, + i32, + ScalarBuilder, + u32, + Value, + VectorValue, +} from '../../../util/conversion.js'; import { cartesianProduct, QuantizeFunc, @@ -103,8 +110,8 @@ function makeScalarVectorBinaryToVectorCase( return undefined; } return { - input: [scalarize(scalar), new Vector(vector.map(scalarize))], - expected: new Vector(result.filter(notUndefined).map(scalarize)), + input: [scalarize(scalar), new VectorValue(vector.map(scalarize))], + expected: new VectorValue(result.filter(notUndefined).map(scalarize)), }; } @@ -154,8 +161,8 @@ function makeVectorScalarBinaryToVectorCase( return undefined; } return { - input: [new Vector(vector.map(scalarize)), scalarize(scalar)], - expected: new Vector(result.filter(notUndefined).map(scalarize)), + input: [new VectorValue(vector.map(scalarize)), scalarize(scalar)], + expected: new VectorValue(result.filter(notUndefined).map(scalarize)), }; } @@ -357,8 +364,8 @@ function makeVectorVectorToScalarCase( return { input: [ - new Vector(param0_quantized.map(scalarize)), - new Vector(param1_quantized.map(scalarize)), + new VectorValue(param0_quantized.map(scalarize)), + new VectorValue(param1_quantized.map(scalarize)), ], expected: scalarize(result), }; diff --git a/src/webgpu/shader/execution/expression/case_cache.ts b/src/webgpu/shader/execution/expression/case_cache.ts index 4321475bf83a..345183c5d506 100644 --- a/src/webgpu/shader/execution/expression/case_cache.ts +++ b/src/webgpu/shader/execution/expression/case_cache.ts @@ -3,11 +3,11 @@ import { unreachable } from '../../../../common/util/util.js'; import BinaryStream from '../../../util/binary_stream.js'; import { deserializeComparator, serializeComparator } from '../../../util/compare.js'; import { - Matrix, + MatrixValue, Value, - Vector, + VectorValue, deserializeValue, - isScalar, + isScalarValue, serializeValue, } from '../../../util/conversion.js'; import { @@ -31,7 +31,7 @@ enum SerializedExpectationKind { /** serializeExpectation() serializes an Expectation to a BinaryStream */ export function serializeExpectation(s: BinaryStream, e: Expectation) { - if (isScalar(e) || e instanceof Vector || e instanceof Matrix) { + if (isScalarValue(e) || e instanceof VectorValue || e instanceof MatrixValue) { s.writeU8(SerializedExpectationKind.Value); serializeValue(s, e); return; diff --git a/src/webgpu/shader/execution/expression/expectation.ts b/src/webgpu/shader/execution/expression/expectation.ts index e56577580c52..d43ab5279116 100644 --- a/src/webgpu/shader/execution/expression/expectation.ts +++ b/src/webgpu/shader/execution/expression/expectation.ts @@ -1,6 +1,6 @@ import { ROArrayArray } from '../../../../common/util/types.js'; import { Comparator, compare } from '../../../util/compare.js'; -import { Matrix, Value, Vector, isScalar } from '../../../util/conversion.js'; +import { MatrixValue, Value, VectorValue, isScalarValue } from '../../../util/conversion.js'; import { FPInterval } from '../../../util/floating_point.js'; export type Expectation = @@ -14,9 +14,9 @@ export type Expectation = export function isComparator(e: Expectation): e is Comparator { return !( e instanceof FPInterval || - isScalar(e) || - e instanceof Vector || - e instanceof Matrix || + isScalarValue(e) || + e instanceof VectorValue || + e instanceof MatrixValue || e instanceof Array ); } diff --git a/src/webgpu/shader/execution/expression/expression.ts b/src/webgpu/shader/execution/expression/expression.ts index c622e6c5fdde..21e1d40eab90 100644 --- a/src/webgpu/shader/execution/expression/expression.ts +++ b/src/webgpu/shader/execution/expression/expression.ts @@ -5,14 +5,12 @@ import { Comparator, ComparatorImpl } from '../../../util/compare.js'; import { kValue } from '../../../util/constants.js'; import { MatrixType, - Scalar, + ScalarValue, ScalarType, Type, - TypeU32, - TypeVec, - Value, - Vector, VectorType, + Value, + VectorValue, isAbstractType, scalarTypeOf, } from '../../../util/conversion.js'; @@ -158,11 +156,11 @@ function storageType(ty: Type): Type { `Custom handling is implemented for 'abstract-float' values` ); if (ty.kind === 'bool') { - return TypeU32; + return Type.u32; } } if (ty instanceof VectorType) { - return TypeVec(ty.width, storageType(ty.elementType) as ScalarType); + return Type.vec(ty.width, storageType(ty.elementType) as ScalarType); } return ty; } @@ -1213,22 +1211,22 @@ function packScalarsToVector( } const packedCases: Array = []; - const packedParameterTypes = parameterTypes.map(p => TypeVec(vectorWidth, p as ScalarType)); - const packedResultType = new VectorType(vectorWidth, resultType); + const packedParameterTypes = parameterTypes.map(p => Type.vec(vectorWidth, p as ScalarType)); + const packedResultType = Type.vec(vectorWidth, resultType); const clampCaseIdx = (idx: number) => Math.min(idx, cases.length - 1); let caseIdx = 0; while (caseIdx < cases.length) { // Construct the vectorized inputs from the scalar cases - const packedInputs = new Array(parameterTypes.length); + const packedInputs = new Array(parameterTypes.length); for (let paramIdx = 0; paramIdx < parameterTypes.length; paramIdx++) { - const inputElements = new Array(vectorWidth); + const inputElements = new Array(vectorWidth); for (let i = 0; i < vectorWidth; i++) { const input = cases[clampCaseIdx(caseIdx + i)].input; - inputElements[i] = (input instanceof Array ? input[paramIdx] : input) as Scalar; + inputElements[i] = (input instanceof Array ? input[paramIdx] : input) as ScalarValue; } - packedInputs[paramIdx] = new Vector(inputElements); + packedInputs[paramIdx] = new VectorValue(inputElements); } // Gather the comparators for the packed cases @@ -1242,7 +1240,7 @@ function packScalarsToVector( const gElements = new Array(vectorWidth); const eElements = new Array(vectorWidth); for (let i = 0; i < vectorWidth; i++) { - const d = cmp_impls[i]((got as Vector).elements[i]); + const d = cmp_impls[i]((got as VectorValue).elements[i]); matched = matched && d.matched; gElements[i] = d.got; eElements[i] = d.expected; diff --git a/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts index 005ddc6448ea..686d4b7c4ac2 100644 --- a/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/af_arithmetic.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for AbstractFloat arithmetic unary expression operations +Execution Tests for Type.abstractFloat arithmetic unary expression operations `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './af_arithmetic.cache.js'; @@ -30,8 +30,8 @@ Accuracy: Correctly rounded await run( t, abstractFloatUnary('-'), - [TypeAbstractFloat], - TypeAbstractFloat, + [Type.abstractFloat], + Type.abstractFloat, t.params, cases, 1 diff --git a/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts b/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts index 76e96fe80354..001c47e117bd 100644 --- a/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/af_assignment.spec.ts @@ -4,7 +4,7 @@ Execution Tests for assignment of AbstractFloats import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractFloat, TypeF16, TypeF32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, abstractFloatShaderBuilder, @@ -35,7 +35,15 @@ testing that extracting abstract floats works .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('abstract'); - await run(t, abstract_assignment(), [TypeAbstractFloat], TypeAbstractFloat, t.params, cases, 1); + await run( + t, + abstract_assignment(), + [Type.abstractFloat], + Type.abstractFloat, + t.params, + cases, + 1 + ); }); g.test('f32') @@ -48,7 +56,7 @@ concretizing to f32 .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('f32'); - await run(t, concrete_assignment(), [TypeAbstractFloat], TypeF32, t.params, cases); + await run(t, concrete_assignment(), [Type.abstractFloat], Type.f32, t.params, cases); }); g.test('f16') @@ -64,5 +72,5 @@ concretizing to f16 .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('f16'); - await run(t, concrete_assignment(), [TypeAbstractFloat], TypeF16, t.params, cases); + await run(t, concrete_assignment(), [Type.abstractFloat], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/ai_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/ai_arithmetic.spec.ts index bc50be4a7b62..625a38b2d5af 100644 --- a/src/webgpu/shader/execution/expression/unary/ai_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/ai_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the abstract integer arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractInt } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { onlyConstInputSource, run } from '../expression.js'; import { d } from './ai_arithmetic.cache.js'; @@ -26,5 +26,5 @@ Expression: -x ) .fn(async t => { const cases = await d.get('negation'); - await run(t, abstractIntUnary('-'), [TypeAbstractInt], TypeAbstractInt, t.params, cases); + await run(t, abstractIntUnary('-'), [Type.abstractInt], Type.abstractInt, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts b/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts index f7915df97adc..fe1ba2d9fc12 100644 --- a/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/ai_assignment.spec.ts @@ -4,7 +4,7 @@ Execution Tests for assignment of AbstractInts import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeAbstractInt, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, abstractIntShaderBuilder, @@ -35,7 +35,7 @@ testing that extracting abstract ints works .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('abstract'); - await run(t, abstract_assignment(), [TypeAbstractInt], TypeAbstractInt, t.params, cases, 1); + await run(t, abstract_assignment(), [Type.abstractInt], Type.abstractInt, t.params, cases, 1); }); g.test('i32') @@ -48,7 +48,7 @@ concretizing to i32 .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('i32'); - await run(t, concrete_assignment(), [TypeAbstractInt], TypeI32, t.params, cases); + await run(t, concrete_assignment(), [Type.abstractInt], Type.i32, t.params, cases); }); g.test('u32') @@ -61,5 +61,5 @@ concretizing to u32 .params(u => u.combine('inputSource', onlyConstInputSource)) .fn(async t => { const cases = await d.get('u32'); - await run(t, concrete_assignment(), [TypeAbstractInt], TypeU32, t.params, cases); + await run(t, concrete_assignment(), [Type.abstractInt], Type.u32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/ai_complement.spec.ts b/src/webgpu/shader/execution/expression/unary/ai_complement.spec.ts index 919321e207ce..507ae219cb29 100644 --- a/src/webgpu/shader/execution/expression/unary/ai_complement.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/ai_complement.spec.ts @@ -1,10 +1,10 @@ export const description = ` -Execution Tests for the AbstractInt bitwise complement operation +Execution Tests for the Type.abstractInt bitwise complement operation `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { abstractInt, TypeAbstractInt } from '../../../../util/conversion.js'; +import { abstractInt, Type } from '../../../../util/conversion.js'; import { fullI64Range } from '../../../../util/math.js'; import { onlyConstInputSource, run } from '../expression.js'; @@ -28,5 +28,5 @@ Expression: ~x const cases = fullI64Range().map(e => { return { input: abstractInt(e), expected: abstractInt(~e) }; }); - await run(t, abstractIntUnary('~'), [TypeAbstractInt], TypeAbstractInt, t.params, cases); + await run(t, abstractIntUnary('~'), [Type.abstractInt], Type.abstractInt, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts b/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts index f64ec1cd4180..5b16c49c421b 100644 --- a/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts +++ b/src/webgpu/shader/execution/expression/unary/bool_conversion.cache.ts @@ -1,5 +1,5 @@ import { anyOf } from '../../../../util/compare.js'; -import { Scalar, bool, f16, f32, i32, u32 } from '../../../../util/conversion.js'; +import { ScalarValue, bool, f16, f32, i32, u32 } from '../../../../util/conversion.js'; import { fullI32Range, fullU32Range, @@ -29,7 +29,7 @@ export const d = makeCaseCache('unary/bool_conversion', { }, f32: () => { return scalarF32Range().map(f => { - const expected: Scalar[] = []; + const expected: ScalarValue[] = []; if (f !== 0) { expected.push(bool(true)); } @@ -41,7 +41,7 @@ export const d = makeCaseCache('unary/bool_conversion', { }, f16: () => { return scalarF16Range().map(f => { - const expected: Scalar[] = []; + const expected: ScalarValue[] = []; if (f !== 0) { expected.push(bool(true)); } diff --git a/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts index 5f5bb31397a3..55d01d3eda1f 100644 --- a/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/bool_conversion.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the boolean conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF16, TypeF32, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './bool_conversion.cache.js'; @@ -31,7 +31,14 @@ Identity operation ) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeBool, t.params, cases); + await run( + t, + vectorizeToExpression(t.params.vectorize), + [Type.bool], + Type.bool, + t.params, + cases + ); }); g.test('u32') @@ -49,7 +56,7 @@ The result is false if e is 0, and true otherwise. ) .fn(async t => { const cases = await d.get('u32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeBool, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.bool, t.params, cases); }); g.test('i32') @@ -67,7 +74,7 @@ The result is false if e is 0, and true otherwise. ) .fn(async t => { const cases = await d.get('i32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeBool, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.bool, t.params, cases); }); g.test('f32') @@ -85,7 +92,7 @@ The result is false if e is 0.0 or -0.0, and true otherwise. ) .fn(async t => { const cases = await d.get('f32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeBool, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.bool, t.params, cases); }); g.test('f16') @@ -106,5 +113,5 @@ The result is false if e is 0.0 or -0.0, and true otherwise. }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeBool, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/bool_logical.spec.ts b/src/webgpu/shader/execution/expression/unary/bool_logical.spec.ts index 01eaaab43a51..58d4b9fc0fc3 100644 --- a/src/webgpu/shader/execution/expression/unary/bool_logical.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/bool_logical.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the boolean unary logical expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { bool, TypeBool } from '../../../../util/conversion.js'; +import { bool, Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { unary } from './unary.js'; @@ -29,5 +29,5 @@ Logical negation. The result is true when e is false and false when e is true. C { input: bool(false), expected: bool(true) }, ]; - await run(t, unary('!'), [TypeBool], TypeBool, t.params, cases); + await run(t, unary('!'), [Type.bool], Type.bool, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts index 60e16e645221..813bbf7a64e6 100644 --- a/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f16_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the f16 arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF16 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { d } from './f16_arithmetic.cache.js'; @@ -28,5 +28,5 @@ Accuracy: Correctly rounded }) .fn(async t => { const cases = await d.get('negation'); - await run(t, unary('-'), [TypeF16], TypeF16, t.params, cases); + await run(t, unary('-'), [Type.f16], Type.f16, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts index a8da8ff3bd7f..765b36d54cc6 100644 --- a/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts @@ -4,14 +4,7 @@ Execution Tests for the f32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { - TypeBool, - TypeF16, - TypeF32, - TypeI32, - TypeMat, - TypeU32, -} from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './f16_conversion.cache.js'; @@ -46,7 +39,7 @@ The result is 1.0 if e is true and 0.0 otherwise }) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.bool], Type.f16, t.params, cases); }); g.test('u32') @@ -66,7 +59,7 @@ Converted to f16, +/-Inf if out of range }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'u32_const' : 'u32_non_const'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.f16, t.params, cases); }); g.test('i32') @@ -86,7 +79,7 @@ Converted to f16, +/-Inf if out of range }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'i32_const' : 'i32_non_const'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.f16, t.params, cases); }); g.test('f32') @@ -106,7 +99,7 @@ Correctly rounded to f16 }) .fn(async t => { const cases = await d.get(t.params.inputSource === 'const' ? 'f32_const' : 'f32_non_const'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.f16, t.params, cases); }); g.test('f32_mat') @@ -132,8 +125,8 @@ g.test('f32_mat') await run( t, matrixExperession(cols, rows), - [TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f16), t.params, cases ); @@ -156,7 +149,7 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeF16, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.f16, t.params, cases); }); g.test('f16_mat') @@ -182,8 +175,8 @@ g.test('f16_mat') await run( t, matrixExperession(cols, rows), - [TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF16), + [Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f16), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts index c60e2c3d51b0..3bb48705e854 100644 --- a/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the f32 arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeF32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { d } from './f32_arithmetic.cache.js'; @@ -25,5 +25,5 @@ Accuracy: Correctly rounded ) .fn(async t => { const cases = await d.get('negation'); - await run(t, unary('-'), [TypeF32], TypeF32, t.params, cases); + await run(t, unary('-'), [Type.f32], Type.f32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts index 3e2fbd540ace..464fdee44e79 100644 --- a/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/f32_conversion.spec.ts @@ -4,14 +4,7 @@ Execution Tests for the f32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { - TypeBool, - TypeF16, - TypeF32, - TypeI32, - TypeMat, - TypeU32, -} from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './f32_conversion.cache.js'; @@ -43,7 +36,7 @@ The result is 1.0 if e is true and 0.0 otherwise ) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.bool], Type.f32, t.params, cases); }); g.test('u32') @@ -60,7 +53,7 @@ Converted to f32 ) .fn(async t => { const cases = await d.get('u32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.f32, t.params, cases); }); g.test('i32') @@ -77,7 +70,7 @@ Converted to f32 ) .fn(async t => { const cases = await d.get('i32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.f32, t.params, cases); }); g.test('f32') @@ -94,7 +87,7 @@ Identity operation ) .fn(async t => { const cases = await d.get('f32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.f32, t.params, cases); }); g.test('f32_mat') @@ -117,8 +110,8 @@ g.test('f32_mat') await run( t, matrixExperession(cols, rows), - [TypeMat(cols, rows, TypeF32)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f32)], + Type.mat(cols, rows, Type.f32), t.params, cases ); @@ -141,7 +134,7 @@ g.test('f16') }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeF32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.f32, t.params, cases); }); g.test('f16_mat') @@ -167,8 +160,8 @@ g.test('f16_mat') await run( t, matrixExperession(cols, rows), - [TypeMat(cols, rows, TypeF16)], - TypeMat(cols, rows, TypeF32), + [Type.mat(cols, rows, Type.f16)], + Type.mat(cols, rows, Type.f32), t.params, cases ); diff --git a/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts index a6fe77ada165..a7d16a96cd88 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_arithmetic.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 arithmetic unary expression operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeI32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { allInputSources, run } from '../expression.js'; import { d } from './i32_arithmetic.cache.js'; @@ -24,5 +24,5 @@ Expression: -x ) .fn(async t => { const cases = await d.get('negation'); - await run(t, unary('-'), [TypeI32], TypeI32, t.params, cases); + await run(t, unary('-'), [Type.i32], Type.i32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts index dc0ca8b8d066..01e5728d70d1 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_complement.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 bitwise complement operation import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { i32, TypeI32 } from '../../../../util/conversion.js'; +import { i32, Type } from '../../../../util/conversion.js'; import { fullI32Range } from '../../../../util/math.js'; import { allInputSources, run } from '../expression.js'; @@ -26,5 +26,5 @@ Expression: ~x const cases = fullI32Range().map(e => { return { input: i32(e), expected: i32(~e) }; }); - await run(t, unary('~'), [TypeI32], TypeI32, t.params, cases); + await run(t, unary('~'), [Type.i32], Type.i32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts index 4ad3c4e7424f..606253b59e5c 100644 --- a/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/i32_conversion.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the i32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF16, TypeF32, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './i32_conversion.cache.js'; @@ -31,7 +31,7 @@ The result is 1u if e is true and 0u otherwise ) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.bool], Type.i32, t.params, cases); }); g.test('u32') @@ -48,7 +48,7 @@ Reinterpretation of bits ) .fn(async t => { const cases = await d.get('u32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.i32, t.params, cases); }); g.test('i32') @@ -65,7 +65,7 @@ Identity operation ) .fn(async t => { const cases = await d.get('i32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.i32, t.params, cases); }); g.test('f32') @@ -82,7 +82,7 @@ e is converted to i32, rounding towards zero ) .fn(async t => { const cases = await d.get('f32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.i32, t.params, cases); }); g.test('f16') @@ -102,5 +102,5 @@ e is converted to u32, rounding towards zero }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeI32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.i32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts b/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts index 6052ac008f0e..74251a32c62b 100644 --- a/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/u32_complement.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the u32 bitwise complement operation import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeU32, u32 } from '../../../../util/conversion.js'; +import { Type, u32 } from '../../../../util/conversion.js'; import { fullU32Range } from '../../../../util/math.js'; import { allInputSources, run } from '../expression.js'; @@ -26,5 +26,5 @@ Expression: ~x const cases = fullU32Range().map(e => { return { input: u32(e), expected: u32(~e) }; }); - await run(t, unary('~'), [TypeU32], TypeU32, t.params, cases); + await run(t, unary('~'), [Type.u32], Type.u32, t.params, cases); }); diff --git a/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts b/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts index d684824362cf..330f713dca5e 100644 --- a/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts +++ b/src/webgpu/shader/execution/expression/unary/u32_conversion.spec.ts @@ -4,7 +4,7 @@ Execution Tests for the u32 conversion operations import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../gpu_test.js'; -import { TypeBool, TypeF16, TypeF32, TypeI32, TypeU32 } from '../../../../util/conversion.js'; +import { Type } from '../../../../util/conversion.js'; import { ShaderBuilder, allInputSources, run } from '../expression.js'; import { d } from './u32_conversion.cache.js'; @@ -31,7 +31,7 @@ The result is 1u if e is true and 0u otherwise ) .fn(async t => { const cases = await d.get('bool'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.bool], Type.u32, t.params, cases); }); g.test('u32') @@ -48,7 +48,7 @@ Identity operation ) .fn(async t => { const cases = await d.get('u32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeU32], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.u32], Type.u32, t.params, cases); }); g.test('i32') @@ -65,7 +65,7 @@ Reinterpretation of bits ) .fn(async t => { const cases = await d.get('i32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeI32], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.i32], Type.u32, t.params, cases); }); g.test('f32') @@ -82,7 +82,7 @@ e is converted to u32, rounding towards zero ) .fn(async t => { const cases = await d.get('f32'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF32], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f32], Type.u32, t.params, cases); }); g.test('f16') @@ -102,14 +102,14 @@ e is converted to u32, rounding towards zero }) .fn(async t => { const cases = await d.get('f16'); - await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeU32, t.params, cases); + await run(t, vectorizeToExpression(t.params.vectorize), [Type.f16], Type.u32, t.params, cases); }); g.test('abstract_int') .specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function') .desc( ` -u32(e), where e is an AbstractInt +u32(e), where e is an Type.abstractInt Identity operation if the e can be represented in u32, otherwise it produces a shader-creation error ` diff --git a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts index 183fa296248f..f7084b5d07d4 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts @@ -5,7 +5,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; -import { TypeF16, elementType, kAllScalarsAndVectors } from '../../../../../util/conversion.js'; +import { Type, elementType, kAllScalarsAndVectors } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; import { @@ -34,7 +34,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts index cabdf6b659d6..13e3492a5146 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -48,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -67,7 +66,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec }); // f32 is included here to confirm that validation is failing due to a type issue and not something else. -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -81,7 +80,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts index 3526b45f32ad..8e60459676f6 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts @@ -6,13 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, - TypeAbstractInt, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -50,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -59,7 +56,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.acosh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -84,8 +81,8 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, - [type.create(type === TypeAbstractInt ? 1n : 1)], + /* expectedResult */ type === Type.f32, + [type.create(type === Type.abstractInt ? 1n : 1)], 'constant' ); }); diff --git a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts index 3b7cdf8b8668..fef4540f4304 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -48,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -66,7 +65,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -80,7 +79,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts index c205ea2d6ecb..a029e38d02f3 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, + Type, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { linearRange, linearRangeBigInt } from '../../../../../util/math.js'; @@ -52,7 +50,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -61,7 +59,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.asinh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -72,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -86,7 +84,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts index 3b7dad4c4059..e8f325be5b94 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -47,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -63,7 +62,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -77,7 +76,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts index 4c61b331e2ad..712695ed0eb4 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts @@ -6,13 +6,11 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, - Vector, - VectorType, + VectorValue, elementType, kFloatScalarsAndVectors, kConcreteIntegerScalarsAndVectors, + Type, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -56,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -75,7 +73,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument_y') .desc( @@ -86,11 +84,11 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() .params(u => u.combine('type', keysOf(kIntegerArgumentTypes))) .fn(t => { const yTy = kIntegerArgumentTypes[t.params.type]; - const xTy = yTy instanceof Vector ? new VectorType(yTy.size, TypeF32) : TypeF32; + const xTy = yTy instanceof VectorValue ? Type.vec(yTy.size, Type.f32) : Type.f32; validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ yTy === TypeF32, + /* expectedResult */ yTy === Type.f32, [yTy.create(1), xTy.create(1)], 'constant' ); @@ -105,11 +103,11 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() .params(u => u.combine('type', keysOf(kIntegerArgumentTypes))) .fn(t => { const xTy = kIntegerArgumentTypes[t.params.type]; - const yTy = xTy instanceof Vector ? new VectorType(xTy.size, TypeF32) : TypeF32; + const yTy = xTy instanceof VectorValue ? Type.vec(xTy.size, Type.f32) : Type.f32; validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ xTy === TypeF32, + /* expectedResult */ xTy === Type.f32, [yTy.create(1), xTy.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts index 1d31ed88db28..460977c78878 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -48,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -66,7 +65,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -80,7 +79,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts index 09018d12a3a1..02fdbf70fc98 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts index 6342189ad643..5e8b3f3fe6ef 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts @@ -6,7 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, + Type, elementType, kFloatScalarsAndVectors, kConcreteIntegerScalarsAndVectors, @@ -44,7 +44,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('high', u => fullRangeForType(kValuesTypes[u.type], 4)) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts b/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts index 62924453f13a..440673c99265 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts @@ -2,7 +2,6 @@ import { assert, unreachable } from '../../../../../../common/util/util.js'; import { kValue } from '../../../../../util/constants.js'; import { Type, - TypeF16, Value, elementType, elementsOf, @@ -156,7 +155,7 @@ export function validateConstOrOverrideBuiltinEval( stage: ConstantOrOverrideStage ) { const elTys = args.map(arg => elementType(arg.type)!); - const enables = elTys.some(ty => ty === TypeF16) ? 'enable f16;' : ''; + const enables = elTys.some(ty => ty === Type.f16) ? 'enable f16;' : ''; switch (stage) { case 'constant': { diff --git a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts index d010cf499276..09b807c1039d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -47,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -61,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -75,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts index 6a7fcddba232..ffb01b707671 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -42,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -51,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.cosh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -62,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -76,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts index 2805b97b1597..35c7ff3c9062 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -42,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -51,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input const expectedResult = isRepresentable( (Number(t.params.value) * 180) / Math.PI, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -62,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -76,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts index cf5a707ba7c7..dd0a3fb595a8 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts @@ -5,9 +5,7 @@ Validation tests for derivative builtins. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, - TypeMat, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConcreteF16ScalarsAndVectors, @@ -98,10 +96,10 @@ fn foo() { // The list of invalid argument types to test, with an f32 control case. const kArgumentTypes = objectsToRecord([ - TypeF32, + Type.f32, ...kConcreteIntegerScalarsAndVectors, ...kConcreteF16ScalarsAndVectors, - TypeMat(2, 2, TypeF32), + Type.mat2x2f, ]); g.test('invalid_argument_types') @@ -115,17 +113,17 @@ Derivative builtins only accept f32 scalar and vector types. u.combine('type', keysOf(kArgumentTypes)).combine('call', ['', ...kDerivativeBuiltins]) ) .beforeAllSubcases(t => { - if (elementType(kArgumentTypes[t.params.type]) === TypeF16) { + if (elementType(kArgumentTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) .fn(t => { const type = kArgumentTypes[t.params.type]; const code = ` -${elementType(kArgumentTypes[t.params.type]) === TypeF16 ? 'enable f16;' : ''} +${elementType(kArgumentTypes[t.params.type]) === Type.f16 ? 'enable f16;' : ''} fn foo() { let x: ${type.toString()} = ${t.params.call}(${type.create(1).wgsl()}); }`; - t.expectCompileResult(kArgumentTypes[t.params.type] === TypeF32 || t.params.call === '', code); + t.expectCompileResult(kArgumentTypes[t.params.type] === Type.f32 || t.params.call === '', code); }); diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts index 6168fafb2336..edac1c0c529d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts @@ -7,9 +7,7 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { kValue } from '../../../../../util/constants.js'; import { - TypeF16, - TypeF32, - TypeAbstractFloat, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -72,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => valueForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -81,7 +79,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.exp(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -92,7 +90,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -106,7 +104,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts index 686cb3f69570..4f961e4259bc 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts @@ -7,9 +7,7 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { kValue } from '../../../../../util/constants.js'; import { - TypeF16, - TypeF32, - TypeAbstractFloat, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -72,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => valueForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -81,7 +79,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.pow(2, Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -92,7 +90,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -106,7 +104,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts index 25dd950dc602..f075b5e25c1b 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts index a192bc098cd9..4ab15948664b 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts @@ -6,11 +6,9 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, - TypeAbstractFloat, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -49,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -60,7 +58,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input isRepresentable( 1 / Math.sqrt(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -71,7 +69,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -85,7 +83,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts index 3fca4fec2cc8..620f09837329 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts @@ -7,15 +7,13 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { ScalarType, - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalar, kConvertableToFloatVec2, kConvertableToFloatVec3, kConvertableToFloatVec4, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -57,12 +55,12 @@ function calculate( isIntermediateRepresentable: isRepresentable( squareSum, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ), isResultRepresentable: isRepresentable( result, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ), result, }; @@ -86,7 +84,7 @@ the input scalar value always compiles without error .expand('value', u => fullRangeForType(kScalarTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kScalarTypes[t.params.type]) === TypeF16) { + if (elementType(kScalarTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -122,7 +120,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec2Types[t.params.type]) === TypeF16) { + if (elementType(kVec2Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -158,7 +156,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec3Types[t.params.type]) === TypeF16) { + if (elementType(kVec3Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -195,7 +193,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec4Types[t.params.type]) === TypeF16) { + if (elementType(kVec4Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -210,7 +208,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() with ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -224,7 +222,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts index b4cf923a50ce..18ea19c3e341 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts index 066fb652499b..b3a6c338c595 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts index 3c32e3e37495..3f0b4a6064ab 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts index 320bde062098..f7f60ec8f478 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts index 654729cadb4e..821fc0f3c382 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -52,7 +51,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input }) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -67,7 +66,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -81,7 +80,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts index 709a006ea8bd..354248e205ba 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -40,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -69,7 +68,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts index 2a4560576ca5..238bb51d8bc7 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kFloatScalarsAndVectors, kConcreteSignedIntegerScalarsAndVectors, @@ -44,7 +43,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -60,7 +59,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input }); const kUnsignedIntegerArgumentTypes = objectsToRecord([ - TypeF32, + Type.f32, ...kConcreteUnsignedIntegerScalarsAndVectors, ]); @@ -76,7 +75,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts index 2cd4ee002a3f..a408323e3a82 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts @@ -6,8 +6,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, @@ -47,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -61,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -75,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts index 45550d5c088f..8f5a4c0c60aa 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -42,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -51,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.sinh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -62,7 +60,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -76,7 +74,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts index 125a9d20de62..ff72a63379bc 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts @@ -6,12 +6,10 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, - TypeAbstractFloat, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -49,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -60,7 +58,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input isRepresentable( Math.sqrt(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); validateConstOrOverrideBuiltinEval( t, @@ -71,7 +69,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -85,7 +83,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(1)], 'constant' ); diff --git a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts index 38b0b204059c..ed5286781d56 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts @@ -6,11 +6,9 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - TypeF16, - TypeF32, + Type, elementType, kConcreteIntegerScalarsAndVectors, - TypeAbstractFloat, kConvertableToFloatScalarsAndVectors, } from '../../../../../util/conversion.js'; import { fpTraitsFor } from '../../../../../util/floating_point.js'; @@ -49,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === TypeF16) { + if (elementType(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -57,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const type = kValuesTypes[t.params.type]; const fp = fpTraitsFor( // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? TypeAbstractFloat : elementType(type) + elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) ); const smallestPositive = fp.constants().positive.min; const v = fp.quantize(Number(t.params.value)); @@ -71,7 +69,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ); }); -const kIntegerArgumentTypes = objectsToRecord([TypeF32, ...kConcreteIntegerScalarsAndVectors]); +const kIntegerArgumentTypes = objectsToRecord([Type.f32, ...kConcreteIntegerScalarsAndVectors]); g.test('integer_argument') .desc( @@ -85,7 +83,7 @@ Validates that scalar and vector integer arguments are rejected by ${builtin}() validateConstOrOverrideBuiltinEval( t, builtin, - /* expectedResult */ type === TypeF32, + /* expectedResult */ type === Type.f32, [type.create(0)], 'constant' ); diff --git a/src/webgpu/util/compare.ts b/src/webgpu/util/compare.ts index d9cc693184f1..7cb71ca8de5a 100644 --- a/src/webgpu/util/compare.ts +++ b/src/webgpu/util/compare.ts @@ -8,7 +8,14 @@ import { import { Expectation, toComparator } from '../shader/execution/expression/expectation.js'; import BinaryStream from './binary_stream.js'; -import { isFloatValue, isScalar, Matrix, Scalar, Value, Vector } from './conversion.js'; +import { + isFloatValue, + isScalarValue, + MatrixValue, + ScalarValue, + Value, + VectorValue, +} from './conversion.js'; import { FPInterval } from './floating_point.js'; /** Comparison describes the result of a Comparator function. */ @@ -98,9 +105,9 @@ function compareValue(got: Value, expected: Value): Comparison { } } - if (isScalar(got)) { + if (isScalarValue(got)) { const g = got; - const e = expected as Scalar; + const e = expected as ScalarValue; const isFloat = g.type.kind === 'f64' || g.type.kind === 'f32' || g.type.kind === 'f16'; const matched = (isFloat && (g.value as number) === (e.value as number)) || (!isFloat && g.value === e.value); @@ -111,8 +118,8 @@ function compareValue(got: Value, expected: Value): Comparison { }; } - if (got instanceof Vector) { - const e = expected as Vector; + if (got instanceof VectorValue) { + const e = expected as VectorValue; const gLen = got.elements.length; const eLen = e.elements.length; let matched = gLen === eLen; @@ -130,8 +137,8 @@ function compareValue(got: Value, expected: Value): Comparison { }; } - if (got instanceof Matrix) { - const e = expected as Matrix; + if (got instanceof MatrixValue) { + const e = expected as MatrixValue; const gCols = got.type.cols; const eCols = e.type.cols; const gRows = got.type.rows; @@ -175,7 +182,7 @@ function compareInterval(got: Value, expected: FPInterval): Comparison { } } - if (isScalar(got)) { + if (isScalarValue(got)) { const g = got.value as number; const matched = expected.contains(g); return { @@ -197,7 +204,7 @@ function compareInterval(got: Value, expected: FPInterval): Comparison { */ function compareVector(got: Value, expected: FPInterval[]): Comparison { // Check got type - if (!(got instanceof Vector)) { + if (!(got instanceof VectorValue)) { return { matched: false, got: `${Colors.red((typeof got).toString())}(${got})`, @@ -262,7 +269,7 @@ function convertArrayToString(m: T[]): string { */ function compareMatrix(got: Value, expected: FPInterval[][]): Comparison { // Check got type - if (!(got instanceof Matrix)) { + if (!(got instanceof MatrixValue)) { return { matched: false, got: `${Colors.red((typeof got).toString())}(${got})`, diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 31ef330d50a5..2b9e7989bcdb 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -596,9 +596,13 @@ export type ScalarKind = export class ScalarType { readonly kind: ScalarKind; // The named type readonly _size: number; // In bytes - readonly read: (buf: Uint8Array, offset: number) => Scalar; // reads a scalar from a buffer + readonly read: (buf: Uint8Array, offset: number) => ScalarValue; // reads a scalar from a buffer - constructor(kind: ScalarKind, size: number, read: (buf: Uint8Array, offset: number) => Scalar) { + constructor( + kind: ScalarKind, + size: number, + read: (buf: Uint8Array, offset: number) => ScalarValue + ) { this.kind = kind; this._size = size; this.read = read; @@ -612,8 +616,8 @@ export class ScalarType { return this._size; } - /** Constructs a Scalar of this type with `value` */ - public create(value: number | bigint): Scalar { + /** Constructs a ScalarValue of this type with `value` */ + public create(value: number | bigint): ScalarValue { switch (typeof value) { case 'number': switch (this.kind) { @@ -659,6 +663,17 @@ export class VectorType { readonly width: number; // Number of elements in the vector readonly elementType: ScalarType; // Element type + static create(width: number, elementType: ScalarType): VectorType { + const key = `${elementType.toString()} ${width}}`; + let ty = vectorTypes.get(key); + if (ty !== undefined) { + return ty; + } + ty = new VectorType(width, elementType); + vectorTypes.set(key, ty); + return ty; + } + constructor(width: number, elementType: ScalarType) { this.width = width; this.elementType = elementType; @@ -668,13 +683,13 @@ export class VectorType { * @returns a vector constructed from the values read from the buffer at the * given byte offset */ - public read(buf: Uint8Array, offset: number): Vector { - const elements: Array = []; + public read(buf: Uint8Array, offset: number): VectorValue { + const elements: Array = []; for (let i = 0; i < this.width; i++) { elements[i] = this.elementType.read(buf, offset); offset += this.elementType.size; } - return new Vector(elements); + return new VectorValue(elements); } public toString(): string { @@ -686,36 +701,36 @@ export class VectorType { } /** Constructs a Vector of this type with the given values */ - public create(value: (number | bigint) | readonly (number | bigint)[]): Vector { + public create(value: (number | bigint) | readonly (number | bigint)[]): VectorValue { if (value instanceof Array) { assert(value.length === this.width); } else { value = Array(this.width).fill(value); } - return new Vector(value.map(v => this.elementType.create(v))); + return new VectorValue(value.map(v => this.elementType.create(v))); } } // Maps a string representation of a vector type to vector type. const vectorTypes = new Map(); -export function TypeVec(width: number, elementType: ScalarType): VectorType { - const key = `${elementType.toString()} ${width}}`; - let ty = vectorTypes.get(key); - if (ty !== undefined) { - return ty; - } - ty = new VectorType(width, elementType); - vectorTypes.set(key, ty); - return ty; -} - /** MatrixType describes the type of WGSL Matrix. */ export class MatrixType { readonly cols: number; // Number of columns in the Matrix readonly rows: number; // Number of elements per column in the Matrix readonly elementType: ScalarType; // Element type + static create(cols: number, rows: number, elementType: ScalarType): MatrixType { + const key = `${elementType.toString()} ${cols} ${rows}`; + let ty = matrixTypes.get(key); + if (ty !== undefined) { + return ty; + } + ty = new MatrixType(cols, rows, elementType); + matrixTypes.set(key, ty); + return ty; + } + constructor(cols: number, rows: number, elementType: ScalarType) { this.cols = cols; this.rows = rows; @@ -732,8 +747,8 @@ export class MatrixType { * @returns a Matrix constructed from the values read from the buffer at the * given byte offset */ - public read(buf: Uint8Array, offset: number): Matrix { - const elements: Scalar[][] = [...Array(this.cols)].map(_ => [...Array(this.rows)]); + public read(buf: Uint8Array, offset: number): MatrixValue { + const elements: ScalarValue[][] = [...Array(this.cols)].map(_ => [...Array(this.rows)]); for (let c = 0; c < this.cols; c++) { for (let r = 0; r < this.rows; r++) { elements[c][r] = this.elementType.read(buf, offset); @@ -745,7 +760,7 @@ export class MatrixType { offset += this.elementType.size; } } - return new Matrix(elements); + return new MatrixValue(elements); } public toString(): string { @@ -753,7 +768,7 @@ export class MatrixType { } /** Constructs a Matrix of this type with the given values */ - public create(value: (number | bigint) | readonly (number | bigint)[]): Matrix { + public create(value: (number | bigint) | readonly (number | bigint)[]): MatrixValue { if (value instanceof Array) { assert(value.length === this.cols * this.rows); } else { @@ -764,27 +779,13 @@ export class MatrixType { const start = i * this.rows; columns.push(value.slice(start, start + this.rows)); } - return new Matrix(columns.map(c => c.map(v => this.elementType.create(v)))); + return new MatrixValue(columns.map(c => c.map(v => this.elementType.create(v)))); } } // Maps a string representation of a Matrix type to Matrix type. const matrixTypes = new Map(); -export function TypeMat(cols: number, rows: number, elementType: ScalarType): MatrixType { - const key = `${elementType.toString()} ${cols} ${rows}`; - let ty = matrixTypes.get(key); - if (ty !== undefined) { - return ty; - } - ty = new MatrixType(cols, rows, elementType); - matrixTypes.set(key, ty); - return ty; -} - -/** Type is a ScalarType, VectorType, or MatrixType. */ -export type Type = ScalarType | VectorType | MatrixType; - /** ArrayElementType infers the element type of the indexable type A */ type ArrayElementType = A extends { [index: number]: infer T } ? T : never; @@ -800,74 +801,128 @@ function valueFromBytes( return workingDataOut[0] as ArrayElementType; } -export const TypeAbstractInt: ScalarType = new ScalarType( - 'abstract-int', - 8, - (buf: Uint8Array, offset: number) => abstractInt(valueFromBytes(workingDataI64, buf, offset)) +const abstractIntType = new ScalarType('abstract-int', 8, (buf: Uint8Array, offset: number) => + abstractInt(valueFromBytes(workingDataI64, buf, offset)) ); -export const TypeI32: ScalarType = new ScalarType('i32', 4, (buf: Uint8Array, offset: number) => +const i32Type = new ScalarType('i32', 4, (buf: Uint8Array, offset: number) => i32(valueFromBytes(workingDataI32, buf, offset)) ); -export const TypeU32: ScalarType = new ScalarType('u32', 4, (buf: Uint8Array, offset: number) => +const u32Type = new ScalarType('u32', 4, (buf: Uint8Array, offset: number) => u32(valueFromBytes(workingDataU32, buf, offset)) ); -export const TypeI16: ScalarType = new ScalarType('i16', 2, (buf: Uint8Array, offset: number) => +const i16Type = new ScalarType('i16', 2, (buf: Uint8Array, offset: number) => i16(valueFromBytes(workingDataI16, buf, offset)) ); -export const TypeU16: ScalarType = new ScalarType('u16', 2, (buf: Uint8Array, offset: number) => +const u16Type = new ScalarType('u16', 2, (buf: Uint8Array, offset: number) => u16(valueFromBytes(workingDataU16, buf, offset)) ); -export const TypeI8: ScalarType = new ScalarType('i8', 1, (buf: Uint8Array, offset: number) => +const i8Type = new ScalarType('i8', 1, (buf: Uint8Array, offset: number) => i8(valueFromBytes(workingDataI8, buf, offset)) ); -export const TypeU8: ScalarType = new ScalarType('u8', 1, (buf: Uint8Array, offset: number) => +const u8Type = new ScalarType('u8', 1, (buf: Uint8Array, offset: number) => u8(valueFromBytes(workingDataU8, buf, offset)) ); -export const TypeAbstractFloat: ScalarType = new ScalarType( - 'abstract-float', - 8, - (buf: Uint8Array, offset: number) => abstractFloat(valueFromBytes(workingDataF64, buf, offset)) +const abstractFloatType = new ScalarType('abstract-float', 8, (buf: Uint8Array, offset: number) => + abstractFloat(valueFromBytes(workingDataF64, buf, offset)) ); -export const TypeF64: ScalarType = new ScalarType('f64', 8, (buf: Uint8Array, offset: number) => +const f64Type = new ScalarType('f64', 8, (buf: Uint8Array, offset: number) => f64(valueFromBytes(workingDataF64, buf, offset)) ); -export const TypeF32: ScalarType = new ScalarType('f32', 4, (buf: Uint8Array, offset: number) => +const f32Type = new ScalarType('f32', 4, (buf: Uint8Array, offset: number) => f32(valueFromBytes(workingDataF32, buf, offset)) ); -export const TypeF16: ScalarType = new ScalarType('f16', 2, (buf: Uint8Array, offset: number) => +const f16Type = new ScalarType('f16', 2, (buf: Uint8Array, offset: number) => f16Bits(valueFromBytes(workingDataU16, buf, offset)) ); -export const TypeBool: ScalarType = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => +const boolType = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => bool(valueFromBytes(workingDataU32, buf, offset) !== 0) ); +/** Type is a ScalarType, VectorType, or MatrixType. */ +export type Type = ScalarType | VectorType | MatrixType; + +/** Type holds pre-declared Types along with helper constructor functions. */ +export const Type = { + abstractInt: abstractIntType, + i32: i32Type, + u32: u32Type, + i16: i16Type, + u16: u16Type, + i8: i8Type, + u8: u8Type, + + abstractFloat: abstractFloatType, + f64: f64Type, + f32: f32Type, + f16: f16Type, + + bool: boolType, + + vec: (width: number, elementType: ScalarType) => VectorType.create(width, elementType), + + vec2i: VectorType.create(2, i32Type), + vec2u: VectorType.create(2, u32Type), + vec2f: VectorType.create(2, f32Type), + vec2h: VectorType.create(2, f16Type), + vec3i: VectorType.create(3, i32Type), + vec3u: VectorType.create(3, u32Type), + vec3f: VectorType.create(3, f32Type), + vec3h: VectorType.create(3, f16Type), + vec4i: VectorType.create(4, i32Type), + vec4u: VectorType.create(4, u32Type), + vec4f: VectorType.create(4, f32Type), + vec4h: VectorType.create(4, f16Type), + + mat: (cols: number, rows: number, elementType: ScalarType) => + MatrixType.create(cols, rows, elementType), + + mat2x2f: MatrixType.create(2, 2, f32Type), + mat2x2h: MatrixType.create(2, 2, f16Type), + mat3x2f: MatrixType.create(3, 2, f32Type), + mat3x2h: MatrixType.create(3, 2, f16Type), + mat4x2f: MatrixType.create(4, 2, f32Type), + mat4x2h: MatrixType.create(4, 2, f16Type), + mat2x3f: MatrixType.create(2, 3, f32Type), + mat2x3h: MatrixType.create(2, 3, f16Type), + mat3x3f: MatrixType.create(3, 3, f32Type), + mat3x3h: MatrixType.create(3, 3, f16Type), + mat4x3f: MatrixType.create(4, 3, f32Type), + mat4x3h: MatrixType.create(4, 3, f16Type), + mat2x4f: MatrixType.create(2, 4, f32Type), + mat2x4h: MatrixType.create(2, 4, f16Type), + mat3x4f: MatrixType.create(3, 4, f32Type), + mat3x4h: MatrixType.create(3, 4, f16Type), + mat4x4f: MatrixType.create(4, 4, f32Type), + mat4x4h: MatrixType.create(4, 4, f16Type), +}; + /** @returns the ScalarType from the ScalarKind */ export function scalarType(kind: ScalarKind): ScalarType { switch (kind) { case 'abstract-float': - return TypeAbstractFloat; + return Type.abstractFloat; case 'f64': - return TypeF64; + return Type.f64; case 'f32': - return TypeF32; + return Type.f32; case 'f16': - return TypeF16; + return Type.f16; case 'u32': - return TypeU32; + return Type.u32; case 'u16': - return TypeU16; + return Type.u16; case 'u8': - return TypeU8; + return Type.u8; case 'abstract-int': - return TypeAbstractInt; + return Type.abstractInt; case 'i32': - return TypeI32; + return Type.i32; case 'i16': - return TypeI16; + return Type.i16; case 'i8': - return TypeI8; + return Type.i8; case 'bool': - return TypeBool; + return Type.bool; } } @@ -886,14 +941,14 @@ export function numElementsOf(ty: Type): number { } /** @returns the scalar elements of the given Value */ -export function elementsOf(value: Value): Scalar[] { - if (isScalar(value)) { +export function elementsOf(value: Value): ScalarValue[] { + if (isScalarValue(value)) { return [value]; } - if (value instanceof Vector) { + if (value instanceof VectorValue) { return value.elements; } - if (value instanceof Matrix) { + if (value instanceof MatrixValue) { return value.elements.flat(); } throw new Error(`unhandled value ${value}`); @@ -935,7 +990,7 @@ export class AbstractIntValue { readonly value: bigint; // The abstract-integer value readonly bitsLow: number; // The low 32 bits of the abstract-integer value. readonly bitsHigh: number; // The high 32 bits of the abstract-integer value. - readonly type = TypeAbstractInt; // The type of the value. + readonly type = Type.abstractInt; // The type of the value. public constructor(value: bigint, bitsLow: number, bitsHigh: number) { this.value = value; @@ -977,7 +1032,7 @@ export class AbstractFloatValue { readonly value: number; // The f32 value readonly bitsLow: number; // The low 32 bits of the abstract-float value. readonly bitsHigh: number; // The high 32 bits of the abstract-float value. - readonly type = TypeAbstractFloat; // The type of the value. + readonly type = Type.abstractFloat; // The type of the value. public constructor(value: number, bitsLow: number, bitsHigh: number) { this.value = value; @@ -1023,7 +1078,7 @@ export class AbstractFloatValue { export class I32Value { readonly value: number; // The i32 value readonly bits: number; // The i32 value, bitcast to a 32-bit integer. - readonly type = TypeI32; // The type of the value. + readonly type = Type.i32; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1055,7 +1110,7 @@ export class I32Value { /** Class that encapsulates a single u32 value. */ export class U32Value { readonly value: number; // The u32 value - readonly type = TypeU32; // The type of the value. + readonly type = Type.u32; // The type of the value. public constructor(value: number) { this.value = value; @@ -1090,7 +1145,7 @@ export class U32Value { export class I16Value { readonly value: number; // The i16 value readonly bits: number; // The i16 value, bitcast to a 16-bit integer. - readonly type = TypeI16; // The type of the value. + readonly type = Type.i16; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1125,7 +1180,7 @@ export class I16Value { */ export class U16Value { readonly value: number; // The u16 value - readonly type = TypeU16; // The type of the value. + readonly type = Type.u16; // The type of the value. public constructor(value: number) { this.value = value; @@ -1161,7 +1216,7 @@ export class U16Value { export class I8Value { readonly value: number; // The i8 value readonly bits: number; // The i8 value, bitcast to a 8-bit integer. - readonly type = TypeI8; // The type of the value. + readonly type = Type.i8; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1196,7 +1251,7 @@ export class I8Value { */ export class U8Value { readonly value: number; // The u8 value - readonly type = TypeU8; // The type of the value. + readonly type = Type.u8; // The type of the value. public constructor(value: number) { this.value = value; @@ -1233,7 +1288,7 @@ export class F64Value { readonly value: number; // The f32 value readonly bitsLow: number; // The low 32 bits of the abstract-float value. readonly bitsHigh: number; // The high 32 bits of the abstract-float value. - readonly type = TypeF64; // The type of the value. + readonly type = Type.f64; // The type of the value. public constructor(value: number, bitsLow: number, bitsHigh: number) { this.value = value; @@ -1280,7 +1335,7 @@ export class F64Value { export class F32Value { readonly value: number; // The f32 value readonly bits: number; // The f32 value, bitcast to a 32-bit integer. - readonly type = TypeF32; // The type of the value. + readonly type = Type.f32; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1324,7 +1379,7 @@ export class F32Value { export class F16Value { readonly value: number; // The f16 value readonly bits: number; // The f16 value, bitcast to a 16-bit integer. - readonly type = TypeF16; // The type of the value. + readonly type = Type.f16; // The type of the value. public constructor(value: number, bits: number) { this.value = value; @@ -1366,7 +1421,7 @@ export class F16Value { /** Class that encapsulates a single bool value. */ export class BoolValue { readonly value: boolean; // The bool value - readonly type = TypeBool; // The type of the value. + readonly type = Type.bool; // The type of the value. public constructor(value: boolean) { this.value = value; @@ -1392,7 +1447,7 @@ export class BoolValue { } /** Scalar represents all the scalar value types */ -export type Scalar = +export type ScalarValue = | AbstractIntValue | AbstractFloatValue | I32Value @@ -1407,10 +1462,10 @@ export type Scalar = | BoolValue; export interface ScalarBuilder { - (value: T): Scalar; + (value: T): ScalarValue; } -export function isScalar(value: object): value is Scalar { +export function isScalarValue(value: object): value is ScalarValue { return ( value instanceof AbstractIntValue || value instanceof AbstractFloatValue || @@ -1472,7 +1527,7 @@ export function u32Bits(bits: number) { /** Create an i16 from a numeric value, a JS `number`. */ export function i16(value: number) { workingDataI16[0] = value; - return new I16Value(value, workingDataU16[0]); + return new I16Value(workingDataI16[0], workingDataU16[0]); } /** Create a u16 from a numeric value, a JS `number`. */ @@ -1484,7 +1539,7 @@ export function u16(value: number) { /** Create an i8 from a numeric value, a JS `number`. */ export function i8(value: number) { workingDataI8[0] = value; - return new I8Value(value, workingDataU8[0]); + return new I8Value(workingDataI8[0], workingDataU8[0]); } /** Create a u8 from a numeric value, a JS `number`. */ @@ -1496,19 +1551,19 @@ export function u8(value: number) { /** Create an f64 from a numeric value, a JS `number`. */ export function f64(value: number) { workingDataF64[0] = value; - return new F64Value(value, workingDataU32[0], workingDataU32[1]); + return new F64Value(workingDataF64[0], workingDataU32[0], workingDataU32[1]); } /** Create an f32 from a numeric value, a JS `number`. */ export function f32(value: number) { workingDataF32[0] = value; - return new F32Value(value, workingDataU32[0]); + return new F32Value(workingDataF32[0], workingDataU32[0]); } /** Create an f32 from a bit representation, a uint32 represented as a JS `number`. */ export function f32Bits(bits: number) { workingDataU32[0] = bits; - return new F32Value(workingDataF32[0], bits); + return new F32Value(workingDataF32[0], workingDataU32[0]); } /** Create an f16 from a numeric value, a JS `number`. */ @@ -1520,11 +1575,11 @@ export function f16(value: number) { /** Create an f16 from a bit representation, a uint16 represented as a JS `number`. */ export function f16Bits(bits: number) { workingDataU16[0] = bits; - return new F16Value(workingDataF16[0], bits); + return new F16Value(workingDataF16[0], workingDataU16[0]); } /** Create a boolean value. */ -export function bool(value: boolean): Scalar { +export function bool(value: boolean): ScalarValue { return new BoolValue(value); } @@ -1537,11 +1592,11 @@ export const False = bool(false); /** * Class that encapsulates a vector value. */ -export class Vector { - readonly elements: Array; +export class VectorValue { + readonly elements: Array; readonly type: VectorType; - public constructor(elements: Array) { + public constructor(elements: Array) { if (elements.length < 2 || elements.length > 4) { throw new Error(`vector element count must be between 2 and 4, got ${elements.length}`); } @@ -1555,7 +1610,7 @@ export class Vector { } } this.elements = elements; - this.type = TypeVec(elements.length, elements[0].type); + this.type = VectorType.create(elements.length, elements[0].type); } /** @@ -1604,18 +1659,18 @@ export class Vector { } /** Helper for constructing a new two-element vector with the provided values */ -export function vec2(x: Scalar, y: Scalar) { - return new Vector([x, y]); +export function vec2(x: ScalarValue, y: ScalarValue) { + return new VectorValue([x, y]); } /** Helper for constructing a new three-element vector with the provided values */ -export function vec3(x: Scalar, y: Scalar, z: Scalar) { - return new Vector([x, y, z]); +export function vec3(x: ScalarValue, y: ScalarValue, z: ScalarValue) { + return new VectorValue([x, y, z]); } /** Helper for constructing a new four-element vector with the provided values */ -export function vec4(x: Scalar, y: Scalar, z: Scalar, w: Scalar) { - return new Vector([x, y, z, w]); +export function vec4(x: ScalarValue, y: ScalarValue, z: ScalarValue, w: ScalarValue) { + return new VectorValue([x, y, z, w]); } /** @@ -1624,7 +1679,7 @@ export function vec4(x: Scalar, y: Scalar, z: Scalar, w: Scalar) { * @param v array of numbers to be converted, must contain 2, 3 or 4 elements * @param op function to convert from number to Scalar, e.g. 'f32` */ -export function toVector(v: readonly number[], op: (n: number) => Scalar): Vector { +export function toVector(v: readonly number[], op: (n: number) => ScalarValue): VectorValue { switch (v.length) { case 2: return vec2(op(v[0]), op(v[1])); @@ -1639,11 +1694,11 @@ export function toVector(v: readonly number[], op: (n: number) => Scalar): Vecto /** * Class that encapsulates a Matrix value. */ -export class Matrix { - readonly elements: Scalar[][]; +export class MatrixValue { + readonly elements: ScalarValue[][]; readonly type: MatrixType; - public constructor(elements: Array>) { + public constructor(elements: Array>) { const num_cols = elements.length; if (num_cols < 2 || num_cols > 4) { throw new Error(`matrix cols count must be between 2 and 4, got ${num_cols}`); @@ -1664,7 +1719,7 @@ export class Matrix { } this.elements = elements; - this.type = TypeMat(num_cols, num_rows, elem_type); + this.type = MatrixType.create(num_cols, num_rows, elem_type); } /** @@ -1706,35 +1761,37 @@ export class Matrix { * be of the same length. All Arrays must have 2, 3, or 4 elements. * @param op function to convert from number to Scalar, e.g. 'f32` */ -export function toMatrix(m: ROArrayArray, op: (n: number) => Scalar): Matrix { +export function toMatrix(m: ROArrayArray, op: (n: number) => ScalarValue): MatrixValue { const cols = m.length; const rows = m[0].length; - const elements: Scalar[][] = [...Array(cols)].map(_ => [...Array(rows)]); + const elements: ScalarValue[][] = [...Array(cols)].map(_ => [ + ...Array(rows), + ]); for (let i = 0; i < cols; i++) { for (let j = 0; j < rows; j++) { elements[i][j] = op(m[i][j]); } } - return new Matrix(elements); + return new MatrixValue(elements); } -/** Value is a Scalar or Vector value. */ -export type Value = Scalar | Vector | Matrix; +/** Value is a Scalar, Vector or Matrix value. */ +export type Value = ScalarValue | VectorValue | MatrixValue; -export type SerializedValueScalar = { +export type SerializedScalarValue = { kind: 'scalar'; type: ScalarKind; value: boolean | number; }; -export type SerializedValueVector = { +export type SerializedVectorValue = { kind: 'vector'; type: ScalarKind; value: boolean[] | readonly number[]; }; -export type SerializedValueMatrix = { +export type SerializedMatrixValue = { kind: 'matrix'; type: ScalarKind; value: ROArrayArray; @@ -1839,7 +1896,7 @@ enum SerializedValueKind { /** serializeValue() serializes a Value to a BinaryStream */ export function serializeValue(s: BinaryStream, v: Value) { - const serializeScalar = (scalar: Scalar, kind: ScalarKind) => { + const serializeScalar = (scalar: ScalarValue, kind: ScalarKind) => { switch (typeof scalar.value) { case 'number': switch (kind) { @@ -1892,13 +1949,13 @@ export function serializeValue(s: BinaryStream, v: Value) { } }; - if (isScalar(v)) { + if (isScalarValue(v)) { s.writeU8(SerializedValueKind.Scalar); serializeScalarKind(s, v.type.kind); serializeScalar(v, v.type.kind); return; } - if (v instanceof Vector) { + if (v instanceof VectorValue) { s.writeU8(SerializedValueKind.Vector); serializeScalarKind(s, v.type.elementType.kind); s.writeU8(v.type.width); @@ -1907,7 +1964,7 @@ export function serializeValue(s: BinaryStream, v: Value) { } return; } - if (v instanceof Matrix) { + if (v instanceof MatrixValue) { s.writeU8(SerializedValueKind.Matrix); serializeScalarKind(s, v.type.elementType.kind); s.writeU8(v.type.cols); @@ -1960,23 +2017,23 @@ export function deserializeValue(s: BinaryStream): Value { return deserializeScalar(scalarKind); case SerializedValueKind.Vector: { const width = s.readU8(); - const scalars = new Array(width); + const scalars = new Array(width); for (let i = 0; i < width; i++) { scalars[i] = deserializeScalar(scalarKind); } - return new Vector(scalars); + return new VectorValue(scalars); } case SerializedValueKind.Matrix: { const numCols = s.readU8(); const numRows = s.readU8(); - const columns = new Array(numCols); + const columns = new Array(numCols); for (let c = 0; c < numCols; c++) { - columns[c] = new Array(numRows); + columns[c] = new Array(numRows); for (let i = 0; i < numRows; i++) { columns[c][i] = deserializeScalar(scalarKind); } } - return new Matrix(columns); + return new MatrixValue(columns); } default: unreachable(`invalid serialized value kind: ${valueKind}`); @@ -2015,35 +2072,23 @@ export function isFloatType(ty: Type): boolean { } /// All floating-point scalar types -const kFloatScalars = [TypeAbstractFloat, TypeF32, TypeF16] as const; +const kFloatScalars = [Type.abstractFloat, Type.f32, Type.f16] as const; /// All floating-point vec2 types -const kFloatVec2 = [ - TypeVec(2, TypeAbstractFloat), - TypeVec(2, TypeF32), - TypeVec(2, TypeF16), -] as const; +const kFloatVec2 = [Type.vec(2, Type.abstractFloat), Type.vec2f, Type.vec2h] as const; /// All floating-point vec3 types -const kFloatVec3 = [ - TypeVec(3, TypeAbstractFloat), - TypeVec(3, TypeF32), - TypeVec(3, TypeF16), -] as const; +const kFloatVec3 = [Type.vec(3, Type.abstractFloat), Type.vec3f, Type.vec3h] as const; /// All floating-point vec4 types -const kFloatVec4 = [ - TypeVec(4, TypeAbstractFloat), - TypeVec(4, TypeF32), - TypeVec(4, TypeF16), -] as const; +const kFloatVec4 = [Type.vec(4, Type.abstractFloat), Type.vec4f, Type.vec4h] as const; /// All f16 floating-point scalar and vector types export const kConcreteF16ScalarsAndVectors = [ - TypeF16, - TypeVec(2, TypeF16), - TypeVec(3, TypeF16), - TypeVec(4, TypeF16), + Type.f16, + Type.vec2h, + Type.vec3h, + Type.vec4h, ] as const; /// All floating-point scalar and vector types @@ -2063,50 +2108,50 @@ export const kFloatScalarsAndVectors = [ /// All concrete integer scalar and vector types export const kConcreteIntegerScalarsAndVectors = [ - TypeI32, - TypeVec(2, TypeI32), - TypeVec(3, TypeI32), - TypeVec(4, TypeI32), - TypeU32, - TypeVec(2, TypeU32), - TypeVec(3, TypeU32), - TypeVec(4, TypeU32), + Type.i32, + Type.vec2i, + Type.vec3i, + Type.vec4i, + Type.u32, + Type.vec2u, + Type.vec3u, + Type.vec4u, ] as const; /// All signed integer scalar and vector types export const kConcreteSignedIntegerScalarsAndVectors = [ - TypeI32, - TypeVec(2, TypeI32), - TypeVec(3, TypeI32), - TypeVec(4, TypeI32), + Type.i32, + Type.vec2i, + Type.vec3i, + Type.vec4i, ] as const; /// All unsigned integer scalar and vector types export const kConcreteUnsignedIntegerScalarsAndVectors = [ - TypeU32, - TypeVec(2, TypeU32), - TypeVec(3, TypeU32), - TypeVec(4, TypeU32), + Type.u32, + Type.vec2u, + Type.vec3u, + Type.vec4u, ] as const; /// All types which are convertable to floating-point scalar types. -export const kConvertableToFloatScalar = [TypeAbstractInt, ...kFloatScalars] as const; +export const kConvertableToFloatScalar = [Type.abstractInt, ...kFloatScalars] as const; /// All types which are convertable to floating-point vector 2 types. -export const kConvertableToFloatVec2 = [TypeVec(2, TypeAbstractInt), ...kFloatVec2] as const; +export const kConvertableToFloatVec2 = [Type.vec(2, Type.abstractInt), ...kFloatVec2] as const; /// All types which are convertable to floating-point vector 3 types. -export const kConvertableToFloatVec3 = [TypeVec(3, TypeAbstractInt), ...kFloatVec3] as const; +export const kConvertableToFloatVec3 = [Type.vec(3, Type.abstractInt), ...kFloatVec3] as const; /// All types which are convertable to floating-point vector 4 types. -export const kConvertableToFloatVec4 = [TypeVec(4, TypeAbstractInt), ...kFloatVec4] as const; +export const kConvertableToFloatVec4 = [Type.vec(4, Type.abstractInt), ...kFloatVec4] as const; /// All types which are convertable to floating-point scalar or vector types. export const kConvertableToFloatScalarsAndVectors = [ - TypeAbstractInt, - TypeVec(2, TypeAbstractInt), - TypeVec(3, TypeAbstractInt), - TypeVec(4, TypeAbstractInt), + Type.abstractInt, + Type.vec(2, Type.abstractInt), + Type.vec(3, Type.abstractInt), + Type.vec(4, Type.abstractInt), ...kFloatScalarsAndVectors, ] as const; diff --git a/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index ea7bb838d4ca..9015eca362eb 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -12,7 +12,7 @@ import { f16, f32, isFloatType, - Scalar, + ScalarValue, ScalarType, toMatrix, toVector, @@ -1073,8 +1073,8 @@ export abstract class FPTraits { public abstract readonly flushSubnormal: (n: number) => number; /** @returns 1 * ULP: (number) */ public abstract readonly oneULP: (target: number, mode?: FlushMode) => number; - /** @returns a builder for converting numbers to Scalars */ - public abstract readonly scalarBuilder: (n: number) => Scalar; + /** @returns a builder for converting numbers to ScalarsValues */ + public abstract readonly scalarBuilder: (n: number) => ScalarValue; /** @returns a range of scalars for testing */ public abstract scalarRange(): readonly number[]; /** @returns a reduced range of scalars for testing */ From 3e45aee0b16dc724a79a0feb0490e2ddb06c9f0d Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Thu, 7 Mar 2024 11:00:27 +0000 Subject: [PATCH 12/13] Add ArrayType / ArrayValue Not currently used by anything --- src/resources/cache/hashes.json | 216 +++++++++--------- .../expression/call/builtin/abs.spec.ts | 4 +- .../expression/call/builtin/acos.spec.ts | 4 +- .../expression/call/builtin/acosh.spec.ts | 6 +- .../expression/call/builtin/asin.spec.ts | 4 +- .../expression/call/builtin/asinh.spec.ts | 6 +- .../expression/call/builtin/atan.spec.ts | 4 +- .../expression/call/builtin/atan2.spec.ts | 6 +- .../expression/call/builtin/atanh.spec.ts | 4 +- .../expression/call/builtin/ceil.spec.ts | 4 +- .../expression/call/builtin/clamp.spec.ts | 4 +- .../call/builtin/const_override_validation.ts | 15 +- .../expression/call/builtin/cos.spec.ts | 4 +- .../expression/call/builtin/cosh.spec.ts | 6 +- .../expression/call/builtin/degrees.spec.ts | 6 +- .../call/builtin/derivatives.spec.ts | 6 +- .../expression/call/builtin/exp.spec.ts | 6 +- .../expression/call/builtin/exp2.spec.ts | 6 +- .../expression/call/builtin/floor.spec.ts | 4 +- .../call/builtin/inverseSqrt.spec.ts | 6 +- .../expression/call/builtin/length.spec.ts | 20 +- .../expression/call/builtin/log.spec.ts | 4 +- .../expression/call/builtin/log2.spec.ts | 4 +- .../expression/call/builtin/modf.spec.ts | 4 +- .../expression/call/builtin/radians.spec.ts | 4 +- .../expression/call/builtin/round.spec.ts | 8 +- .../expression/call/builtin/saturate.spec.ts | 4 +- .../expression/call/builtin/sign.spec.ts | 4 +- .../expression/call/builtin/sin.spec.ts | 4 +- .../expression/call/builtin/sinh.spec.ts | 6 +- .../expression/call/builtin/sqrt.spec.ts | 6 +- .../expression/call/builtin/tan.spec.ts | 6 +- src/webgpu/util/conversion.ts | 184 +++++++++++++-- 33 files changed, 360 insertions(+), 219 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 6733022574c8..6c9518de081e 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,110 +1,110 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "45a00f48", - "webgpu/shader/execution/binary/af_logical.bin": "458eba4", - "webgpu/shader/execution/binary/af_division.bin": "ed6916d6", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "ddeaa1d3", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "bdc18f23", - "webgpu/shader/execution/binary/af_multiplication.bin": "e6e11b40", - "webgpu/shader/execution/binary/af_remainder.bin": "61849bd4", - "webgpu/shader/execution/binary/af_subtraction.bin": "6afd0c9a", - "webgpu/shader/execution/binary/f16_addition.bin": "118a69b1", - "webgpu/shader/execution/binary/f16_logical.bin": "53cbe093", - "webgpu/shader/execution/binary/f16_division.bin": "6cf5db74", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "37b3e5b1", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "f9f9c546", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "10c32980", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "4a992ee0", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "3969786a", - "webgpu/shader/execution/binary/f16_multiplication.bin": "27375c0a", - "webgpu/shader/execution/binary/f16_remainder.bin": "1e5d8fc7", - "webgpu/shader/execution/binary/f16_subtraction.bin": "daffd0ed", - "webgpu/shader/execution/binary/f32_addition.bin": "384766d0", - "webgpu/shader/execution/binary/f32_logical.bin": "d4f9fd6a", - "webgpu/shader/execution/binary/f32_division.bin": "760f650f", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "504aac15", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "740e31c4", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "3c5abc3c", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "c41fee39", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "43bfea5a", - "webgpu/shader/execution/binary/f32_multiplication.bin": "d6b990a9", - "webgpu/shader/execution/binary/f32_remainder.bin": "53f7d8e9", - "webgpu/shader/execution/binary/f32_subtraction.bin": "f644082", - "webgpu/shader/execution/binary/i32_arithmetic.bin": "8ce49cc7", - "webgpu/shader/execution/binary/i32_comparison.bin": "ac0e960f", - "webgpu/shader/execution/binary/u32_arithmetic.bin": "68560dc0", - "webgpu/shader/execution/binary/u32_comparison.bin": "ec9ec4c6", - "webgpu/shader/execution/abs.bin": "d7043582", - "webgpu/shader/execution/acos.bin": "a7a01d03", - "webgpu/shader/execution/acosh.bin": "3bfd9ebc", - "webgpu/shader/execution/asin.bin": "f91850f1", - "webgpu/shader/execution/asinh.bin": "19169ea3", - "webgpu/shader/execution/atan.bin": "51a04ddb", - "webgpu/shader/execution/atan2.bin": "e732e242", - "webgpu/shader/execution/atanh.bin": "4763b613", - "webgpu/shader/execution/bitcast.bin": "195c00a7", - "webgpu/shader/execution/ceil.bin": "9856b786", - "webgpu/shader/execution/clamp.bin": "5a700a65", - "webgpu/shader/execution/cos.bin": "ff14c921", - "webgpu/shader/execution/cosh.bin": "21c587ad", - "webgpu/shader/execution/cross.bin": "c159771f", - "webgpu/shader/execution/degrees.bin": "b0de92be", - "webgpu/shader/execution/determinant.bin": "83d642d4", - "webgpu/shader/execution/distance.bin": "e65d0cb7", - "webgpu/shader/execution/dot.bin": "dc57a00c", - "webgpu/shader/execution/exp.bin": "f0c6b19", - "webgpu/shader/execution/exp2.bin": "5d3dd4e0", - "webgpu/shader/execution/faceForward.bin": "3979a4de", - "webgpu/shader/execution/floor.bin": "3fecf76d", - "webgpu/shader/execution/fma.bin": "e7fe86b8", - "webgpu/shader/execution/fract.bin": "71caa066", - "webgpu/shader/execution/frexp.bin": "ed72dcec", - "webgpu/shader/execution/inverseSqrt.bin": "383e6e9c", - "webgpu/shader/execution/ldexp.bin": "bedfc1d5", - "webgpu/shader/execution/length.bin": "38e35ab4", - "webgpu/shader/execution/log.bin": "2517404c", - "webgpu/shader/execution/log2.bin": "a833136", - "webgpu/shader/execution/max.bin": "8c2f7c51", - "webgpu/shader/execution/min.bin": "7f732adb", - "webgpu/shader/execution/mix.bin": "982c982c", - "webgpu/shader/execution/modf.bin": "743632fc", - "webgpu/shader/execution/normalize.bin": "52adf424", - "webgpu/shader/execution/pack2x16float.bin": "2c879955", - "webgpu/shader/execution/pow.bin": "ba686c94", - "webgpu/shader/execution/quantizeToF16.bin": "e704252d", - "webgpu/shader/execution/radians.bin": "afe57c6e", - "webgpu/shader/execution/reflect.bin": "332e001e", - "webgpu/shader/execution/refract.bin": "246f6c0b", - "webgpu/shader/execution/round.bin": "e555383f", - "webgpu/shader/execution/saturate.bin": "9dce4047", - "webgpu/shader/execution/sign.bin": "3ef39d2e", - "webgpu/shader/execution/sin.bin": "8546b36c", - "webgpu/shader/execution/sinh.bin": "72ae8d37", - "webgpu/shader/execution/smoothstep.bin": "79eca0b6", - "webgpu/shader/execution/sqrt.bin": "ac8f95e9", - "webgpu/shader/execution/step.bin": "34ce6432", - "webgpu/shader/execution/tan.bin": "928e0e2f", - "webgpu/shader/execution/tanh.bin": "be078de7", - "webgpu/shader/execution/transpose.bin": "2ce22a5b", - "webgpu/shader/execution/trunc.bin": "26115486", - "webgpu/shader/execution/unpack2x16float.bin": "d052cda6", - "webgpu/shader/execution/unpack2x16snorm.bin": "a3ab8e29", - "webgpu/shader/execution/unpack2x16unorm.bin": "f42b9498", - "webgpu/shader/execution/unpack4x8snorm.bin": "5c90b367", - "webgpu/shader/execution/unpack4x8unorm.bin": "ef24abbe", - "webgpu/shader/execution/unary/af_arithmetic.bin": "28b510fa", - "webgpu/shader/execution/unary/af_assignment.bin": "4f4d507a", - "webgpu/shader/execution/unary/bool_conversion.bin": "5cbbd5e2", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "55ff626f", - "webgpu/shader/execution/unary/f16_conversion.bin": "e16712e2", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "672609de", - "webgpu/shader/execution/unary/f32_conversion.bin": "daa3ffb8", - "webgpu/shader/execution/unary/i32_arithmetic.bin": "eecbb027", - "webgpu/shader/execution/unary/i32_conversion.bin": "c3f19a9", - "webgpu/shader/execution/unary/u32_conversion.bin": "b58b1876", - "webgpu/shader/execution/unary/ai_assignment.bin": "326020c6", - "webgpu/shader/execution/binary/ai_arithmetic.bin": "40123e00", - "webgpu/shader/execution/unary/ai_arithmetic.bin": "37ffc69", - "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "35e08b61", - "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "6f31c22f", - "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "7b17ec2a" + "webgpu/shader/execution/binary/af_addition.bin": "25dcfcce", + "webgpu/shader/execution/binary/af_logical.bin": "409d37cf", + "webgpu/shader/execution/binary/af_division.bin": "bfcb23bd", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "cefea21a", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "624d816d", + "webgpu/shader/execution/binary/af_multiplication.bin": "34077746", + "webgpu/shader/execution/binary/af_remainder.bin": "9eddf765", + "webgpu/shader/execution/binary/af_subtraction.bin": "629bbf4", + "webgpu/shader/execution/binary/f16_addition.bin": "e3ff1263", + "webgpu/shader/execution/binary/f16_logical.bin": "99c0a0ed", + "webgpu/shader/execution/binary/f16_division.bin": "c0f3376", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "3054016e", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "9cb6bbb9", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "82f32c1d", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "29e9e0c7", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "109260c1", + "webgpu/shader/execution/binary/f16_multiplication.bin": "39287c38", + "webgpu/shader/execution/binary/f16_remainder.bin": "98b48517", + "webgpu/shader/execution/binary/f16_subtraction.bin": "a4975dc4", + "webgpu/shader/execution/binary/f32_addition.bin": "b0e6fddd", + "webgpu/shader/execution/binary/f32_logical.bin": "71a0f0be", + "webgpu/shader/execution/binary/f32_division.bin": "29a37cf9", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "76e48aa9", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "37f3a30b", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "f00c4cd8", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "25553482", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "26750bd3", + "webgpu/shader/execution/binary/f32_multiplication.bin": "68a7516b", + "webgpu/shader/execution/binary/f32_remainder.bin": "fb22b625", + "webgpu/shader/execution/binary/f32_subtraction.bin": "2e26b05f", + "webgpu/shader/execution/binary/i32_arithmetic.bin": "9256ccab", + "webgpu/shader/execution/binary/i32_comparison.bin": "c813abfa", + "webgpu/shader/execution/binary/u32_arithmetic.bin": "498f877e", + "webgpu/shader/execution/binary/u32_comparison.bin": "d69e1738", + "webgpu/shader/execution/abs.bin": "a6b6fed1", + "webgpu/shader/execution/acos.bin": "2a8f33b0", + "webgpu/shader/execution/acosh.bin": "3b39f532", + "webgpu/shader/execution/asin.bin": "94c67af3", + "webgpu/shader/execution/asinh.bin": "34c8547a", + "webgpu/shader/execution/atan.bin": "c5f98dae", + "webgpu/shader/execution/atan2.bin": "5c666ada", + "webgpu/shader/execution/atanh.bin": "5efbba8b", + "webgpu/shader/execution/bitcast.bin": "e90abd26", + "webgpu/shader/execution/ceil.bin": "fe43b603", + "webgpu/shader/execution/clamp.bin": "f4ec869a", + "webgpu/shader/execution/cos.bin": "78f30673", + "webgpu/shader/execution/cosh.bin": "15c18b89", + "webgpu/shader/execution/cross.bin": "72823897", + "webgpu/shader/execution/degrees.bin": "3b06fea4", + "webgpu/shader/execution/determinant.bin": "f0f66549", + "webgpu/shader/execution/distance.bin": "593c41fb", + "webgpu/shader/execution/dot.bin": "3eee146b", + "webgpu/shader/execution/exp.bin": "7f926769", + "webgpu/shader/execution/exp2.bin": "715c82a", + "webgpu/shader/execution/faceForward.bin": "8a365384", + "webgpu/shader/execution/floor.bin": "7b9a6254", + "webgpu/shader/execution/fma.bin": "88549fc7", + "webgpu/shader/execution/fract.bin": "e4eff2f9", + "webgpu/shader/execution/frexp.bin": "18a53421", + "webgpu/shader/execution/inverseSqrt.bin": "71016a37", + "webgpu/shader/execution/ldexp.bin": "450c6068", + "webgpu/shader/execution/length.bin": "62190368", + "webgpu/shader/execution/log.bin": "c4b1985b", + "webgpu/shader/execution/log2.bin": "28ccd982", + "webgpu/shader/execution/max.bin": "ba573f18", + "webgpu/shader/execution/min.bin": "5a834580", + "webgpu/shader/execution/mix.bin": "bf942d40", + "webgpu/shader/execution/modf.bin": "a8de3f36", + "webgpu/shader/execution/normalize.bin": "4bf84190", + "webgpu/shader/execution/pack2x16float.bin": "cc42ef45", + "webgpu/shader/execution/pow.bin": "6e8d08d1", + "webgpu/shader/execution/quantizeToF16.bin": "8c615e93", + "webgpu/shader/execution/radians.bin": "624ff571", + "webgpu/shader/execution/reflect.bin": "d0d624ae", + "webgpu/shader/execution/refract.bin": "3dd68359", + "webgpu/shader/execution/round.bin": "502c36a8", + "webgpu/shader/execution/saturate.bin": "be4cf88f", + "webgpu/shader/execution/sign.bin": "91310555", + "webgpu/shader/execution/sin.bin": "441d6be3", + "webgpu/shader/execution/sinh.bin": "4ebaa2e7", + "webgpu/shader/execution/smoothstep.bin": "6298e644", + "webgpu/shader/execution/sqrt.bin": "3cf64df1", + "webgpu/shader/execution/step.bin": "90148c99", + "webgpu/shader/execution/tan.bin": "e753749c", + "webgpu/shader/execution/tanh.bin": "62c99641", + "webgpu/shader/execution/transpose.bin": "2a4448c6", + "webgpu/shader/execution/trunc.bin": "ed9a0d22", + "webgpu/shader/execution/unpack2x16float.bin": "e81297f7", + "webgpu/shader/execution/unpack2x16snorm.bin": "513f9a8b", + "webgpu/shader/execution/unpack2x16unorm.bin": "8b56d0ce", + "webgpu/shader/execution/unpack4x8snorm.bin": "51af8a63", + "webgpu/shader/execution/unpack4x8unorm.bin": "302cf4a6", + "webgpu/shader/execution/unary/af_arithmetic.bin": "cd4618d", + "webgpu/shader/execution/unary/af_assignment.bin": "7d5de0f1", + "webgpu/shader/execution/unary/bool_conversion.bin": "401fb8c5", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "ad86d013", + "webgpu/shader/execution/unary/f16_conversion.bin": "86e35a85", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "fe97afd1", + "webgpu/shader/execution/unary/f32_conversion.bin": "97730c3c", + "webgpu/shader/execution/unary/i32_arithmetic.bin": "69f783bb", + "webgpu/shader/execution/unary/i32_conversion.bin": "4fec061e", + "webgpu/shader/execution/unary/u32_conversion.bin": "1a298dea", + "webgpu/shader/execution/unary/ai_assignment.bin": "1c2edca2", + "webgpu/shader/execution/binary/ai_arithmetic.bin": "657f2fb7", + "webgpu/shader/execution/unary/ai_arithmetic.bin": "216769d9", + "webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "544f4363", + "webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "14212e77", + "webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "57f19c15" } \ No newline at end of file diff --git a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts index f7084b5d07d4..3fc9eeb22c3d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/abs.spec.ts @@ -5,7 +5,7 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; -import { Type, elementType, kAllScalarsAndVectors } from '../../../../../util/conversion.js'; +import { Type, kAllScalarsAndVectors, scalarTypeOf } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; import { @@ -34,7 +34,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts index 13e3492a5146..9b6438aaec65 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acos.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts index 8e60459676f6..c3644befc9ba 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/acosh.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -56,7 +56,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.acosh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts index fef4540f4304..bff34f104dee 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asin.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts index a029e38d02f3..cec4b73c6094 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/asinh.spec.ts @@ -6,9 +6,9 @@ Validation tests for the ${builtin}() builtin. import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, Type, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -50,7 +50,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -59,7 +59,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.asinh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts index e8f325be5b94..76bafbbcca8a 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -46,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts index 712695ed0eb4..1a6776a018fa 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atan2.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { VectorValue, - elementType, kFloatScalarsAndVectors, kConcreteIntegerScalarsAndVectors, + scalarTypeOf, Type, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; @@ -54,7 +54,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -62,7 +62,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const type = kValuesTypes[t.params.type]; const expectedResult = isRepresentable( Math.abs(Math.atan2(Number(t.params.x), Number(t.params.y))), - elementType(type) + scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts index 460977c78878..1a9184a7416c 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/atanh.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { absBigInt } from '../../../../../util/math.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts index 02fdbf70fc98..951958d02c12 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/ceil.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts index 5e8b3f3fe6ef..9a23328d650f 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/clamp.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kFloatScalarsAndVectors, kConcreteIntegerScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -44,7 +44,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('high', u => fullRangeForType(kValuesTypes[u.type], 4)) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts b/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts index 440673c99265..6c46bfb0a054 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/const_override_validation.ts @@ -3,9 +3,10 @@ import { kValue } from '../../../../../util/constants.js'; import { Type, Value, - elementType, - elementsOf, + elementTypeOf, isAbstractType, + scalarElementsOf, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { scalarF16Range, @@ -22,7 +23,7 @@ export function rangeForType( bigint_range: readonly bigint[] ): (type: Type) => readonly (number | bigint)[] { return (type: Type): readonly (number | bigint)[] => { - switch (elementType(type).kind) { + switch (scalarTypeOf(type).kind) { case 'abstract-float': case 'f32': case 'f16': @@ -131,7 +132,7 @@ export type ConstantOrOverrideStage = 'constant' | 'override'; * @returns true if evaluation stage `stage` supports expressions of type @p. */ export function stageSupportsType(stage: ConstantOrOverrideStage, type: Type) { - if (stage === 'override' && isAbstractType(elementType(type)!)) { + if (stage === 'override' && isAbstractType(elementTypeOf(type)!)) { // Abstract numerics are concretized before being used in an override expression. return false; } @@ -154,7 +155,7 @@ export function validateConstOrOverrideBuiltinEval( args: Value[], stage: ConstantOrOverrideStage ) { - const elTys = args.map(arg => elementType(arg.type)!); + const elTys = args.map(arg => elementTypeOf(arg.type)!); const enables = elTys.some(ty => ty === Type.f16) ? 'enable f16;' : ''; switch (stage) { @@ -174,7 +175,7 @@ const v = ${builtin}(${args.map(arg => arg.wgsl()).join(', ')});` let numOverrides = 0; for (const arg of args) { const argOverrides: string[] = []; - for (const el of elementsOf(arg)) { + for (const el of scalarElementsOf(arg)) { const name = `o${numOverrides++}`; overrideDecls.push(`override ${name} : ${el.type};`); argOverrides.push(name); @@ -200,7 +201,7 @@ export function fullRangeForType(type: Type, count?: number): readonly (number | if (count === undefined) { count = 25; } - switch (elementType(type)?.kind) { + switch (scalarTypeOf(type)?.kind) { case 'abstract-float': return scalarF64Range({ pos_sub: Math.ceil((count * 1) / 5), diff --git a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts index 09b807c1039d..81d283abb299 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cos.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -46,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts index ffb01b707671..4178f5760d87 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/cosh.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -40,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -49,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.cosh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts index 35c7ff3c9062..f1cba6a6aef8 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/degrees.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -40,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -49,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input const expectedResult = isRepresentable( (Number(t.params.value) * 180) / Math.PI, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts index dd0a3fb595a8..54620ce17904 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/derivatives.spec.ts @@ -6,9 +6,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConcreteF16ScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -113,14 +113,14 @@ Derivative builtins only accept f32 scalar and vector types. u.combine('type', keysOf(kArgumentTypes)).combine('call', ['', ...kDerivativeBuiltins]) ) .beforeAllSubcases(t => { - if (elementType(kArgumentTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kArgumentTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) .fn(t => { const type = kArgumentTypes[t.params.type]; const code = ` -${elementType(kArgumentTypes[t.params.type]) === Type.f16 ? 'enable f16;' : ''} +${scalarTypeOf(kArgumentTypes[t.params.type]) === Type.f16 ? 'enable f16;' : ''} fn foo() { let x: ${type.toString()} = ${t.params.call}(${type.create(1).wgsl()}); diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts index edac1c0c529d..dcbd5b79e51d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp.spec.ts @@ -8,9 +8,9 @@ import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tabl import { kValue } from '../../../../../util/constants.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -70,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => valueForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -79,7 +79,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.exp(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts index 4f961e4259bc..0144abeefb04 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/exp2.spec.ts @@ -8,9 +8,9 @@ import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tabl import { kValue } from '../../../../../util/constants.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -70,7 +70,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => valueForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -79,7 +79,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.pow(2, Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts index f075b5e25c1b..b65fafc74466 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/floor.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() never .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts index 4ab15948664b..6310bfe25fee 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/inverseSqrt.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -58,7 +58,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input isRepresentable( 1 / Math.sqrt(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts index 620f09837329..003ad6811d72 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/length.spec.ts @@ -8,12 +8,12 @@ import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tabl import { ScalarType, Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalar, kConvertableToFloatVec2, kConvertableToFloatVec3, kConvertableToFloatVec4, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -55,12 +55,12 @@ function calculate( isIntermediateRepresentable: isRepresentable( squareSum, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ), isResultRepresentable: isRepresentable( result, // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ), result, }; @@ -84,7 +84,7 @@ the input scalar value always compiles without error .expand('value', u => fullRangeForType(kScalarTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kScalarTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kScalarTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -116,11 +116,11 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .beginSubcases() .expand('x', u => fullRangeForType(kVec2Types[u.type], 5)) .expand('y', u => fullRangeForType(kVec2Types[u.type], 5)) - .expand('_result', u => [calculate([u.x, u.y], elementType(kVec2Types[u.type]))]) + .expand('_result', u => [calculate([u.x, u.y], scalarTypeOf(kVec2Types[u.type]))]) .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec2Types[t.params.type]) === Type.f16) { + if (scalarTypeOf(kVec2Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -152,11 +152,11 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .expand('x', u => fullRangeForType(kVec3Types[u.type], 4)) .expand('y', u => fullRangeForType(kVec3Types[u.type], 4)) .expand('z', u => fullRangeForType(kVec3Types[u.type], 4)) - .expand('_result', u => [calculate([u.x, u.y, u.z], elementType(kVec3Types[u.type]))]) + .expand('_result', u => [calculate([u.x, u.y, u.z], scalarTypeOf(kVec3Types[u.type]))]) .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec3Types[t.params.type]) === Type.f16) { + if (scalarTypeOf(kVec3Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -189,11 +189,11 @@ Validates that constant evaluation and override evaluation of ${builtin}() with .expand('y', u => fullRangeForType(kVec4Types[u.type], 3)) .expand('z', u => fullRangeForType(kVec4Types[u.type], 3)) .expand('w', u => fullRangeForType(kVec4Types[u.type], 3)) - .expand('_result', u => [calculate([u.x, u.y, u.z, u.w], elementType(kVec4Types[u.type]))]) + .expand('_result', u => [calculate([u.x, u.y, u.z, u.w], scalarTypeOf(kVec4Types[u.type]))]) .filter(u => u._result.isResultRepresentable === u._result.isIntermediateRepresentable) ) .beforeAllSubcases(t => { - if (elementType(kVec4Types[t.params.type]) === Type.f16) { + if (scalarTypeOf(kVec4Types[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts index 18ea19c3e341..6d755304f2ff 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts index b3a6c338c595..0dfe64153651 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/log2.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts index 3f0b4a6064ab..2a90fa878ea6 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/modf.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts index f7f60ec8f478..8689bc3dbbe8 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/radians.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts index 821fc0f3c382..dae7482c18ce 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/round.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { fpTraitsFor } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,10 +39,10 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .filter(u => stageSupportsType(u.stage, kValuesTypes[u.type])) .beginSubcases() .expand('value', u => { - if (elementType(kValuesTypes[u.type]).kind === 'abstract-int') { + if (scalarTypeOf(kValuesTypes[u.type]).kind === 'abstract-int') { return fullRangeForType(kValuesTypes[u.type]); } else { - const constants = fpTraitsFor(elementType(kValuesTypes[u.type])).constants(); + const constants = fpTraitsFor(scalarTypeOf(kValuesTypes[u.type])).constants(); return unique(fullRangeForType(kValuesTypes[u.type]), [ constants.negative.min + 0.1, constants.positive.max - 0.1, @@ -51,7 +51,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input }) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts index 354248e205ba..cbd1b3f369ff 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/saturate.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -39,7 +39,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts index 238bb51d8bc7..f8e00ab9486d 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sign.spec.ts @@ -7,10 +7,10 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kFloatScalarsAndVectors, kConcreteSignedIntegerScalarsAndVectors, kConcreteUnsignedIntegerScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -43,7 +43,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts index a408323e3a82..3c807d094c5c 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sin.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -46,7 +46,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) diff --git a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts index 8f5a4c0c60aa..1983d0e1a7e4 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sinh.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -40,7 +40,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec .expand('value', u => fullRangeForType(kValuesTypes[u.type])) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -49,7 +49,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const expectedResult = isRepresentable( Math.sinh(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts index ff72a63379bc..7e983adf188c 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/sqrt.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { isRepresentable } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -58,7 +58,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() input isRepresentable( Math.sqrt(Number(t.params.value)), // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); validateConstOrOverrideBuiltinEval( t, diff --git a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts index ed5286781d56..097c2b374d95 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/tan.spec.ts @@ -7,9 +7,9 @@ import { makeTestGroup } from '../../../../../../common/framework/test_group.js' import { keysOf, objectsToRecord } from '../../../../../../common/util/data_tables.js'; import { Type, - elementType, kConcreteIntegerScalarsAndVectors, kConvertableToFloatScalarsAndVectors, + scalarTypeOf, } from '../../../../../util/conversion.js'; import { fpTraitsFor } from '../../../../../util/floating_point.js'; import { ShaderValidationTest } from '../../../shader_validation_test.js'; @@ -47,7 +47,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec ) ) .beforeAllSubcases(t => { - if (elementType(kValuesTypes[t.params.type]) === Type.f16) { + if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { t.selectDeviceOrSkipTestCase('shader-f16'); } }) @@ -55,7 +55,7 @@ Validates that constant evaluation and override evaluation of ${builtin}() rejec const type = kValuesTypes[t.params.type]; const fp = fpTraitsFor( // AbstractInt is converted to AbstractFloat before calling into the builtin - elementType(type).kind === 'abstract-int' ? Type.abstractFloat : elementType(type) + scalarTypeOf(type).kind === 'abstract-int' ? Type.abstractFloat : scalarTypeOf(type) ); const smallestPositive = fp.constants().positive.min; const v = fp.quantize(Number(t.params.value)); diff --git a/src/webgpu/util/conversion.ts b/src/webgpu/util/conversion.ts index 2b9e7989bcdb..a44f34cedfeb 100644 --- a/src/webgpu/util/conversion.ts +++ b/src/webgpu/util/conversion.ts @@ -616,6 +616,10 @@ export class ScalarType { return this._size; } + public get alignment(): number { + return this._size; + } + /** Constructs a ScalarValue of this type with `value` */ public create(value: number | bigint): ScalarValue { switch (typeof value) { @@ -663,14 +667,17 @@ export class VectorType { readonly width: number; // Number of elements in the vector readonly elementType: ScalarType; // Element type + // Maps a string representation of a vector type to vector type. + private static instances = new Map(); + static create(width: number, elementType: ScalarType): VectorType { const key = `${elementType.toString()} ${width}}`; - let ty = vectorTypes.get(key); + let ty = this.instances.get(key); if (ty !== undefined) { return ty; } ty = new VectorType(width, elementType); - vectorTypes.set(key, ty); + this.instances.set(key, ty); return ty; } @@ -700,6 +707,14 @@ export class VectorType { return this.elementType.size * this.width; } + public get alignment(): number { + return VectorType.alignmentOf(this.width, this.elementType); + } + + public static alignmentOf(width: number, elementType: ScalarType) { + return elementType.size * (width === 3 ? 4 : width); + } + /** Constructs a Vector of this type with the given values */ public create(value: (number | bigint) | readonly (number | bigint)[]): VectorValue { if (value instanceof Array) { @@ -711,23 +726,23 @@ export class VectorType { } } -// Maps a string representation of a vector type to vector type. -const vectorTypes = new Map(); - /** MatrixType describes the type of WGSL Matrix. */ export class MatrixType { readonly cols: number; // Number of columns in the Matrix readonly rows: number; // Number of elements per column in the Matrix readonly elementType: ScalarType; // Element type + // Maps a string representation of a Matrix type to Matrix type. + private static instances = new Map(); + static create(cols: number, rows: number, elementType: ScalarType): MatrixType { const key = `${elementType.toString()} ${cols} ${rows}`; - let ty = matrixTypes.get(key); + let ty = this.instances.get(key); if (ty !== undefined) { return ty; } ty = new MatrixType(cols, rows, elementType); - matrixTypes.set(key, ty); + this.instances.set(key, ty); return ty; } @@ -767,6 +782,14 @@ export class MatrixType { return `mat${this.cols}x${this.rows}<${this.elementType}>`; } + public get size(): number { + return VectorType.alignmentOf(this.rows, this.elementType) * this.cols; + } + + public get alignment(): number { + return VectorType.alignmentOf(this.rows, this.elementType); + } + /** Constructs a Matrix of this type with the given values */ public create(value: (number | bigint) | readonly (number | bigint)[]): MatrixValue { if (value instanceof Array) { @@ -783,8 +806,55 @@ export class MatrixType { } } -// Maps a string representation of a Matrix type to Matrix type. -const matrixTypes = new Map(); +/** ArrayType describes the type of WGSL Array. */ +export class ArrayType { + readonly count: number; // Number of elements in the array + readonly elementType: Type; // Element type + + // Maps a string representation of a array type to array type. + private static instances = new Map(); + + static create(count: number, elementType: Type): ArrayType { + const key = `${elementType.toString()} ${count}`; + let ty = this.instances.get(key); + if (ty !== undefined) { + return ty; + } + ty = new ArrayType(count, elementType); + this.instances.set(key, ty); + return ty; + } + + constructor(count: number, elementType: Type) { + this.count = count; + this.elementType = elementType; + } + + /** + * @returns a array constructed from the values read from the buffer at the + * given byte offset + */ + public read(buf: Uint8Array, offset: number): ArrayValue { + const elements: Array = []; + for (let i = 0; i < this.count; i++) { + elements[i] = this.elementType.read(buf, offset); + offset += this.elementType.size; + } + return new ArrayValue(elements); + } + + public toString(): string { + return `array<${this.elementType}, ${this.count}>`; + } + + public get size(): number { + return this.elementType.alignment * this.count; + } + + public get alignment(): number { + return this.elementType.alignment; + } +} /** ArrayElementType infers the element type of the indexable type A */ type ArrayElementType = A extends { [index: number]: infer T } ? T : never; @@ -838,8 +908,8 @@ const boolType = new ScalarType('bool', 4, (buf: Uint8Array, offset: number) => bool(valueFromBytes(workingDataU32, buf, offset) !== 0) ); -/** Type is a ScalarType, VectorType, or MatrixType. */ -export type Type = ScalarType | VectorType | MatrixType; +/** Type is a ScalarType, VectorType, MatrixType or ArrayType. */ +export type Type = ScalarType | VectorType | MatrixType | ArrayType; /** Type holds pre-declared Types along with helper constructor functions. */ export const Type = { @@ -894,6 +964,8 @@ export const Type = { mat3x4h: MatrixType.create(3, 4, f16Type), mat4x4f: MatrixType.create(4, 4, f32Type), mat4x4h: MatrixType.create(4, 4, f16Type), + + array: (count: number, elementType: Type) => ArrayType.create(count, elementType), }; /** @returns the ScalarType from the ScalarKind */ @@ -937,11 +1009,31 @@ export function numElementsOf(ty: Type): number { if (ty instanceof MatrixType) { return ty.cols * ty.rows; } + if (ty instanceof ArrayType) { + return ty.count; + } throw new Error(`unhandled type ${ty}`); } /** @returns the scalar elements of the given Value */ -export function elementsOf(value: Value): ScalarValue[] { +export function elementsOf(value: Value): Value[] { + if (isScalarValue(value)) { + return [value]; + } + if (value instanceof VectorValue) { + return value.elements; + } + if (value instanceof MatrixValue) { + return value.elements.flat(); + } + if (value instanceof ArrayValue) { + return value.elements; + } + throw new Error(`unhandled value ${value}`); +} + +/** @returns the scalar elements of the given Value */ +export function scalarElementsOf(value: Value): ScalarValue[] { if (isScalarValue(value)) { return [value]; } @@ -951,9 +1043,20 @@ export function elementsOf(value: Value): ScalarValue[] { if (value instanceof MatrixValue) { return value.elements.flat(); } + if (value instanceof ArrayValue) { + return value.elements.map(els => scalarElementsOf(els)).flat(); + } throw new Error(`unhandled value ${value}`); } +/** @returns the inner element type of the given type */ +export function elementTypeOf(t: Type) { + if (t instanceof ScalarType) { + return t; + } + return t.elementType; +} + /** @returns the scalar (element) type of the given Type */ export function scalarTypeOf(ty: Type): ScalarType { if (ty instanceof ScalarType) { @@ -965,6 +1068,9 @@ export function scalarTypeOf(ty: Type): ScalarType { if (ty instanceof MatrixType) { return ty.elementType; } + if (ty instanceof ArrayType) { + return scalarTypeOf(ty.elementType); + } throw new Error(`unhandled type ${ty}`); } @@ -1754,6 +1860,48 @@ export class MatrixValue { } } +/** + * Class that encapsulates an Array value. + */ +export class ArrayValue { + readonly elements: Value[]; + readonly type: ArrayType; + + public constructor(elements: Array) { + const elem_type = elements[0].type; + if (!elements.every(c => elements.every(r => objectEquals(r.type, elem_type)))) { + throw new Error(`cannot mix array element types`); + } + + this.elements = elements; + this.type = ArrayType.create(elements.length, elem_type); + } + + /** + * Copies the matrix value to the Uint8Array buffer at the provided byte offset. + * @param buffer the destination buffer + * @param offset the byte offset within buffer + */ + public copyTo(buffer: Uint8Array, offset: number) { + for (const element of this.elements) { + element.copyTo(buffer, offset); + offset += this.type.elementType.size; + } + } + + /** + * @returns the WGSL representation of this matrix value + */ + public wgsl(): string { + const els = this.elements.map(r => r.wgsl()).join(', '); + return `${this.type}(${els})`; + } + + public toString(): string { + return this.wgsl(); + } +} + /** * Helper for constructing Matrices from arrays of numbers * @@ -1776,8 +1924,8 @@ export function toMatrix(m: ROArrayArray, op: (n: number) => ScalarValue return new MatrixValue(elements); } -/** Value is a Scalar, Vector or Matrix value. */ -export type Value = ScalarValue | VectorValue | MatrixValue; +/** Value is a Scalar, Vector, Matrix or Array value. */ +export type Value = ScalarValue | VectorValue | MatrixValue | ArrayValue; export type SerializedScalarValue = { kind: 'scalar'; @@ -2160,11 +2308,3 @@ export const kAllScalarsAndVectors = [ ...kConvertableToFloatScalarsAndVectors, ...kConcreteIntegerScalarsAndVectors, ] as const; - -/** @returns the inner element type of the given type */ -export function elementType(t: ScalarType | VectorType | MatrixType) { - if (t instanceof ScalarType) { - return t; - } - return t.elementType; -} From 0a68bf71fb07b7ecd117d84d919fbaf81e302403 Mon Sep 17 00:00:00 2001 From: Greggman Date: Thu, 7 Mar 2024 20:59:20 -0800 Subject: [PATCH 13/13] Add AdapterLimitsGPUTest that sets all limits to the adapter's (#3466) Note: I'm not sure how I refactored device_pool.ts is good but, the old code created one initial device with no descriptor the first time in acquire. It then created another of the type actually wanted by the user. That no longer works becauase the adapter is passed in and so if the first requestDevice succeeds, then the adapter has been used and can't be used for the second one. Also, assuming this change is approved, I can (and should?) probably refactor LimitsTest to use this vs how it's doing it now. That would end up using cached devices. --- .../api/operation/rendering/draw.spec.ts | 4 +- src/webgpu/gpu_test.ts | 90 +++++++++++++++++-- src/webgpu/util/device_pool.ts | 32 ++++--- 3 files changed, 104 insertions(+), 22 deletions(-) diff --git a/src/webgpu/api/operation/rendering/draw.spec.ts b/src/webgpu/api/operation/rendering/draw.spec.ts index 6ed4be08fd24..fc8fdf2224d1 100644 --- a/src/webgpu/api/operation/rendering/draw.spec.ts +++ b/src/webgpu/api/operation/rendering/draw.spec.ts @@ -11,10 +11,10 @@ import { TypedArrayBufferView, TypedArrayBufferViewConstructor, } from '../../../../common/util/util.js'; -import { GPUTest, TextureTestMixin } from '../../../gpu_test.js'; +import { AdapterLimitsGPUTest, TextureTestMixin } from '../../../gpu_test.js'; import { PerPixelComparison } from '../../../util/texture/texture_ok.js'; -class DrawTest extends TextureTestMixin(GPUTest) { +class DrawTest extends TextureTestMixin(AdapterLimitsGPUTest) { checkTriangleDraw(opts: { firstIndex: number | undefined; count: number; diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts index 1f021218d290..a2ae8f1b901f 100644 --- a/src/webgpu/gpu_test.ts +++ b/src/webgpu/gpu_test.ts @@ -93,11 +93,47 @@ export function initUncanonicalizedDeviceDescriptor( } } +/** + * Gets the adapter limits as a standard JavaScript object. + */ +function getAdapterLimitsAsDeviceRequiredLimits(adapter: GPUAdapter) { + const requiredLimits: Record = {}; + const adapterLimits = adapter.limits as unknown as Record; + for (const key in adapter.limits) { + requiredLimits[key] = adapterLimits[key]; + } + return requiredLimits; +} + +/** + * Conditionally applies adapter limits to device descriptor + * but does not overwrite existing requested limits. + */ +function conditionallyApplyAdapterLimitsToDeviceDescriptor( + adapter: GPUAdapter, + useAdapterLimits: boolean, + descriptor: UncanonicalizedDeviceDescriptor | undefined +): UncanonicalizedDeviceDescriptor { + return { + ...(descriptor || {}), + requiredLimits: { + ...(useAdapterLimits && getAdapterLimitsAsDeviceRequiredLimits(adapter)), + ...(descriptor?.requiredLimits || {}), + }, + }; +} + export class GPUTestSubcaseBatchState extends SubcaseBatchState { /** Provider for default device. */ private provider: Promise | undefined; /** Provider for mismatched device. */ private mismatchedProvider: Promise | undefined; + /** True if device should be created with adapter limits */ + private useAdapterLimits = false; + + constructor(recorder: TestCaseRecorder, params: TestParams) { + super(recorder, params); + } override async postInit(): Promise { // Skip all subcases if there's no device. @@ -123,6 +159,14 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState { return this.provider; } + useAdapterLimitsForDevice() { + assert( + this.provider === undefined, + 'useAdapterLimits must be called before getting the device' + ); + this.useAdapterLimits = true; + } + get isCompatibility() { return globalTestConfig.compatibility; } @@ -140,10 +184,18 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState { */ selectDeviceOrSkipTestCase(descriptor: DeviceSelectionDescriptor): void { assert(this.provider === undefined, "Can't selectDeviceOrSkipTestCase() multiple times"); - this.provider = devicePool.acquire( - this.recorder, - initUncanonicalizedDeviceDescriptor(descriptor) - ); + this.provider = devicePool + .requestAdapter(this.recorder) + .then(adapter => + devicePool.acquire( + adapter, + conditionallyApplyAdapterLimitsToDeviceDescriptor( + adapter, + this.useAdapterLimits, + initUncanonicalizedDeviceDescriptor(descriptor) + ) + ) + ); // Suppress uncaught promise rejection (we'll catch it later). this.provider.catch(() => {}); } @@ -201,10 +253,18 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState { "Can't selectMismatchedDeviceOrSkipTestCase() multiple times" ); - this.mismatchedProvider = mismatchedDevicePool.acquire( - this.recorder, - initUncanonicalizedDeviceDescriptor(descriptor) - ); + this.mismatchedProvider = mismatchedDevicePool + .requestAdapter(this.recorder) + .then(adapter => + mismatchedDevicePool.acquire( + adapter, + conditionallyApplyAdapterLimitsToDeviceDescriptor( + adapter, + this.useAdapterLimits, + initUncanonicalizedDeviceDescriptor(descriptor) + ) + ) + ); // Suppress uncaught promise rejection (we'll catch it later). this.mismatchedProvider.catch(() => {}); } @@ -1203,6 +1263,20 @@ export class GPUTest extends GPUTestBase { } } +/** + * A version of GPUTest that requires the adapter limits. + */ +export class AdapterLimitsGPUTest extends GPUTest { + public static override MakeSharedState( + recorder: TestCaseRecorder, + params: TestParams + ): GPUTestSubcaseBatchState { + const state = new GPUTestSubcaseBatchState(recorder, params); + state.useAdapterLimitsForDevice(); + return state; + } +} + /** * Texture expectation mixin can be applied on top of GPUTest to add texture * related expectation helpers. diff --git a/src/webgpu/util/device_pool.ts b/src/webgpu/util/device_pool.ts index 4e45aac76bb8..d0d1e6c0b6b5 100644 --- a/src/webgpu/util/device_pool.ts +++ b/src/webgpu/util/device_pool.ts @@ -24,16 +24,27 @@ export class TestOOMedShouldAttemptGC extends Error {} export class DevicePool { private holders: 'uninitialized' | 'failed' | DescriptorToHolderMap = 'uninitialized'; + async requestAdapter(recorder: TestCaseRecorder) { + const gpu = getGPU(recorder); + const adapter = await gpu.requestAdapter(); + assert(adapter !== null, 'requestAdapter returned null'); + return adapter; + } + /** Acquire a device from the pool and begin the error scopes. */ async acquire( - recorder: TestCaseRecorder, + adapter: GPUAdapter, descriptor?: UncanonicalizedDeviceDescriptor ): Promise { - let errorMessage = ''; + let holder; if (this.holders === 'uninitialized') { this.holders = new DescriptorToHolderMap(); + } + + let errorMessage = ''; + if (this.holders !== 'failed') { try { - await this.holders.getOrCreate(recorder, undefined); + holder = await this.holders.getOrCreate(adapter, descriptor); } catch (ex) { this.holders = 'failed'; if (ex instanceof Error) { @@ -46,9 +57,7 @@ export class DevicePool { this.holders !== 'failed', `WebGPU device failed to initialize${errorMessage}; not retrying` ); - - const holder = await this.holders.getOrCreate(recorder, descriptor); - + assert(!!holder); assert(holder.state === 'free', 'Device was in use on DevicePool.acquire'); holder.state = 'acquired'; holder.beginTestScope(); @@ -138,7 +147,7 @@ class DescriptorToHolderMap { * Throws SkipTestCase if devices with this descriptor are unsupported. */ async getOrCreate( - recorder: TestCaseRecorder, + adapter: GPUAdapter, uncanonicalizedDescriptor: UncanonicalizedDeviceDescriptor | undefined ): Promise { const [descriptor, key] = canonicalizeDescriptor(uncanonicalizedDescriptor); @@ -163,7 +172,7 @@ class DescriptorToHolderMap { // No existing item was found; add a new one. let value; try { - value = await DeviceHolder.create(recorder, descriptor); + value = await DeviceHolder.create(adapter, descriptor); } catch (ex) { if (ex instanceof FeaturesNotSupported) { this.unsupported.add(key); @@ -298,15 +307,14 @@ class DeviceHolder implements DeviceProvider { // Gets a device and creates a DeviceHolder. // If the device is lost, DeviceHolder.lost gets set. static async create( - recorder: TestCaseRecorder, + adapter: GPUAdapter, descriptor: CanonicalDeviceDescriptor | undefined ): Promise { - const gpu = getGPU(recorder); - const adapter = await gpu.requestAdapter(); - assert(adapter !== null, 'requestAdapter returned null'); + assert(adapter !== null, 'requestAdapter is null'); if (!supportsFeature(adapter, descriptor)) { throw new FeaturesNotSupported('One or more features are not supported'); } + const device = await adapter.requestDevice(descriptor); assert(device !== null, 'requestDevice returned null');