Skip to content

Commit f5977ec

Browse files
authored
Add Render Bundle operation tests (#4493)
Issue #4492
1 parent bd0414c commit f5977ec

1 file changed

Lines changed: 208 additions & 0 deletions

File tree

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
export const description = `
2+
Ensure that render bundles execute.
3+
- Test they run
4+
- Test they can be used multiple times
5+
- in different passes
6+
- in the same pass
7+
- in the same executeBundles call
8+
`;
9+
10+
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
11+
import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../../../gpu_test.js';
12+
import * as ttu from '../../../../texture_test_utils.js';
13+
14+
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
15+
16+
// Makes a render pipeline where we can select kColor0 or kColor1 by instance index
17+
// We can select top right triangle or bottom left triangle by vertex index
18+
function makeRenderPipeline(device: GPUDevice, blend: GPUBlendState | undefined = undefined) {
19+
const module = device.createShaderModule({
20+
code: `
21+
struct Interop {
22+
@builtin(position) pos: vec4f,
23+
@location(0) @interpolate(flat, either) inst: u32,
24+
}
25+
@vertex fn vs(@builtin(vertex_index) vNdx: u32,
26+
@builtin(instance_index) inst: u32) -> Interop {
27+
let pos = array(
28+
vec2f(-1, -1), vec2f(1, -1), vec2f(-1, 1),
29+
vec2f(-1, 1), vec2f(1, -1), vec2f( 1, 1),
30+
);
31+
return Interop(vec4f(pos[vNdx], 0, 1), inst);
32+
}
33+
34+
@fragment fn fs(v: Interop) -> @location(0) vec4f {
35+
// round these colors a little since different GPUs might go up or down a bit to rgba8unorm
36+
let colors = array(
37+
vec4f(1.1 / 255, 2.1 / 255, 3.1 / 255, 4.1 / 255),
38+
vec4f(5.1 / 255, 6.1 / 255, 7.1 / 255, 8.1 / 255),
39+
);
40+
return colors[v.inst];
41+
}
42+
`,
43+
});
44+
45+
const pipeline = device.createRenderPipeline({
46+
layout: 'auto',
47+
vertex: { module },
48+
fragment: { module, targets: [{ format: 'rgba8unorm', blend }] },
49+
});
50+
51+
return pipeline;
52+
}
53+
54+
function makeRenderPass(encoder: GPUCommandEncoder, view: GPUTextureView) {
55+
const pass = encoder.beginRenderPass({
56+
colorAttachments: [
57+
{
58+
view,
59+
loadOp: 'clear',
60+
storeOp: 'store',
61+
},
62+
],
63+
});
64+
return pass;
65+
}
66+
67+
function makeTexture(t: GPUTest) {
68+
return t.createTextureTracked({
69+
size: [4, 4],
70+
format: 'rgba8unorm',
71+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
72+
});
73+
}
74+
75+
const kColor0 = { R: 1 / 255, G: 2 / 255, B: 3 / 255, A: 4 / 255 };
76+
const kColor1 = { R: 5 / 255, G: 6 / 255, B: 7 / 255, A: 8 / 255 };
77+
const kColor0x3 = { R: kColor0.R * 3, G: kColor0.G * 3, B: kColor0.B * 3, A: kColor0.A * 3 };
78+
const kZero = { R: 0, G: 0, B: 0, A: 0 };
79+
80+
g.test('basic')
81+
.desc(`Test a basic render bundle`)
82+
.fn(t => {
83+
const pipeline = makeRenderPipeline(t.device);
84+
85+
const bundleEncoder = t.device.createRenderBundleEncoder({
86+
colorFormats: ['rgba8unorm'],
87+
});
88+
bundleEncoder.setPipeline(pipeline);
89+
bundleEncoder.draw(6);
90+
const bundle = bundleEncoder.finish();
91+
92+
const texture = makeTexture(t);
93+
94+
const encoder = t.device.createCommandEncoder();
95+
const pass = makeRenderPass(encoder, texture.createView());
96+
pass.executeBundles([bundle]);
97+
pass.end();
98+
t.device.queue.submit([encoder.finish()]);
99+
100+
ttu.expectSingleColorWithTolerance(t, texture, 'rgba8unorm', {
101+
size: [4, 4, 1],
102+
exp: kColor0,
103+
});
104+
});
105+
106+
g.test('two_bundles')
107+
.desc(`Test drawing 2 render bundles`)
108+
.fn(t => {
109+
const pipeline = makeRenderPipeline(t.device);
110+
111+
const bundleEncoder1 = t.device.createRenderBundleEncoder({
112+
colorFormats: ['rgba8unorm'],
113+
});
114+
bundleEncoder1.setPipeline(pipeline);
115+
bundleEncoder1.draw(3);
116+
const bundle1 = bundleEncoder1.finish();
117+
118+
const bundleEncoder2 = t.device.createRenderBundleEncoder({
119+
colorFormats: ['rgba8unorm'],
120+
});
121+
bundleEncoder2.setPipeline(pipeline);
122+
bundleEncoder2.draw(3, 1, 3, 1);
123+
const bundle2 = bundleEncoder2.finish();
124+
125+
const texture = makeTexture(t);
126+
127+
const encoder = t.device.createCommandEncoder();
128+
const pass = makeRenderPass(encoder, texture.createView());
129+
pass.executeBundles([bundle1, bundle2]);
130+
pass.end();
131+
t.device.queue.submit([encoder.finish()]);
132+
133+
ttu.expectSinglePixelComparisonsAreOkInTexture(t, { texture }, [
134+
{ coord: { x: 0, y: 3 }, exp: kColor0 },
135+
{ coord: { x: 3, y: 0 }, exp: kColor1 },
136+
]);
137+
});
138+
139+
g.test('one_bundle_used_multiple_times')
140+
.desc(`Test drawing 1 render bundle multiple times using the viewport to select where`)
141+
.fn(t => {
142+
const pipeline = makeRenderPipeline(t.device);
143+
144+
const bundleEncoder = t.device.createRenderBundleEncoder({
145+
colorFormats: ['rgba8unorm'],
146+
});
147+
bundleEncoder.setPipeline(pipeline);
148+
bundleEncoder.draw(6);
149+
const bundle = bundleEncoder.finish();
150+
151+
const texture = makeTexture(t);
152+
153+
const encoder = t.device.createCommandEncoder();
154+
const pass = makeRenderPass(encoder, texture.createView());
155+
pass.setViewport(0, 0, 1, 1, 0, 1);
156+
pass.executeBundles([bundle]);
157+
pass.setViewport(2, 0, 1, 1, 0, 1);
158+
pass.executeBundles([bundle]);
159+
pass.setViewport(0, 2, 1, 1, 0, 1);
160+
pass.executeBundles([bundle]);
161+
pass.setViewport(2, 2, 1, 1, 0, 1);
162+
pass.executeBundles([bundle]);
163+
pass.end();
164+
t.device.queue.submit([encoder.finish()]);
165+
166+
ttu.expectSinglePixelComparisonsAreOkInTexture(t, { texture }, [
167+
{ coord: { x: 0, y: 0 }, exp: kColor0 },
168+
{ coord: { x: 2, y: 0 }, exp: kColor0 },
169+
{ coord: { x: 0, y: 2 }, exp: kColor0 },
170+
{ coord: { x: 2, y: 2 }, exp: kColor0 },
171+
// Check a few places we should not have rendered.
172+
{ coord: { x: 1, y: 0 }, exp: kZero },
173+
{ coord: { x: 3, y: 0 }, exp: kZero },
174+
{ coord: { x: 0, y: 1 }, exp: kZero },
175+
{ coord: { x: 0, y: 3 }, exp: kZero },
176+
{ coord: { x: 3, y: 3 }, exp: kZero },
177+
]);
178+
});
179+
180+
g.test('one_bundle_used_multiple_times_same_executeBundles')
181+
.desc(`Test drawing 1 render bundle multiple times using the same call to executeBundles`)
182+
.fn(t => {
183+
const pipeline = makeRenderPipeline(t.device, {
184+
color: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
185+
alpha: { srcFactor: 'one', dstFactor: 'one', operation: 'add' },
186+
});
187+
188+
const bundleEncoder = t.device.createRenderBundleEncoder({
189+
colorFormats: ['rgba8unorm'],
190+
});
191+
bundleEncoder.setPipeline(pipeline);
192+
bundleEncoder.draw(6);
193+
const bundle = bundleEncoder.finish();
194+
195+
const texture = makeTexture(t);
196+
197+
const encoder = t.device.createCommandEncoder();
198+
const pass = makeRenderPass(encoder, texture.createView());
199+
pass.executeBundles([bundle, bundle, bundle]);
200+
pass.end();
201+
t.device.queue.submit([encoder.finish()]);
202+
203+
// Check the result is kColor0 added 3 times.
204+
ttu.expectSingleColorWithTolerance(t, texture, 'rgba8unorm', {
205+
size: [4, 4, 1],
206+
exp: kColor0x3,
207+
});
208+
});

0 commit comments

Comments
 (0)