083dbfb438aea4d4bc4ac4c316f4a57c4af8bfa3
[WebKit-https.git] / LayoutTests / webgpu / bind-groups.html
1 <!DOCTYPE html>
2 <meta charset=utf-8>
3 <title>Create a basic GPUBindGroup.</title>
4 <body>
5 <script src="js/webgpu-functions.js"></script>
6 <script src="../resources/testharness.js"></script>
7 <script src="../resources/testharnessreport.js"></script>
8 <script>
9 let tests = {};
10
11 const basicBufferShader = `
12 [numthreads(1, 1, 1)]
13 compute void compute_main(device int[] buffer : register(u0))
14 {
15     ++buffer[0];
16 }
17 `;
18
19 let basicPipeline;
20
21 tests["Create and use a basic GPUBindGroup."] = async device => {
22     const bufferLayoutBinding = {
23         binding: 0,
24         visibility: GPUShaderStageBit.COMPUTE,
25         type: "storage-buffer"
26     };
27
28     const bindGroupLayout = device.createBindGroupLayout({ bindings: [bufferLayoutBinding] });
29
30     const basicBuffer = device.createBuffer({ size: 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.MAP_READ });
31     const bufferBinding = { buffer: basicBuffer, size: 4 };
32     const bindGroupBinding = { binding: 0, resource: bufferBinding };
33
34     const bindGroup = device.createBindGroup({ layout: bindGroupLayout, bindings: [bindGroupBinding] });
35
36     const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });
37
38     const basicShaderModule = device.createShaderModule({ code: basicBufferShader, isWHLSL: true });
39     basicPipeline = device.createComputePipeline({
40         layout: pipelineLayout,
41         computeStage: {
42             module: basicShaderModule,
43             entryPoint: "compute_main"
44         }
45     });
46
47     const commandEncoder = device.createCommandEncoder();
48     const passEncoder = commandEncoder.beginComputePass();
49     passEncoder.setPipeline(basicPipeline);
50     passEncoder.setBindGroup(0, bindGroup);
51     passEncoder.dispatch(1, 1, 1);
52     passEncoder.endPass();
53     device.getQueue().submit([commandEncoder.finish()]);
54
55     const results = new Int32Array(await basicBuffer.mapReadAsync());
56     basicBuffer.unmap();
57     assert_equals(results[0], 1, "Storage buffer binding written to successfully.");
58 };
59
60 tests["Create and use many GPUBindGroups in a single compute pass."] = async device => {
61     const bufferLayoutBinding = {
62         binding: 0,
63         visibility: GPUShaderStageBit.COMPUTE,
64         type: "storage-buffer"
65     };
66
67     const bindGroupLayout = device.createBindGroupLayout({ bindings: [bufferLayoutBinding] });
68
69     const basicBuffer = device.createBuffer({ size: 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.MAP_READ });
70     const bufferBinding = { buffer: basicBuffer, size: 4 };
71     const bindGroupBinding = { binding: 0, resource: bufferBinding };
72
73     const numGroups = 1000;
74     let bindGroups = new Array(numGroups);
75     for (let i = 0; i < numGroups; ++i)
76         bindGroups[i] = device.createBindGroup({ layout: bindGroupLayout, bindings: [bindGroupBinding] });
77
78     const commandEncoder = device.createCommandEncoder();
79     const passEncoder = commandEncoder.beginComputePass();
80
81     let j = 0;
82     for (; j < numGroups; ++j) {
83         passEncoder.setPipeline(basicPipeline);
84         passEncoder.setBindGroup(0, bindGroups[j]);
85         passEncoder.dispatch(1, 1, 1);
86     }
87
88     passEncoder.endPass();
89     device.getQueue().submit([commandEncoder.finish()]);
90
91     const results = new Int32Array(await basicBuffer.mapReadAsync());
92     basicBuffer.unmap();
93     assert_equals(results[0], j, "Storage buffer accessed successfully through multiple bind groups.");
94 };
95
96 const uniformBufferShader = `
97 [numthreads(1, 1, 1)]
98 compute void compute_main(constant int[] uniforms : register(b0), device int[] buffer : register(u1))
99 {
100     buffer[0] += uniforms[0];
101 }
102 `;
103
104 tests["Create and access a uniform-buffer in a GPUBindGroup."] = async device => {
105     const [uniformBuffer, writeArrayBuffer] = device.createBufferMapped({ size: 4, usage: GPUBufferUsage.UNIFORM });
106     new Int32Array(writeArrayBuffer).set([42]);
107     uniformBuffer.unmap();
108
109     const storageBuffer = device.createBuffer({ size: 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.MAP_READ });
110
111     const bindGroupLayout = device.createBindGroupLayout({
112         bindings: [{
113             binding: 0,
114             visibility: GPUShaderStageBit.COMPUTE,
115             type: "uniform-buffer"
116         }, {
117             binding: 1,
118             visibility: GPUShaderStageBit.COMPUTE,
119             type: "storage-buffer"
120         }]
121     });
122
123     const bindGroup = device.createBindGroup({
124         layout: bindGroupLayout,
125         bindings: [{
126             binding: 0,
127             resource: {
128                 buffer: uniformBuffer,
129                 size: 4
130             }
131         }, {
132             binding: 1,
133             resource: {
134                 buffer: storageBuffer,
135                 size: 4
136             }
137         }]
138     });
139
140     const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });
141
142     const shaderModule = device.createShaderModule({ code: uniformBufferShader, isWHLSL: true });
143
144     const pipeline = device.createComputePipeline({
145         layout: pipelineLayout,
146         computeStage: {
147             module: shaderModule,
148             entryPoint: "compute_main"
149         }
150     });
151
152     const commandEncoder = device.createCommandEncoder();
153     const passEncoder = commandEncoder.beginComputePass();
154     passEncoder.setPipeline(pipeline);
155     passEncoder.setBindGroup(0, bindGroup);
156     passEncoder.dispatch(1, 1, 1);
157     passEncoder.endPass();
158     device.getQueue().submit([commandEncoder.finish()]);
159
160     const results = new Int32Array(await storageBuffer.mapReadAsync());
161     storageBuffer.unmap();
162     assert_equals(results[0], 42, "Storage buffer binding written to successfully.");
163 };
164
165 const sampledTextureShader = `
166 [numthreads(1, 1, 1)]
167 compute void compute_main(Texture2D<uint> inputTexture : register(t0), sampler inputSampler : register(s1), device uint[] output : register(u2))
168 {
169     output[0] = Sample(inputTexture, inputSampler, float2(0, 0));
170 }
171 `;
172
173 tests["Create and access a sampled texture in a GPUBindGroup."] = async device => {
174     const [textureDataBuffer, textureArrayBuffer] = device.createBufferMapped({ size: 4, usage: GPUBufferUsage.TRANSFER_SRC });
175     new Uint32Array(textureArrayBuffer).set([42]);
176     textureDataBuffer.unmap();
177
178     const textureSize = { width: 1, height: 1, depth: 1 };
179     const texture = device.createTexture({
180         size: textureSize,
181         format: "rgba8uint",
182         usage: GPUTextureUsage.SAMPLED | GPUTextureUsage.TRANSFER_DST
183     });
184
185     const outputBuffer = device.createBuffer({ size: 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.MAP_READ });
186
187     const bindGroupLayout = device.createBindGroupLayout({
188         bindings: [{
189             binding: 0,
190             visibility: GPUShaderStageBit.COMPUTE,
191             type: "sampled-texture"
192         }, {
193             binding: 1,
194             visibility: GPUShaderStageBit.COMPUTE,
195             type: "sampler"
196         }, {
197             binding: 2,
198             visibility: GPUShaderStageBit.COMPUTE,
199             type: "storage-buffer"
200         }]
201     });
202     const bindGroup = device.createBindGroup({
203         layout: bindGroupLayout,
204         bindings: [{
205             binding: 0,
206             resource: texture.createDefaultView()
207         }, {
208             binding: 1,
209             resource: device.createSampler({})
210         }, {
211             binding: 2,
212             resource: {
213                 buffer: outputBuffer,
214                 size: 4
215             }
216         }]
217     });
218
219     const shaderModule = device.createShaderModule({ code: sampledTextureShader, isWHLSL: true });
220     const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });
221
222     const pipeline = device.createComputePipeline({
223         layout: pipelineLayout,
224         computeStage: {
225             module: shaderModule,
226             entryPoint: "compute_main"
227         }
228     });
229
230     const commandEncoder = device.createCommandEncoder();
231     commandEncoder.copyBufferToTexture({
232         buffer: textureDataBuffer,
233         rowPitch: 4,
234         imageHeight: 0
235     }, { texture: texture }, textureSize);
236
237     const passEncoder = commandEncoder.beginComputePass();
238     passEncoder.setPipeline(pipeline);
239     passEncoder.setBindGroup(0, bindGroup);
240     passEncoder.dispatch(1, 1, 1);
241     passEncoder.endPass();
242
243     device.getQueue().submit([commandEncoder.finish()]);
244
245     const results = new Uint32Array(await outputBuffer.mapReadAsync());
246     outputBuffer.unmap();
247     assert_equals(results[0], 42, "Correct value sampled from a bound 2D texture.");
248 };
249
250 const comboShader = `
251 [numthreads(1, 1, 1)]
252 compute void compute_main(
253     Texture2D<uint> inputTexture : register(t0, space0), 
254     sampler inputSampler : register(s0, space1), 
255     constant uint[] input : register(b0, space2), 
256     device uint[] output : register(u0, space3))
257 {
258     output[0] = input[0] + Sample(inputTexture, inputSampler, float2(0, 0));
259 }
260 `;
261
262 tests["Create and use multiple GPUBindGroups in a single dispatch."] = async device => {
263     const [textureDataBuffer, textureArrayBuffer] = device.createBufferMapped({ size: 4, usage: GPUBufferUsage.TRANSFER_SRC });
264     new Uint32Array(textureArrayBuffer).set([17]);
265     textureDataBuffer.unmap();
266
267     const textureSize = { width: 1, height: 1, depth: 1 };
268     const texture = device.createTexture({
269         size: textureSize,
270         format: "rgba8uint",
271         usage: GPUTextureUsage.SAMPLED | GPUTextureUsage.TRANSFER_DST
272     });
273
274     const [inputBuffer, inputArrayBuffer] = device.createBufferMapped({ size: 4, usage: GPUBufferUsage.UNIFORM });
275     new Uint32Array(inputArrayBuffer).set([25]);
276     inputBuffer.unmap();
277
278     const outputBuffer = device.createBuffer({ size: 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.MAP_READ });
279
280     const bgl0 = device.createBindGroupLayout({
281         bindings: [{
282             binding: 0,
283             visibility: GPUShaderStageBit.COMPUTE,
284             type: "sampled-texture"
285         }]
286     });
287     const bgl1 = device.createBindGroupLayout({
288         bindings: [{
289             binding: 0,
290             visibility: GPUShaderStageBit.COMPUTE,
291             type: "sampler"
292         }]
293     });
294     const bgl2 = device.createBindGroupLayout({
295         bindings: [{
296             binding: 0,
297             visibility: GPUShaderStageBit.COMPUTE,
298             type: "uniform-buffer"
299         }]
300     });
301     const bgl3 = device.createBindGroupLayout({
302         bindings: [{
303             binding: 0,
304             visibility: GPUShaderStageBit.COMPUTE,
305             type: "storage-buffer"
306         }]
307     })
308
309     const bg0 = device.createBindGroup({
310         layout: bgl0,
311         bindings: [{
312             binding: 0,
313             resource: texture.createDefaultView()
314         }]
315     });
316     const bg1 = device.createBindGroup({
317         layout: bgl1,
318         bindings: [{
319             binding: 0,
320             resource: device.createSampler({})
321         }]
322     });
323     const bg2 = device.createBindGroup({
324         layout: bgl2,
325         bindings: [{
326             binding: 0,
327             resource: {
328                 buffer: inputBuffer,
329                 size: 4
330             }
331         }]
332     });
333     const bg3 = device.createBindGroup({
334         layout: bgl3,
335         bindings: [{
336             binding: 0,
337             resource: {
338                 buffer: outputBuffer,
339                 size: 4
340             }
341         }]
342     });
343
344     const shaderModule = device.createShaderModule({ code: comboShader, isWHLSL: true });
345     const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bgl0, bgl1, bgl2, bgl3] });
346
347     const pipeline = device.createComputePipeline({
348         layout: pipelineLayout,
349         computeStage: {
350             module: shaderModule,
351             entryPoint: "compute_main"
352         }
353     });
354
355     const commandEncoder = device.createCommandEncoder();
356     commandEncoder.copyBufferToTexture({
357         buffer: textureDataBuffer,
358         rowPitch: 4,
359         imageHeight: 0
360     }, { texture: texture }, textureSize);
361
362     const passEncoder = commandEncoder.beginComputePass();
363     passEncoder.setPipeline(pipeline);
364     passEncoder.setBindGroup(0, bg0);
365     passEncoder.setBindGroup(1, bg1);
366     passEncoder.setBindGroup(2, bg2);
367     passEncoder.setBindGroup(3, bg3);
368     passEncoder.dispatch(1, 1, 1);
369     passEncoder.endPass();
370
371     device.getQueue().submit([commandEncoder.finish()]);
372
373     const results = new Uint32Array(await outputBuffer.mapReadAsync());
374     outputBuffer.unmap();
375     assert_equals(results[0], 42, "Correct value sampled from a bound 2D texture.");
376 };
377
378 tests["Bind a single GPUBuffer with different offsets in different GPUBindGroups"] = async device => {
379     const numInputs = 4;
380     const [uniformBuffer, writeArrayBuffer] = device.createBufferMapped({ size: 4 * numInputs, usage: GPUBufferUsage.UNIFORM });
381     new Int32Array(writeArrayBuffer).set([1, 2, 3, 36]);
382     uniformBuffer.unmap();
383
384     const storageBuffer = device.createBuffer({ size: 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.MAP_READ });
385
386     const bindGroupLayout = device.createBindGroupLayout({
387         bindings: [{
388             binding: 0,
389             visibility: GPUShaderStageBit.COMPUTE,
390             type: "uniform-buffer"
391         }, {
392             binding: 1,
393             visibility: GPUShaderStageBit.COMPUTE,
394             type: "storage-buffer"
395         }]
396     });
397
398     let bindGroups = new Array(numInputs);
399     for (let i = 0; i < numInputs; ++i) {
400         bindGroups[i] = device.createBindGroup({
401             layout: bindGroupLayout,
402             bindings: [{
403                 binding: 0,
404                 resource: {
405                     buffer: uniformBuffer,
406                     offset: i * numInputs,
407                     size: 4
408                 }
409             }, {
410                 binding: 1,
411                 resource: {
412                     buffer: storageBuffer,
413                     size: 4
414                 }
415             }]
416         });
417     }
418
419     const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });
420
421     const shaderModule = device.createShaderModule({ code: uniformBufferShader, isWHLSL: true });
422
423     const pipeline = device.createComputePipeline({
424         layout: pipelineLayout,
425         computeStage: {
426             module: shaderModule,
427             entryPoint: "compute_main"
428         }
429     });
430
431     const commandEncoder = device.createCommandEncoder();
432     const passEncoder = commandEncoder.beginComputePass();
433     passEncoder.setPipeline(pipeline);
434     for (let i = 0; i < numInputs; ++i) {
435         passEncoder.setBindGroup(0, bindGroups[i]);
436         passEncoder.dispatch(1, 1, 1);
437     }
438     passEncoder.endPass();
439     device.getQueue().submit([commandEncoder.finish()]);
440
441     const results = new Int32Array(await storageBuffer.mapReadAsync());
442     storageBuffer.unmap();
443     assert_equals(results[0], 42, "Storage buffer binding written to successfully.");
444 };
445
446 runTestsWithDevice(tests);
447 </script>
448 </body>