Skip to content

Commit

Permalink
wgsl: F16 unary conversion expression execution tests (gpuweb#2965)
Browse files Browse the repository at this point in the history
This PR add execution tests for f16 unary conversion expression,
and also add f16-to-f32 tests.

Issue: gpuweb#2302
  • Loading branch information
jzm-intel authored Sep 20, 2023
1 parent cff8019 commit 808711e
Show file tree
Hide file tree
Showing 3 changed files with 397 additions and 130 deletions.
10 changes: 9 additions & 1 deletion src/webgpu/listing_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -1443,9 +1443,17 @@
"webgpu:shader,execution,expression,unary,bool_conversion:u32:*": { "subcaseMS": 7.401 },
"webgpu:shader,execution,expression,unary,bool_logical:negation:*": { "subcaseMS": 6.413 },
"webgpu:shader,execution,expression,unary,f16_arithmetic:negation:*": { "subcaseMS": 117.604 },
"webgpu:shader,execution,expression,unary,f16_conversion:bool:*": { "subcaseMS": 34.694 },
"webgpu:shader,execution,expression,unary,f16_conversion:f16:*": { "subcaseMS": 36.013 },
"webgpu:shader,execution,expression,unary,f16_conversion:f16_mat:*": { "subcaseMS": 47.109 },
"webgpu:shader,execution,expression,unary,f16_conversion:f32:*": { "subcaseMS": 30.900 },
"webgpu:shader,execution,expression,unary,f16_conversion:f32_mat:*": { "subcaseMS": 37.820 },
"webgpu:shader,execution,expression,unary,f16_conversion:i32:*": { "subcaseMS": 24.557 },
"webgpu:shader,execution,expression,unary,f16_conversion:u32:*": { "subcaseMS": 84.500 },
"webgpu:shader,execution,expression,unary,f32_arithmetic:negation:*": { "subcaseMS": 16.400 },
"webgpu:shader,execution,expression,unary,f32_conversion:bool:*": { "subcaseMS": 7.182 },
"webgpu:shader,execution,expression,unary,f32_conversion:f16:*": { "subcaseMS": 15.908 },
"webgpu:shader,execution,expression,unary,f32_conversion:f16:*": { "subcaseMS": 107.463 },
"webgpu:shader,execution,expression,unary,f32_conversion:f16_mat:*": { "subcaseMS": 60.170 },
"webgpu:shader,execution,expression,unary,f32_conversion:f32:*": { "subcaseMS": 7.538 },
"webgpu:shader,execution,expression,unary,f32_conversion:f32_mat:*": { "subcaseMS": 7.759 },
"webgpu:shader,execution,expression,unary,f32_conversion:i32:*": { "subcaseMS": 7.701 },
Expand Down
301 changes: 301 additions & 0 deletions src/webgpu/shader/execution/expression/unary/f16_conversion.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
export const description = `
Execution Tests for the f32 conversion operations
`;

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { GPUTest } from '../../../../gpu_test.js';
import {
bool,
f16,
i32,
TypeBool,
TypeF32,
TypeF16,
TypeI32,
TypeMat,
TypeU32,
u32,
} from '../../../../util/conversion.js';
import { FP, FPInterval } from '../../../../util/floating_point.js';
import {
fullF32Range,
fullF16Range,
fullI32Range,
fullU32Range,
sparseMatrixF32Range,
sparseMatrixF16Range,
} from '../../../../util/math.js';
import { makeCaseCache } from '../case_cache.js';
import { allInputSources, run, ShaderBuilder } from '../expression.js';

import { unary } from './unary.js';

export const g = makeTestGroup(GPUTest);

const f16FiniteRangeInterval = new FPInterval(
'f32',
FP.f16.constants().negative.min,
FP.f16.constants().positive.max
);

// Cases: f32_matCxR_[non_]const
// Note that f32 values may be not exactly representable in f16 and/or out of range.
const f32_mat_cases = ([2, 3, 4] as const)
.flatMap(cols =>
([2, 3, 4] as const).flatMap(rows =>
([true, false] as const).map(nonConst => ({
[`f32_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => {
return FP.f32.generateMatrixToMatrixCases(
sparseMatrixF32Range(cols, rows),
nonConst ? 'unfiltered' : 'finite',
FP.f16.correctlyRoundedMatrix
);
},
}))
)
)
.reduce((a, b) => ({ ...a, ...b }), {});

// Cases: f16_matCxR_[non_]const
const f16_mat_cases = ([2, 3, 4] as const)
.flatMap(cols =>
([2, 3, 4] as const).flatMap(rows =>
([true, false] as const).map(nonConst => ({
[`f16_mat${cols}x${rows}_${nonConst ? 'non_const' : 'const'}`]: () => {
// Input matrix is of f16 types, use f16.generateMatrixToMatrixCases.
return FP.f16.generateMatrixToMatrixCases(
sparseMatrixF16Range(cols, rows),
nonConst ? 'unfiltered' : 'finite',
FP.f16.correctlyRoundedMatrix
);
},
}))
)
)
.reduce((a, b) => ({ ...a, ...b }), {});

export const d = makeCaseCache('unary/f16_conversion', {
bool: () => {
return [
{ input: bool(true), expected: f16(1.0) },
{ input: bool(false), expected: f16(0.0) },
];
},
u32_non_const: () => {
return [...fullU32Range(), 65504].map(u => {
return { input: u32(u), expected: FP.f16.correctlyRoundedInterval(u) };
});
},
u32_const: () => {
return [...fullU32Range(), 65504]
.filter(v => f16FiniteRangeInterval.contains(v))
.map(u => {
return { input: u32(u), expected: FP.f16.correctlyRoundedInterval(u) };
});
},
i32_non_const: () => {
return [...fullI32Range(), 65504, -65504].map(i => {
return { input: i32(i), expected: FP.f16.correctlyRoundedInterval(i) };
});
},
i32_const: () => {
return [...fullI32Range(), 65504, -65504]
.filter(v => f16FiniteRangeInterval.contains(v))
.map(i => {
return { input: i32(i), expected: FP.f16.correctlyRoundedInterval(i) };
});
},
// Note that f32 values may be not exactly representable in f16 and/or out of range.
f32_non_const: () => {
return FP.f32.generateScalarToIntervalCases(
[...fullF32Range(), 65535.996, -65535.996],
'unfiltered',
FP.f16.correctlyRoundedInterval
);
},
f32_const: () => {
return FP.f32.generateScalarToIntervalCases(
[...fullF32Range(), 65535.996, -65535.996],
'finite',
FP.f16.correctlyRoundedInterval
);
},
// All f16 values are exactly representable in f16.
f16: () => {
return fullF16Range().map(f => {
return { input: f16(f), expected: FP.f16.correctlyRoundedInterval(f) };
});
},
...f32_mat_cases,
...f16_mat_cases,
});

/** Generate a ShaderBuilder based on how the test case is to be vectorized */
function vectorizeToExpression(vectorize: undefined | 2 | 3 | 4): ShaderBuilder {
return vectorize === undefined ? unary('f16') : unary(`vec${vectorize}<f16>`);
}

/** Generate a ShaderBuilder for a matrix of the provided dimensions */
function matrixExperession(cols: number, rows: number): ShaderBuilder {
return unary(`mat${cols}x${rows}<f16>`);
}

g.test('bool')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(
`
f16(e), where e is a bool
The result is 1.0 if e is true and 0.0 otherwise
`
)
.params(u =>
u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
)
.beforeAllSubcases(t => {
t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] });
})
.fn(async t => {
const cases = await d.get('bool');
await run(t, vectorizeToExpression(t.params.vectorize), [TypeBool], TypeF16, t.params, cases);
});

g.test('u32')
.specURL('https://www.w3.org/TR/WGSL/#bool-builtin')
.desc(
`
f16(e), where e is a u32
Converted to f16, +/-Inf if out of range
`
)
.params(u =>
u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
)
.beforeAllSubcases(t => {
t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] });
})
.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);
});

g.test('i32')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(
`
f16(e), where e is a i32
Converted to f16, +/-Inf if out of range
`
)
.params(u =>
u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
)
.beforeAllSubcases(t => {
t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] });
})
.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);
});

g.test('f32')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(
`
f16(e), where e is a f32
Correctly rounded to f16
`
)
.params(u =>
u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
)
.beforeAllSubcases(t => {
t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-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);
});

g.test('f32_mat')
.specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
.desc(`f32 matrix to f16 matrix tests`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('cols', [2, 3, 4] as const)
.combine('rows', [2, 3, 4] as const)
)
.beforeAllSubcases(t => {
t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] });
})
.fn(async t => {
const cols = t.params.cols;
const rows = t.params.rows;
const cases = await d.get(
t.params.inputSource === 'const'
? `f32_mat${cols}x${rows}_const`
: `f32_mat${cols}x${rows}_non_const`
);
await run(
t,
matrixExperession(cols, rows),
[TypeMat(cols, rows, TypeF32)],
TypeMat(cols, rows, TypeF16),
t.params,
cases
);
});

g.test('f16')
.specURL('https://www.w3.org/TR/WGSL/#value-constructor-builtin-function')
.desc(
`
f16(e), where e is a f16
Identical.
`
)
.params(u =>
u.combine('inputSource', allInputSources).combine('vectorize', [undefined, 2, 3, 4] as const)
)
.beforeAllSubcases(t => {
t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] });
})
.fn(async t => {
const cases = await d.get('f16');
await run(t, vectorizeToExpression(t.params.vectorize), [TypeF16], TypeF16, t.params, cases);
});

g.test('f16_mat')
.specURL('https://www.w3.org/TR/WGSL/#matrix-builtin-functions')
.desc(`f16 matrix to f16 matrix tests, expected identical`)
.params(u =>
u
.combine('inputSource', allInputSources)
.combine('cols', [2, 3, 4] as const)
.combine('rows', [2, 3, 4] as const)
)
.beforeAllSubcases(t => {
t.selectDeviceOrSkipTestCase({ requiredFeatures: ['shader-f16'] });
})
.fn(async t => {
const cols = t.params.cols;
const rows = t.params.rows;
const cases = await d.get(
t.params.inputSource === 'const'
? `f16_mat${cols}x${rows}_const`
: `f16_mat${cols}x${rows}_non_const`
);
await run(
t,
matrixExperession(cols, rows),
[TypeMat(cols, rows, TypeF16)],
TypeMat(cols, rows, TypeF16),
t.params,
cases
);
});
Loading

0 comments on commit 808711e

Please sign in to comment.