From a17d71da10b513be9f42342337f1ac07d9f17431 Mon Sep 17 00:00:00 2001 From: David Neto Date: Wed, 14 Aug 2024 12:45:57 -0400 Subject: [PATCH] fix smoothstep execution: only compute valid cases when const (#3901) For const cases, low < high is required See https://github.com/gpuweb/gpuweb/pull/4616 Bug: crbug.com/351378281 --- .../call/builtin/smoothstep.spec.ts | 36 ++++++++++++++++--- .../call/builtin/smoothstep.spec.ts | 9 +++-- 2 files changed, 39 insertions(+), 6 deletions(-) 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 42d8d09ff569..f65bb951bf25 100644 --- a/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts +++ b/src/webgpu/shader/execution/expression/call/builtin/smoothstep.spec.ts @@ -7,11 +7,16 @@ T is S or vecN Returns the smooth Hermite interpolation between 0 and 1. Component-wise when T is a vector. For scalar T, the result is t * t * (3.0 - 2.0 * t), where t = clamp((x - low) / (high - low), 0.0, 1.0). + +If low >= high: +* It is a shader-creation error if low and high are const-expressions. +* It is a pipeline-creation error if low and high are override-expressions. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; import { GPUTest } from '../../../../../gpu_test.js'; -import { Type } from '../../../../../util/conversion.js'; +import { ScalarValue, Type, Value } from '../../../../../util/conversion.js'; +import { Case } from '../../case.js'; import { allInputSources, onlyConstInputSource, run } from '../../expression.js'; import { abstractFloatBuiltin, builtin } from './builtin.js'; @@ -19,6 +24,13 @@ import { d } from './smoothstep.cache.js'; export const g = makeTestGroup(GPUTest); +// Returns true if `c` is valid for a const evaluation of smoothstep. +function validForConst(c: Case): boolean { + const low = (c.input as Value[])[0] as ScalarValue; + const high = (c.input as Value[])[1] as ScalarValue; + return low.value < high.value; +} + g.test('abstract_float') .specURL('https://www.w3.org/TR/WGSL/#float-builtin-functions') .desc(`abstract float tests`) @@ -28,7 +40,7 @@ g.test('abstract_float') .combine('vectorize', [undefined, 2, 3, 4] as const) ) .fn(async t => { - const cases = await d.get('abstract_const'); + const cases = (await d.get('abstract_const')).filter(c => validForConst(c)); await run( t, abstractFloatBuiltin('smoothstep'), @@ -47,7 +59,15 @@ 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'), [Type.f32, Type.f32, Type.f32], Type.f32, t.params, cases); + const validCases = cases.filter(c => t.params.inputSource !== 'const' || validForConst(c)); + await run( + t, + builtin('smoothstep'), + [Type.f32, Type.f32, Type.f32], + Type.f32, + t.params, + validCases + ); }); g.test('f16') @@ -61,5 +81,13 @@ 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'), [Type.f16, Type.f16, Type.f16], Type.f16, t.params, cases); + const validCases = cases.filter(c => t.params.inputSource !== 'const' || validForConst(c)); + await run( + t, + builtin('smoothstep'), + [Type.f16, Type.f16, Type.f16], + Type.f16, + t.params, + validCases + ); }); diff --git a/src/webgpu/shader/validation/expression/call/builtin/smoothstep.spec.ts b/src/webgpu/shader/validation/expression/call/builtin/smoothstep.spec.ts index d9dd63b85bf3..2879055ab216 100644 --- a/src/webgpu/shader/validation/expression/call/builtin/smoothstep.spec.ts +++ b/src/webgpu/shader/validation/expression/call/builtin/smoothstep.spec.ts @@ -80,6 +80,8 @@ g.test('partial_eval_errors') .beginSubcases() .expand('low', u => [0, 10]) .expand('high', u => [0, 10]) + // in_shader: Is the function call statically accessed by the entry point? + .combine('in_shader', [false, true] as const) ) .beforeAllSubcases(t => { if (scalarTypeOf(kValuesTypes[t.params.type]) === Type.f16) { @@ -129,7 +131,10 @@ fn foo() { const shader_error = error && t.params.lowStage === 'constant' && t.params.highStage === 'constant'; const pipeline_error = - error && t.params.lowStage !== 'runtime' && t.params.highStage !== 'runtime'; + t.params.in_shader && + error && + t.params.lowStage !== 'runtime' && + t.params.highStage !== 'runtime'; t.expectCompileResult(!shader_error, wgsl); if (!shader_error) { const constants: Record = {}; @@ -140,7 +145,7 @@ fn foo() { code: wgsl, constants, reference: ['o_low', 'o_high'], - statements: ['foo();'], + statements: t.params.in_shader ? ['foo();'] : [], }); } });