Skip to content

Commit

Permalink
Add validation tests on the access of the alpha channel of Src1 (#3886)
Browse files Browse the repository at this point in the history
* Add validation tests on the access of the alpha channel of Src1

This patch adds validation tests on the use of `src1-alpha` in the
blend factor. When the blend factor uses the alpha channel of src1,
the fragment output must be vec4.

This patch also implements function `IsDualSourceBlendingFactor()`
to check if a blend factor needs the extension "dual-source-blending".

This patch also adds the support of dual source blending in the helper
function `getFragmentShaderCodeWithOutput()` to simplify the shaders
used in dual source blending tests.

* Fix `IsDualSourceBlendingFactor()`

* Allow `IsDualSourceBlendingFactor()` accepts null value as parameter

* More fixes
  • Loading branch information
Jiawei-Shao authored Aug 2, 2024
1 parent 8ab018e commit 9cf0129
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 79 deletions.
10 changes: 5 additions & 5 deletions src/webgpu/api/operation/rendering/color_target_state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ TODO:
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { assert, TypedArrayBufferView, unreachable } from '../../../../common/util/util.js';
import {
IsDualSourceBlendingFactor,
kBlendFactors,
kDualSourceBlendingFactorsSet,
kBlendOperations,
} from '../../../capability_info.js';
import { GPUConst } from '../../../constants.js';
Expand Down Expand Up @@ -203,8 +203,8 @@ g.test('blending,GPUBlendComponent')
)
.beforeAllSubcases(t => {
if (
kDualSourceBlendingFactorsSet.has(t.params.srcFactor) ||
kDualSourceBlendingFactorsSet.has(t.params.dstFactor)
IsDualSourceBlendingFactor(t.params.srcFactor) ||
IsDualSourceBlendingFactor(t.params.dstFactor)
) {
t.selectDeviceOrSkipTestCase('dual-source-blending');
}
Expand Down Expand Up @@ -251,8 +251,8 @@ g.test('blending,GPUBlendComponent')
}

const useBlendSrc1 =
kDualSourceBlendingFactorsSet.has(t.params.srcFactor) ||
kDualSourceBlendingFactorsSet.has(t.params.dstFactor);
IsDualSourceBlendingFactor(t.params.srcFactor) ||
IsDualSourceBlendingFactor(t.params.dstFactor);

const pipeline = t.device.createRenderPipeline({
layout: 'auto',
Expand Down
111 changes: 48 additions & 63 deletions src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ This test dedicatedly tests validation of GPUFragmentState of createRenderPipeli
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { assert, range } from '../../../../common/util/util.js';
import {
IsDualSourceBlendingFactor,
kBlendFactors,
kDualSourceBlendingFactorsSet,
kBlendOperations,
kMaxColorAttachmentsToTest,
} from '../../../capability_info.js';
Expand Down Expand Up @@ -298,10 +298,7 @@ g.test('targets_blend')
)
.beforeAllSubcases(t => {
const { srcFactor, dstFactor } = t.params;
if (
kDualSourceBlendingFactorsSet.has(srcFactor) ||
kDualSourceBlendingFactorsSet.has(dstFactor)
) {
if (IsDualSourceBlendingFactor(srcFactor) || IsDualSourceBlendingFactor(dstFactor)) {
t.selectDeviceOrSkipTestCase('dual-source-blending');
}
})
Expand All @@ -320,20 +317,12 @@ g.test('targets_blend')
};
const format = 'rgba8unorm';
const useDualSourceBlending =
kDualSourceBlendingFactorsSet.has(srcFactor) || kDualSourceBlendingFactorsSet.has(dstFactor);
const fragmentShaderCode = useDualSourceBlending
? `enable dual_source_blending;
struct FragOutput {
@location(0) @blend_src(0) color : vec4f,
@location(0) @blend_src(1) blend : vec4f,
}
@fragment fn main() -> FragOutput {
var fragmentOutput : FragOutput;
return fragmentOutput;
}`
: undefined;
IsDualSourceBlendingFactor(srcFactor) || IsDualSourceBlendingFactor(dstFactor);
const fragmentShaderCode = getFragmentShaderCodeWithOutput(
[{ values, plainType: 'f32', componentCount: 4 }],
null,
useDualSourceBlending
);

const descriptor = t.getDescriptor({
targets: [
Expand Down Expand Up @@ -437,9 +426,6 @@ g.test('pipeline_output_targets,blend')
`On top of requirements from pipeline_output_targets, when blending is enabled and alpha channel
is read indicated by any color blend factor, an extra requirement is added:
- fragment output must be vec4.
TODO: Implement all the skipped tests about the blend factors added in the extension
"dual-source-blending"
`
)
.params(u =>
Expand All @@ -454,18 +440,23 @@ g.test('pipeline_output_targets,blend')
...u.combine('alphaSrcFactor', kBlendFactors),
...u.combine('alphaDstFactor', kBlendFactors),
] as const)
.unless(
p =>
(p.colorSrcFactor !== undefined && kDualSourceBlendingFactorsSet.has(p.colorSrcFactor)) ||
(p.colorDstFactor !== undefined && kDualSourceBlendingFactorsSet.has(p.colorDstFactor)) ||
(p.alphaSrcFactor !== undefined && kDualSourceBlendingFactorsSet.has(p.alphaSrcFactor)) ||
(p.alphaDstFactor !== undefined && kDualSourceBlendingFactorsSet.has(p.alphaDstFactor))
)
)
.beforeAllSubcases(t => {
const { format } = t.params;
const { format, colorSrcFactor, colorDstFactor, alphaSrcFactor, alphaDstFactor } = t.params;

const info = kTextureFormatInfo[format];
t.selectDeviceOrSkipTestCase(info.feature);
const requiredFeatures: (GPUFeatureName | undefined)[] = [info.feature];

if (
IsDualSourceBlendingFactor(colorSrcFactor) ||
IsDualSourceBlendingFactor(colorDstFactor) ||
IsDualSourceBlendingFactor(alphaSrcFactor) ||
IsDualSourceBlendingFactor(alphaDstFactor)
) {
requiredFeatures.push('dual-source-blending');
}

t.selectDeviceOrSkipTestCase(requiredFeatures);
})
.fn(t => {
const sampleType = 'float';
Expand All @@ -480,6 +471,12 @@ g.test('pipeline_output_targets,blend')
} = t.params;
const info = kTextureFormatInfo[format];

const useDualSourceBlending =
IsDualSourceBlendingFactor(colorSrcFactor) ||
IsDualSourceBlendingFactor(colorDstFactor) ||
IsDualSourceBlendingFactor(alphaSrcFactor) ||
IsDualSourceBlendingFactor(alphaDstFactor);

const descriptor = t.getDescriptor({
targets: [
{
Expand All @@ -490,13 +487,18 @@ g.test('pipeline_output_targets,blend')
},
},
],
fragmentShaderCode: getFragmentShaderCodeWithOutput([
{ values, plainType: getPlainTypeInfo(sampleType), componentCount },
]),
fragmentShaderCode: getFragmentShaderCodeWithOutput(
[{ values, plainType: getPlainTypeInfo(sampleType), componentCount }],
null,
useDualSourceBlending
),
});

const colorBlendReadsSrcAlpha =
colorSrcFactor?.includes('src-alpha') || colorDstFactor?.includes('src-alpha');
colorSrcFactor?.includes('src-alpha') ||
colorDstFactor?.includes('src-alpha') ||
colorSrcFactor?.includes('src1-alpha') ||
colorDstFactor?.includes('src1-alpha');
const meetsExtraBlendingRequirement = !colorBlendReadsSrcAlpha || componentCount === 4;
const _success =
info.color.type === sampleType &&
Expand Down Expand Up @@ -564,21 +566,11 @@ g.test('dual_source_blending,color_target_count')

const descriptor = t.getDescriptor({
targets: colorTargetStates,
fragmentShaderCode: `
enable dual_source_blending;
struct FragOutput {
@location(0) @blend_src(0) color : vec4f,
@location(0) @blend_src(1) blend : vec4f,
}
@fragment fn main() -> FragOutput {
var fragmentOutput : FragOutput;
fragmentOutput.color = vec4f(0.0, 1.0, 0.0, 1.0);
fragmentOutput.blend = fragmentOutput.color;
return fragmentOutput;
}
`,
fragmentShaderCode: getFragmentShaderCodeWithOutput(
[{ values, plainType: 'f32', componentCount: 4 }],
null,
true
),
});

const isAsync = false;
Expand Down Expand Up @@ -628,21 +620,14 @@ g.test('dual_source_blending,use_blend_src')
writeMask,
},
],
fragmentShaderCode: `
${useBlendSrc1 ? 'enable dual_source_blending;' : ''}
struct FragOutput {
@location(0) ${useBlendSrc1 ? '@blend_src(0)' : ''} color : vec4f,
${useBlendSrc1 ? '@location(0) @blend_src(1) blend : vec4f,' : ''}
}
@fragment fn main() -> FragOutput {
var fragmentOutput : FragOutput;
return fragmentOutput;
}
`,
fragmentShaderCode: getFragmentShaderCodeWithOutput(
[{ values, plainType: 'f32', componentCount: 4 }],
null,
useBlendSrc1
),
});

