Skip to content

Commit b526ce4

Browse files
authored
Merge pull request #20390 from takahirox/ComputeShader
WebGPU compute shader support prototype.
2 parents ff404ba + 0ed5756 commit b526ce4

File tree

8 files changed

+402
-6
lines changed

8 files changed

+402
-6
lines changed

examples/files.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,8 @@ var files = {
323323
],
324324
"webgpu": [
325325
"webgpu_sandbox",
326-
"webgpu_rtt"
326+
"webgpu_rtt",
327+
"webgpu_compute",
327328
],
328329
"webaudio": [
329330
"webaudio_orientation",

examples/jsm/renderers/webgpu/WebGPUAttributes.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,25 @@ class WebGPUAttributes {
2727

2828
}
2929

30-
update( attribute, isIndex = false ) {
30+
update( attribute, isIndex = false, usage = null ) {
3131

3232
let data = this.buffers.get( attribute );
3333

3434
if ( data === undefined ) {
3535

36-
const usage = ( isIndex === true ) ? GPUBufferUsage.INDEX : GPUBufferUsage.VERTEX;
36+
if ( usage === null ) {
37+
38+
usage = ( isIndex === true ) ? GPUBufferUsage.INDEX : GPUBufferUsage.VERTEX;
39+
40+
}
41+
42+
data = this._createBuffer( attribute, usage );
43+
44+
this.buffers.set( attribute, data );
45+
46+
} else if ( usage && usage !== data.usage ) {
47+
48+
data.buffer.destroy();
3749

3850
data = this._createBuffer( attribute, usage );
3951

@@ -68,7 +80,8 @@ class WebGPUAttributes {
6880

6981
return {
7082
version: attribute.version,
71-
buffer: buffer
83+
buffer: buffer,
84+
usage: usage
7285
};
7386

7487
}

examples/jsm/renderers/webgpu/WebGPUBindings.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1+
import WebGPUStorageBuffer from './WebGPUStorageBuffer.js';
12
import WebGPUUniformsGroup from './WebGPUUniformsGroup.js';
23
import { FloatUniform, Matrix3Uniform, Matrix4Uniform } from './WebGPUUniform.js';
34
import WebGPUSampler from './WebGPUSampler.js';
45
import { WebGPUSampledTexture } from './WebGPUSampledTexture.js';
56

67
class WebGPUBindings {
78

8-
constructor( device, info, properties, textures, pipelines ) {
9+
constructor( device, info, properties, textures, pipelines, computePipelines, attributes ) {
910

1011
this.device = device;
1112
this.info = info;
1213
this.properties = properties;
1314
this.textures = textures;
1415
this.pipelines = pipelines;
16+
this.computePipelines = computePipelines;
17+
this.attributes = attributes;
1518

1619
this.uniformsData = new WeakMap();
1720

@@ -72,6 +75,31 @@ class WebGPUBindings {
7275

7376
}
7477

78+
getForCompute( param ) {
79+
80+
let data = this.uniformsData.get( param );
81+
82+
if ( data === undefined ) {
83+
84+
const pipeline = this.computePipelines.get( param );
85+
const bindings = param.bindings !== undefined ? param.bindings.slice() : [];
86+
const bindLayout = pipeline.getBindGroupLayout( 0 );
87+
const bindGroup = this._createBindGroup( bindings, bindLayout );
88+
89+
data = {
90+
layout: bindLayout,
91+
group: bindGroup,
92+
bindings: bindings
93+
};
94+
95+
this.uniformsData.set( param, data );
96+
97+
}
98+
99+
return data;
100+
101+
}
102+
75103
update( object, camera ) {
76104

77105
const textures = this.textures;
@@ -116,6 +144,11 @@ class WebGPUBindings {
116144

117145
updateMap.set( binding, frame );
118146

147+
} else if ( binding.isStorageBuffer ) {
148+
149+
const attribute = binding.attribute;
150+
this.attributes.update( attribute, false, binding.usage );
151+
119152
} else if ( binding.isSampler ) {
120153

121154
const material = object.material;
@@ -198,6 +231,19 @@ class WebGPUBindings {
198231

199232
entries.push( { binding: bindingPoint, resource: { buffer: binding.bufferGPU } } );
200233

234+
} else if ( binding.isStorageBuffer ) {
235+
236+
if ( binding.bufferGPU === null ) {
237+
238+
const attribute = binding.attribute;
239+
240+
this.attributes.update( attribute, false, binding.usage );
241+
binding.bufferGPU = this.attributes.get( attribute ).buffer;
242+
243+
}
244+
245+
entries.push( { binding: bindingPoint, resource: { buffer: binding.bufferGPU } } );
246+
201247
} else if ( binding.isSampler ) {
202248

203249
if ( binding.samplerGPU === null ) {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
class WebGPUComputePipelines {
2+
3+
constructor( device, glslang ) {
4+
5+
this.device = device;
6+
this.glslang = glslang;
7+
8+
this.pipelines = new WeakMap();
9+
this.shaderModules = {
10+
compute: new WeakMap()
11+
};
12+
13+
}
14+
15+
get( param ) {
16+
17+
let pipeline = this.pipelines.get( param );
18+
19+
if ( pipeline === undefined ) {
20+
21+
const device = this.device;
22+
const shader = {
23+
computeShader: param.shader
24+
};
25+
26+
// shader modules
27+
28+
const glslang = this.glslang;
29+
30+
let moduleCompute = this.shaderModules.compute.get( shader );
31+
32+
if ( moduleCompute === undefined ) {
33+
34+
const byteCodeCompute = glslang.compileGLSL( shader.computeShader, 'compute' );
35+
36+
moduleCompute = device.createShaderModule( { code: byteCodeCompute } );
37+
38+
this.shaderModules.compute.set( shader, moduleCompute );
39+
40+
}
41+
42+
//
43+
44+
const computeStage = {
45+
module: moduleCompute,
46+
entryPoint: 'main'
47+
};
48+
49+
pipeline = device.createComputePipeline( {
50+
computeStage: computeStage
51+
} );
52+
53+
this.pipelines.set( param, pipeline );
54+
55+
}
56+
57+
return pipeline;
58+
59+
}
60+
61+
dispose() {
62+
63+
this.pipelines = new WeakMap();
64+
this.shaderModules = {
65+
compute: new WeakMap()
66+
};
67+
68+
}
69+
70+
}
71+
72+
export default WebGPUComputePipelines;

examples/jsm/renderers/webgpu/WebGPURenderer.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import WebGPUGeometries from './WebGPUGeometries.js';
55
import WebGPUInfo from './WebGPUInfo.js';
66
import WebGPUProperties from './WebGPUProperties.js';
77
import WebGPURenderPipelines from './WebGPURenderPipelines.js';
8+
import WebGPUComputePipelines from './WebGPUComputePipelines.js';
89
import WebGPUBindings from './WebGPUBindings.js';
910
import WebGPURenderLists from './WebGPURenderLists.js';
1011
import WebGPUTextures from './WebGPUTextures.js';
@@ -99,6 +100,7 @@ class WebGPURenderer {
99100
this._bindings = null;
100101
this._objects = null;
101102
this._renderPipelines = null;
103+
this._computePipelines = null;
102104
this._renderLists = null;
103105
this._textures = null;
104106
this._background = null;
@@ -174,7 +176,8 @@ class WebGPURenderer {
174176
this._textures = new WebGPUTextures( device, this._properties, this._info, compiler );
175177
this._objects = new WebGPUObjects( this._geometries, this._info );
176178
this._renderPipelines = new WebGPURenderPipelines( this, this._properties, device, compiler, parameters.sampleCount );
177-
this._bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines );
179+
this._computePipelines = new WebGPUComputePipelines( device, compiler );
180+
this._bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes );
178181
this._renderLists = new WebGPURenderLists();
179182
this._background = new WebGPUBackground( this );
180183

@@ -510,6 +513,7 @@ class WebGPURenderer {
510513
this._objects.dispose();
511514
this._properties.dispose();
512515
this._renderPipelines.dispose();
516+
this._computePipelines.dispose();
513517
this._bindings.dispose();
514518
this._info.dispose();
515519
this._renderLists.dispose();
@@ -529,6 +533,34 @@ class WebGPURenderer {
529533

530534
}
531535

536+
compute( computeParams ) {
537+
538+
const device = this._device;
539+
const cmdEncoder = device.createCommandEncoder( {} );
540+
const passEncoder = cmdEncoder.beginComputePass();
541+
542+
for ( const param of computeParams ) {
543+
544+
// pipeline
545+
546+
const pipeline = this._computePipelines.get( param );
547+
passEncoder.setPipeline( pipeline );
548+
549+
// bind group
550+
551+
const bindGroup = this._bindings.getForCompute( param ).group;
552+
this._bindings.update( param );
553+
passEncoder.setBindGroup( 0, bindGroup );
554+
555+
passEncoder.dispatch( param.num );
556+
557+
}
558+
559+
passEncoder.endPass();
560+
device.defaultQueue.submit( [ cmdEncoder.finish() ] );
561+
562+
}
563+
532564
getRenderTarget() {
533565

534566
return this._renderTarget;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import WebGPUBinding from './WebGPUBinding.js';
2+
3+
class WebGPUStorageBuffer extends WebGPUBinding {
4+
5+
constructor ( name, attribute ) {
6+
7+
super( name );
8+
9+
this.type = 'storage-buffer';
10+
11+
this.usage = GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST;
12+
13+
this.attribute = attribute;
14+
this.bufferGPU = null; // set by the renderer
15+
16+
Object.defineProperty( this, 'isStorageBuffer', { value: true } );
17+
18+
}
19+
20+
}
21+
22+
export default WebGPUStorageBuffer;
5.62 KB
Loading

0 commit comments

Comments
 (0)