From a70b240cee25ed4baab5a914b1eefa5832fd4c1b Mon Sep 17 00:00:00 2001 From: Jiawei Shao Date: Wed, 31 Jul 2024 16:52:16 +0800 Subject: [PATCH] Add dual source blending blend factors to `kBlendFactors` (#3884) * 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` --- .../rendering/color_target_state.spec.ts | 20 +-- .../render_pipeline/fragment_state.spec.ts | 125 ++++++------------ src/webgpu/capability_info.ts | 12 ++ 3 files changed, 61 insertions(+), 96 deletions(-) diff --git a/src/webgpu/api/operation/rendering/color_target_state.spec.ts b/src/webgpu/api/operation/rendering/color_target_state.spec.ts index e71119df8832..b5db227caa4d 100644 --- a/src/webgpu/api/operation/rendering/color_target_state.spec.ts +++ b/src/webgpu/api/operation/rendering/color_target_state.spec.ts @@ -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'; @@ -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. @@ -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 => { diff --git a/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts b/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts index 3657e8713990..43ac6464b7c5 100644 --- a/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts +++ b/src/webgpu/api/validation/render_pipeline/fragment_state.spec.ts @@ -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'; @@ -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; @@ -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: [ @@ -320,6 +345,7 @@ g.test('targets_blend') }, }, ], + fragmentShaderCode, }); if (operation === 'min' || operation === 'max') { @@ -408,8 +434,12 @@ 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 => @@ -417,7 +447,6 @@ g.test('pipeline_output_targets,blend') .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), @@ -425,6 +454,13 @@ 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; @@ -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() @@ -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); - }); diff --git a/src/webgpu/capability_info.ts b/src/webgpu/capability_info.ts index c905f1f65f53..6d8c1589209c 100644 --- a/src/webgpu/capability_info.ts +++ b/src/webgpu/capability_info.ts @@ -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', //