diff --git a/src/webgpu/api/operation/sampling/filter_mode.spec.ts b/src/webgpu/api/operation/sampling/filter_mode.spec.ts index cf1d7682e102..90fdbcca2f51 100644 --- a/src/webgpu/api/operation/sampling/filter_mode.spec.ts +++ b/src/webgpu/api/operation/sampling/filter_mode.spec.ts @@ -10,5 +10,164 @@ TODO: import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { GPUTest } from '../../../gpu_test.js'; +import { TexelView } from '../../../util/texture/texel_view.js'; +import { textureContentIsOKByT2B } from '../../../util/texture/texture_ok.js'; -export const g = makeTestGroup(GPUTest); +class FilterModeTest extends GPUTest { + createRenderPipelineForTest(): GPURenderPipeline { + return this.device.createRenderPipeline({ + layout: 'auto', + vertex: { + module: this.device.createShaderModule({ + code: ` + struct Outputs { + @builtin(position) Position : vec4, + @location(0) fragUV : vec2, + }; + + @vertex fn main( + @builtin(vertex_index) VertexIndex : u32) -> Outputs { + var position : array, 6> = array, 6>( + vec3(-0.5, 0.5, -0.5), + vec3(0.5, 0.5, -0.5), + vec3(-0.5, 0.5, 0.5), + vec3(-0.5, 0.5, 0.5), + vec3(0.5, 0.5, -0.5), + vec3(0.5, 0.5, 0.5)); + + // uv is pre-scaled to mimic repeating tiled texture + var uv : array, 6> = array, 6>( + vec2(0.0, 0.0), + vec2(1.0, 0.0), + vec2(0.0, 50.0), + vec2(0.0, 50.0), + vec2(1.0, 0.0), + vec2(1.0, 50.0)); + + // draw a slanted plane in a specific way + let matrix : mat4x4 = mat4x4( + vec4(-1.7320507764816284, 1.8322050568049563e-16, -6.176817699518044e-17, -6.170640314703498e-17), + vec4(-2.1211504944260596e-16, -1.496108889579773, 0.5043753981590271, 0.5038710236549377), + vec4(0.0, -43.63650894165039, -43.232173919677734, -43.18894577026367), + vec4(0.0, 21.693578720092773, 21.789791107177734, 21.86800193786621)); + + var output : Outputs; + output.fragUV = uv[VertexIndex]; + output.Position = matrix * vec4(position[VertexIndex], 1.0); + return output; + } + `, + }), + entryPoint: 'main', + }, + fragment: { + module: this.device.createShaderModule({ + code: ` + @group(0) @binding(0) var sampler0 : sampler; + @group(0) @binding(1) var texture0 : texture_2d; + + @fragment fn main( + @builtin(position) FragCoord : vec4, + @location(0) fragUV: vec2) + -> @location(0) vec4 { + return textureSample(texture0, sampler0, fragUV); + } + `, + }), + entryPoint: 'main', + targets: [{ format: 'rgba8unorm' }], + }, + primitive: { topology: 'triangle-list' }, + }); + } + + createBindGroupForTest( + layout: GPUBindGroupLayout, + textureView: GPUTextureView, + sampler: GPUSampler + ): GPUBindGroup { + return this.device.createBindGroup({ + layout, + entries: [ + { binding: 0, resource: sampler }, + { binding: 1, resource: textureView }, + ], + }); + } + + drawTriangle(textureView: GPUTextureView, sampler: GPUSampler) { + const renderTargetFormat = 'rgba8unorm'; + const renderTarget = this.device.createTexture({ + format: renderTargetFormat, + size: { width: 2, height: 2, depthOrArrayLayers: 1 }, + usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT, + }); + + const encoder = this.device.createCommandEncoder(); + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view: renderTarget.createView(), + storeOp: 'store', + loadOp: 'load', + }, + ], + }); + + const testPipeline = this.createRenderPipelineForTest(); + pass.setPipeline(testPipeline); + pass.setBindGroup( + 0, + this.createBindGroupForTest(testPipeline.getBindGroupLayout(0), textureView, sampler) + ); + pass.draw(6); + + pass.end(); + this.device.queue.submit([encoder.finish()]); + + const expColor = { + R: 0, + G: 0, + B: 0, + A: 0.0, + }; + const expTexelView = TexelView.fromTexelsAsColors(renderTargetFormat, coords => expColor); + + const result = textureContentIsOKByT2B( + this, + { texture: renderTarget }, + [2, 2], + { expTexelView }, + { maxDiffULPsForNormFormat: 1 } + ); + this.eventualExpectOK(result); + this.trackForCleanup(renderTarget); + } +} + +export const g = makeTestGroup(FilterModeTest); + +const kTextureFormat = 'rgba8unorm'; + +g.test('minFilter') + .desc('Test that stencil reference is initialized as zero for new render pass.') + .params(u => u.combine('magFilter', ['nearest', 'linear'] as const)) + .fn(async t => { + const { magFilter } = t.params; + + const textureSize = 2; + const texture = t.device.createTexture({ + mipLevelCount: 1, + size: { width: textureSize, height: textureSize, depthOrArrayLayers: 1 }, + format: kTextureFormat, + usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.TEXTURE_BINDING, + }); + + const sampler = t.device.createSampler({ + magFilter, + minFilter: 'linear', + mipmapFilter: 'linear', + }); + + t.drawTriangle(texture.createView(), sampler); + });