Skip to content

Commit 13d9899

Browse files
committed
op: Implement 'mipmapFilter' test in filter_mode.spec.ts
This PR adds a new test to test that mipmapFilter affects drawing the slanted plane with different filter modes. Issue: #2070
1 parent 44173ed commit 13d9899

File tree

1 file changed

+167
-1
lines changed

1 file changed

+167
-1
lines changed

src/webgpu/api/operation/sampling/filter_mode.spec.ts

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,172 @@ TODO:
99
`;
1010

1111
import { makeTestGroup } from '../../../../common/framework/test_group.js';
12+
import { assert } from '../../../../common/util/util.js';
1213
import { GPUTest } from '../../../gpu_test.js';
1314

14-
export const g = makeTestGroup(GPUTest);
15+
const kRTSize = 16;
16+
const xMiddle = kRTSize / 2; // we check the pixel value in the middle of the render target
17+
const kColorAttachmentFormat = 'rgba8unorm';
18+
const colors = [
19+
new Uint8Array([0xff, 0x00, 0x00, 0xff]), // miplevel = 0
20+
new Uint8Array([0x00, 0xff, 0x00, 0xff]), // miplevel = 1
21+
new Uint8Array([0x00, 0x00, 0xff, 0xff]), // miplevel = 2
22+
];
23+
24+
class SamplerFilterModeSlantedPlaneTest extends GPUTest {
25+
private pipeline: GPURenderPipeline | undefined;
26+
async init(): Promise<void> {
27+
await super.init();
28+
29+
this.pipeline = this.device.createRenderPipeline({
30+
layout: 'auto',
31+
vertex: {
32+
module: this.device.createShaderModule({
33+
code: `
34+
struct Outputs {
35+
@builtin(position) Position : vec4<f32>,
36+
@location(0) fragUV : vec2<f32>,
37+
};
38+
39+
@vertex fn main(
40+
@builtin(vertex_index) VertexIndex : u32) -> Outputs {
41+
var position : array<vec3<f32>, 6> = array<vec3<f32>, 6>(
42+
vec3<f32>(-0.5, 0.5, -0.5),
43+
vec3<f32>(0.5, 0.5, -0.5),
44+
vec3<f32>(-0.5, 0.5, 0.5),
45+
vec3<f32>(-0.5, 0.5, 0.5),
46+
vec3<f32>(0.5, 0.5, -0.5),
47+
vec3<f32>(0.5, 0.5, 0.5));
48+
// uv is pre-scaled to mimic repeating tiled texture
49+
var uv : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
50+
vec2<f32>(0.0, 0.0),
51+
vec2<f32>(1.0, 0.0),
52+
vec2<f32>(0.0, 50.0),
53+
vec2<f32>(0.0, 50.0),
54+
vec2<f32>(1.0, 0.0),
55+
vec2<f32>(1.0, 50.0));
56+
// draw a slanted plane in a specific way
57+
let matrix : mat4x4<f32> = mat4x4<f32>(
58+
vec4<f32>(-1.7320507764816284, 1.8322050568049563e-16, -6.176817699518044e-17, -6.170640314703498e-17),
59+
vec4<f32>(-2.1211504944260596e-16, -1.496108889579773, 0.5043753981590271, 0.5038710236549377),
60+
vec4<f32>(0.0, -43.63650894165039, -43.232173919677734, -43.18894577026367),
61+
vec4<f32>(0.0, 21.693578720092773, 21.789791107177734, 21.86800193786621));
62+
63+
var output : Outputs;
64+
output.fragUV = uv[VertexIndex];
65+
output.Position = matrix * vec4<f32>(position[VertexIndex], 1.0);
66+
return output;
67+
}
68+
`,
69+
}),
70+
entryPoint: 'main',
71+
},
72+
fragment: {
73+
module: this.device.createShaderModule({
74+
code: `
75+
@group(0) @binding(0) var sampler0 : sampler;
76+
@group(0) @binding(1) var texture0 : texture_2d<f32>;
77+
78+
@fragment fn main(
79+
@builtin(position) FragCoord : vec4<f32>,
80+
@location(0) fragUV: vec2<f32>)
81+
-> @location(0) vec4<f32> {
82+
return textureSample(texture0, sampler0, fragUV);
83+
}
84+
`,
85+
}),
86+
entryPoint: 'main',
87+
targets: [{ format: 'rgba8unorm' }],
88+
},
89+
primitive: { topology: 'triangle-list' },
90+
});
91+
}
92+
93+
// return the render target texture object
94+
drawSlantedPlane(textureView: GPUTextureView, sampler: GPUSampler): GPUTexture {
95+
// make sure it's already initialized
96+
assert(this.pipeline !== undefined);
97+
98+
const bindGroup = this.device.createBindGroup({
99+
entries: [
100+
{ binding: 0, resource: sampler },
101+
{ binding: 1, resource: textureView },
102+
],
103+
layout: this.pipeline.getBindGroupLayout(0),
104+
});
105+
106+
const colorAttachment = this.device.createTexture({
107+
format: kColorAttachmentFormat,
108+
size: { width: kRTSize, height: kRTSize, depthOrArrayLayers: 1 },
109+
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
110+
});
111+
const colorAttachmentView = colorAttachment.createView();
112+
113+
const encoder = this.device.createCommandEncoder();
114+
const pass = encoder.beginRenderPass({
115+
colorAttachments: [
116+
{
117+
view: colorAttachmentView,
118+
storeOp: 'store',
119+
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
120+
loadOp: 'clear',
121+
},
122+
],
123+
});
124+
pass.setPipeline(this.pipeline);
125+
pass.setBindGroup(0, bindGroup);
126+
pass.draw(6);
127+
pass.end();
128+
this.device.queue.submit([encoder.finish()]);
129+
130+
return colorAttachment;
131+
}
132+
}
133+
134+
export const g = makeTestGroup(SamplerFilterModeSlantedPlaneTest);
135+
136+
g.test('mipmapFilter')
137+
.desc('Test that mipmapFilter affects drawing the slanted plane with different filter modes.')
138+
.params(u =>
139+
u //
140+
.combine('mipmapFilter', ['linear', 'nearest'] as const)
141+
.combineWithParams([
142+
{
143+
results: [
144+
{ coord: { x: xMiddle, y: 2 }, expected: colors[2] },
145+
{ coord: { x: xMiddle, y: 6 }, expected: [colors[0], colors[1]] },
146+
],
147+
},
148+
])
149+
)
150+
.fn(async t => {
151+
const { mipmapFilter } = t.params;
152+
153+
const texture = t.createTexture2DWithMipmaps(colors);
154+
const textureView = texture.createView();
155+
156+
const sampler = t.device.createSampler({
157+
mipmapFilter,
158+
});
159+
160+
const colorAttachment = t.drawSlantedPlane(textureView, sampler);
161+
162+
for (const entry of t.params.results) {
163+
if (entry.expected instanceof Uint8Array) {
164+
// equal exactly one color
165+
t.expectSinglePixelIn2DTexture(colorAttachment, kColorAttachmentFormat, entry.coord, {
166+
exp: entry.expected,
167+
});
168+
} else {
169+
// a lerp between two colors
170+
t.expectSinglePixelBetweenTwoValuesIn2DTexture(
171+
colorAttachment,
172+
kColorAttachmentFormat,
173+
entry.coord,
174+
{
175+
exp: entry.expected as [Uint8Array, Uint8Array],
176+
}
177+
);
178+
}
179+
}
180+
});

0 commit comments

Comments
 (0)