Skip to content

Commit

Permalink
Add dual source blending blend factors to kBlendFactors (#3884)
Browse files Browse the repository at this point in the history
* Add dual source blending blend factors to `kBlendFactors`

This patch adds all the blend factors defined in the extension
"dual-source-blending" to `kBlendFactors` in capability_info.ts so
that all the tests about blend factors can test these newly added
blend factors.

This patch also removes the validation tests about using the alpha
channel of `Src1` because it doesn't follow the latest SPEC: only when
the color blend factor uses the alpha channel of `Src` must the fragment
output be `vec4`. When the color target doesn't have the alpha channel,
the alpha blend factor is ignored so it is OK to set it as if it "uses"
the alpha channel of `Src`.

Note that the new validation test against `Src1-alpha` will be added into
'pipeline_output_targets,blend' after the fix is landed in the browser.

* Improve the test descriptions and messages in `TODO`
  • Loading branch information
Jiawei-Shao authored Jul 31, 2024
1 parent 1654b1d commit a70b240
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 96 deletions.
20 changes: 7 additions & 13 deletions src/webgpu/api/operation/rendering/color_target_state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ TODO:

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { assert, TypedArrayBufferView, unreachable } from '../../../../common/util/util.js';
import { kBlendFactors, kBlendOperations } from '../../../capability_info.js';
import {
kBlendFactors,
kDualSourceBlendingFactorsSet,
kBlendOperations,
} from '../../../capability_info.js';
import { GPUConst } from '../../../constants.js';
import { kRegularTextureFormats, kTextureFormatInfo } from '../../../format_info.js';
import { GPUTest, TextureTestMixin } from '../../../gpu_test.js';
Expand Down Expand Up @@ -155,16 +159,6 @@ function computeBlendOperation(
}
}

const kDualSourceBlendingFactors: GPUBlendFactor[] = [
'src1',
'one-minus-src1',
'src1-alpha',
'one-minus-src1-alpha',
];
const kDualSourceBlendingFactorsSet = new Set(kDualSourceBlendingFactors);

const kAllBlendFactors = [...kBlendFactors, ...kDualSourceBlendingFactors];

g.test('blending,GPUBlendComponent')
.desc(
`Test all combinations of parameters for GPUBlendComponent.
Expand All @@ -182,8 +176,8 @@ g.test('blending,GPUBlendComponent')
.params(u =>
u //
.combine('component', ['color', 'alpha'] as const)
.combine('srcFactor', kAllBlendFactors)
.combine('dstFactor', kAllBlendFactors)
.combine('srcFactor', kBlendFactors)
.combine('dstFactor', kBlendFactors)
.beginSubcases()
.combine('operation', kBlendOperations)
.filter(t => {
Expand Down
125 changes: 42 additions & 83 deletions src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ This test dedicatedly tests validation of GPUFragmentState of createRenderPipeli
`;

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { assert, range, unreachable } from '../../../../common/util/util.js';
import { assert, range } from '../../../../common/util/util.js';
import {
kBlendFactors,
kDualSourceBlendingFactorsSet,
kBlendOperations,
kMaxColorAttachmentsToTest,
} from '../../../capability_info.js';
Expand Down Expand Up @@ -290,11 +291,20 @@ g.test('targets_blend')
u
.combine('isAsync', [false, true])
.combine('component', ['color', 'alpha'] as const)
.beginSubcases()
.combine('srcFactor', kBlendFactors)
.combine('dstFactor', kBlendFactors)
.beginSubcases()
.combine('operation', kBlendOperations)
)
.beforeAllSubcases(t => {
const { srcFactor, dstFactor } = t.params;
if (
kDualSourceBlendingFactorsSet.has(srcFactor) ||
kDualSourceBlendingFactorsSet.has(dstFactor)
) {
t.selectDeviceOrSkipTestCase('dual-source-blending');
}
})
.fn(t => {
const { isAsync, component, srcFactor, dstFactor, operation } = t.params;

Expand All @@ -309,6 +319,21 @@ g.test('targets_blend')
operation,
};
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;

const descriptor = t.getDescriptor({
targets: [
Expand All @@ -320,6 +345,7 @@ g.test('targets_blend')
},
},
],
fragmentShaderCode,
});