const kDualSourceBlendingFactorsSet = new Set(kDualSourceBlendingFactors);
const _success = !kDualSourceBlendingFactorsSet.has(blendFactor) || useBlendSrc1;
const _success = !IsDualSourceBlendingFactor(blendFactor) || useBlendSrc1;
const isAsync = false;
t.doCreateRenderPipelineTest(isAsync, _success, descriptor);
});
21 changes: 13 additions & 8 deletions src/webgpu/capability_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,17 +665,22 @@ export const kBlendFactors: readonly GPUBlendFactor[] = [
'one-minus-src1-alpha',
];

/** Set of all GPUBlendFactor values added in the extension "dual-source-blending". */
export const kDualSourceBlendingFactorsSet = new Set([
'src1',
'one-minus-src1',
'src1-alpha',
'one-minus-src1-alpha',
]);
/** Check if `blendFactor` belongs to the blend factors in the extension "dual-source-blending". */
export function IsDualSourceBlendingFactor(blendFactor?: GPUBlendFactor): boolean {
switch (blendFactor) {
case 'src1':
case 'one-minus-src1':
case 'src1-alpha':
case 'one-minus-src1-alpha':
return true;
default:
return false;
}
}

/** List of all GPUBlendOperation values. */
export const kBlendOperations: readonly GPUBlendOperation[] = [
'add', //
'add',
'subtract',
'reverse-subtract',
'min',
Expand Down
19 changes: 16 additions & 3 deletions src/webgpu/util/shader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { unreachable } from '../../common/util/util.js';
import { assert, unreachable } from '../../common/util/util.js';

export const kDefaultVertexShaderCode = `
@vertex fn main() -> @builtin(position) vec4<f32> {
Expand Down Expand Up @@ -109,7 +109,8 @@ export function getFragmentShaderCodeWithOutput(
plainType: 'i32' | 'u32' | 'f32';
componentCount: number;
} | null)[],
fragDepth: { value: number } | null = null
fragDepth: { value: number } | null = null,
dualSourceBlending: boolean = false
): string {
if (outputs.length === 0) {
if (fragDepth) {
Expand Down Expand Up @@ -165,10 +166,22 @@ export function getFragmentShaderCodeWithOutput(
unreachable();
}

outputStructString += `@location(${i}) o${i} : ${outputType},\n`;
if (dualSourceBlending) {
assert(i === 0 && outputs.length === 1);
outputStructString += `
@location(0) @blend_src(0) o0 : ${outputType},
@location(0) @blend_src(1) o0_blend : ${outputType},
`;
resultStrings.push(resultStrings[0]);
break;
} else {
outputStructString += `@location(${i}) o${i} : ${outputType},\n`;
}
}

return `
${dualSourceBlending ? 'enable dual_source_blending;' : ''}
struct Outputs {
${outputStructString}
}
Expand Down

0 comments on commit 9cf0129

Please sign in to comment.