diff --git a/src/webgpu/api/operation/rendering/blending.spec.ts b/src/webgpu/api/operation/rendering/blending.spec.ts index 5e5fe279e7cd..2430d28f27bf 100644 --- a/src/webgpu/api/operation/rendering/blending.spec.ts +++ b/src/webgpu/api/operation/rendering/blending.spec.ts @@ -8,7 +8,7 @@ TODO: `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; -import { assert, unreachable } from '../../../../common/util/util.js'; +import { assert, TypedArrayBufferView, unreachable } from '../../../../common/util/util.js'; import { kBlendFactors, kBlendOperations, @@ -20,7 +20,71 @@ import { float32ToFloat16Bits } from '../../../util/conversion.js'; import { TexelView } from '../../../util/texture/texel_view.js'; import { textureContentIsOKByT2B } from '../../../util/texture/texture_ok.js'; -export const g = makeTestGroup(GPUTest); +class BlendingTest extends GPUTest { + createRenderPipelineForTest( + colorTargetState: GPUColorTargetState, + blendComponent: GPUBlendComponent | undefined + ): GPURenderPipeline { + return this.device.createRenderPipeline({ + layout: 'auto', + fragment: { + targets: [ + { + format: colorTargetState.format, + blend: { + color: blendComponent ?? {}, + alpha: blendComponent ?? {}, + }, + }, + ], + module: this.device.createShaderModule({ + code: ` + struct Params { + color : vec4 + } + @group(0) @binding(0) var params : Params; + @fragment fn main() -> @location(0) vec4 { + return params.color; + } + `, + }), + entryPoint: 'main', + }, + vertex: { + module: this.device.createShaderModule({ + code: ` + @vertex fn main( + @builtin(vertex_index) VertexIndex : u32 + ) -> @builtin(position) vec4 { + var pos = array, 3>( + vec2(-1.0, -1.0), + vec2(3.0, -1.0), + vec2(-1.0, 3.0)); + return vec4(pos[VertexIndex], 0.0, 1.0); + } + `, + }), + entryPoint: 'main', + }, + }); + } + + createBindGroupForTest(layout: GPUBindGroupLayout, data: TypedArrayBufferView): GPUBindGroup { + return this.device.createBindGroup({ + layout, + entries: [ + { + binding: 0, + resource: { + buffer: this.makeBufferWithContents(data, GPUBufferUsage.UNIFORM), + }, + }, + ], + }); + } +} + +export const g = makeTestGroup(BlendingTest); function mapColor( col: GPUColorDict, @@ -372,6 +436,72 @@ g.test('formats') t.expectOK(result); }); +g.test('default_blend_constant,initial_blend_constant') + .desc(`Test that the blend constant is set to [0,0,0,0] at the beginning of a pass.`) + .fn(async t => { + const format = 'rgba8unorm'; + const kSize = 1; + const kWhiteColorData = new Float32Array([255, 255, 255, 255]); + const kBlackColorData = new Float32Array([0, 0, 0, 0]); + + const basePipeline = t.createRenderPipelineForTest({ format }, undefined); + const testPipeline = t.createRenderPipelineForTest( + { format }, + { + srcFactor: 'constant', + dstFactor: 'one', + operation: 'add', + } + ); + + const renderTarget = t.device.createTexture({ + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, + size: [kSize, kSize], + format, + }); + + const commandEncoder = t.device.createCommandEncoder(); + const renderPass = commandEncoder.beginRenderPass({ + colorAttachments: [ + { + view: renderTarget.createView(), + loadOp: 'load', + storeOp: 'store', + }, + ], + }); + renderPass.setPipeline(basePipeline); + renderPass.setBindGroup( + 0, + t.createBindGroupForTest(basePipeline.getBindGroupLayout(0), kBlackColorData) + ); + renderPass.setPipeline(testPipeline); + renderPass.setBindGroup( + 0, + t.createBindGroupForTest(testPipeline.getBindGroupLayout(0), kWhiteColorData) + ); + renderPass.draw(3); + // Draw [1,1,1,1] with `src * constant + dst * 1`. + // The blend constant defaults to [0,0,0,0], so the result is + // `[1,1,1,1] * [0,0,0,0] + [0,0,0,0] * 1` = [0,0,0,0]. + renderPass.end(); + t.device.queue.submit([commandEncoder.finish()]); + + // Check that the initial blend color is black(0,0,0,0) after setting testPipeline which has + // a white color buffer data. + const expColor = { R: 0, G: 0, B: 0, A: 0 }; + const expTexelView = TexelView.fromTexelsAsColors(format, coords => expColor); + + const result = await textureContentIsOKByT2B( + t, + { texture: renderTarget }, + [kSize, kSize], + { expTexelView }, + { maxDiffULPsForNormFormat: 1 } + ); + t.expectOK(result); + }); + g.test('clamp,blend_factor') .desc('For fixed-point formats, test that the blend factor is clamped in the blend equation.') .unimplemented();