if (operation === 'min' || operation === 'max') {
Expand Down Expand Up @@ -408,23 +434,33 @@ g.test('pipeline_output_targets')

g.test('pipeline_output_targets,blend')
.desc(
`On top of requirements from pipeline_output_targets, when blending is enabled and alpha channel is read indicated by any blend factor, an extra requirement is added:
- fragment output must be vec4.
`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 =>
u
.combine('isAsync', [false, true])
.combine('format', ['r8unorm', 'rg8unorm', 'rgba8unorm', 'bgra8unorm'] as const)
.combine('componentCount', [1, 2, 3, 4])
.beginSubcases()
// The default srcFactor and dstFactor are 'one' and 'zero'. Override just one at a time.
.combineWithParams([
...u.combine('colorSrcFactor', kBlendFactors),
...u.combine('colorDstFactor', kBlendFactors),
...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;
Expand Down Expand Up @@ -561,7 +597,7 @@ g.test('dual_source_blending,use_blend_src')
.beforeAllSubcases(t => t.selectDeviceOrSkipTestCase('dual-source-blending'))
.params(u =>
u
.combine('blendFactor', [...kDualSourceBlendingFactors, ...kBlendFactors] as const)
.combine('blendFactor', kBlendFactors)
.combine('useBlendSrc1', [true, false] as const)
.combine('writeMask', [0, GPUConst.ColorWrite.ALL] as const)
.beginSubcases()
Expand Down Expand Up @@ -610,80 +646,3 @@ g.test('dual_source_blending,use_blend_src')
const isAsync = false;
t.doCreateRenderPipelineTest(isAsync, _success, descriptor);
});

g.test('dual_source_blending,access_src1_alpha')
.desc(
`Test that when the blend factor of color attachment 0 reads the alpha channel of src1 (the
second input of the corresponding blending unit) the fragment output must be vec4f, otherwise
there is no such restriction.
`
)
.beforeAllSubcases(t => t.selectDeviceOrSkipTestCase('dual-source-blending'))
.params(u =>
u
.combine('blendFactor', kDualSourceBlendingFactors)
.combine('fragmentOutputComponentCount', [1, 2, 4] as const)
.beginSubcases()
.combine('component', ['color', 'alpha'] as const)
)
.fn(t => {
const { blendFactor, fragmentOutputComponentCount, component } = t.params;

const defaultBlendComponent: GPUBlendComponent = {
srcFactor: 'src',
dstFactor: 'dst',
operation: 'add',
};
const testBlendComponent: GPUBlendComponent = {
srcFactor: blendFactor,
dstFactor: blendFactor,
operation: 'add',
};
let format: GPUTextureFormat = 'r8uint';
let fragmentOutputType = '';
switch (fragmentOutputComponentCount) {
case 1:
format = 'r8unorm';
fragmentOutputType = 'f32';
break;
case 2:
format = 'rg8unorm';
fragmentOutputType = 'vec2f';
break;
case 4:
format = 'rgba8unorm';
fragmentOutputType = 'vec4f';
break;
default:
unreachable();
break;
}

const descriptor = t.getDescriptor({
targets: [
{
format,
blend: {
color: component === 'color' ? testBlendComponent : defaultBlendComponent,
alpha: component === 'alpha' ? testBlendComponent : defaultBlendComponent,
},
},
],
fragmentShaderCode: `
enable dual_source_blending;
struct FragOutput {
@location(0) @blend_src(0) color : ${fragmentOutputType},
@location(0) @blend_src(1) blend : ${fragmentOutputType},
}
@fragment fn main() -> FragOutput {
var fragmentOutput : FragOutput;
return fragmentOutput;
}
`,
});

const isAsync = false;
const useSrc1Alpha = blendFactor === 'src1-alpha' || blendFactor === 'one-minus-src1-alpha';
const _success = !useSrc1Alpha || fragmentOutputComponentCount === 4;
t.doCreateRenderPipelineTest(isAsync, _success, descriptor);
});
12 changes: 12 additions & 0 deletions src/webgpu/capability_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -659,8 +659,20 @@ export const kBlendFactors: readonly GPUBlendFactor[] = [
'src-alpha-saturated',
'constant',
'one-minus-constant',
'src1',
'one-minus-src1',
'src1-alpha',
'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',
]);

/** List of all GPUBlendOperation values. */
export const kBlendOperations: readonly GPUBlendOperation[] = [
'add', //
Expand Down

0 comments on commit a70b240

Please sign in to comment.