1
- import { mat4 , vec3 } from ' gl-matrix'
2
- import shaders from ' ./shaders.wgsl'
1
+ import { mat4 , vec3 } from " gl-matrix" ;
2
+ import shaders from " ./shaders.wgsl" ;
3
3
4
4
// Simulation parameters.
5
5
let numBodies ;
@@ -26,25 +26,26 @@ let positionsOut: GPUBuffer = null;
26
26
let velocities : GPUBuffer = null ;
27
27
let renderParams : GPUBuffer = null ;
28
28
let computeBindGroup : GPUBindGroup = null ;
29
+ let othergroup : GPUBindGroup = null ;
29
30
let renderBindGroup : GPUBindGroup = null ;
30
31
31
32
const init = async ( ) => {
32
33
// Initialize the WebGPU device.
33
- const powerPref = < HTMLSelectElement > document . getElementById ( ' powerpref' ) ;
34
+ const powerPref = < HTMLSelectElement > document . getElementById ( " powerpref" ) ;
34
35
const adapter = await navigator . gpu . requestAdapter ( {
35
36
powerPreference : < GPUPowerPreference > powerPref . selectedOptions [ 0 ] . value ,
36
37
} ) ;
37
- device = await adapter . requestDevice ( )
38
+ device = await adapter . requestDevice ( ) ;
38
39
queue = device . queue ;
39
40
40
41
// Set up the canvas context.
41
- canvas = < HTMLCanvasElement > document . getElementById ( ' canvas' ) ;
42
- canvasContext = canvas . getContext ( ' webgpu' ) ;
43
- }
42
+ canvas = < HTMLCanvasElement > document . getElementById ( " canvas" ) ;
43
+ canvasContext = canvas . getContext ( " webgpu" ) ;
44
+ } ;
44
45
45
46
// Generate WGSL shader source.
46
47
function getShaders ( ) {
47
- let preamble = ''
48
+ let preamble = "" ;
48
49
preamble += `const kWorkgroupSize = ${ workgroupSize } ;\n` ;
49
50
preamble += `const kNumBodies = ${ numBodies } ;\n` ;
50
51
return preamble + shaders ;
@@ -69,8 +70,7 @@ const updateRenderParams = async () => {
69
70
// Generate the view projection matrix.
70
71
let projectionMatrix = mat4 . create ( ) ;
71
72
let viewProjectionMatrix = mat4 . create ( ) ;
72
- mat4 . perspectiveZO ( projectionMatrix ,
73
- 1.0 , canvas . width / canvas . height , 0.1 , 50.0 ) ;
73
+ mat4 . perspectiveZO ( projectionMatrix , 1.0 , canvas . width / canvas . height , 0.1 , 50.0 ) ;
74
74
mat4 . translate ( viewProjectionMatrix , viewProjectionMatrix , eyePosition ) ;
75
75
mat4 . multiply ( viewProjectionMatrix , projectionMatrix , viewProjectionMatrix ) ;
76
76
@@ -79,7 +79,7 @@ const updateRenderParams = async () => {
79
79
let viewProjectionMatrixHost = new Float32Array ( renderParamsHost ) ;
80
80
viewProjectionMatrixHost . set ( viewProjectionMatrix ) ;
81
81
queue . writeBuffer ( renderParams , 0 , renderParamsHost ) ;
82
- }
82
+ } ;
83
83
84
84
function initPipelines ( ) {
85
85
// Reset pipelines.
@@ -104,42 +104,44 @@ function initPipelines() {
104
104
const positionsAttribute : GPUVertexAttribute = {
105
105
shaderLocation : 0 ,
106
106
offset : 0 ,
107
- format : ' float32x4' ,
107
+ format : " float32x4" ,
108
108
} ;
109
109
const positionsLayout : GPUVertexBufferLayout = {
110
110
attributes : [ positionsAttribute ] ,
111
111
arrayStride : 4 * 4 ,
112
- stepMode : ' instance' ,
112
+ stepMode : " instance" ,
113
113
} ;
114
114
renderPipeline = device . createRenderPipeline ( {
115
115
vertex : {
116
116
module : module ,
117
- entryPoint : ' vs_main' ,
117
+ entryPoint : " vs_main" ,
118
118
buffers : [ positionsLayout ] ,
119
119
} ,
120
120
fragment : {
121
121
module : module ,
122
- entryPoint : 'fs_main' ,
123
- targets : [ {
124
- format : navigator . gpu . getPreferredCanvasFormat ( ) ,
125
- blend : {
126
- color : {
127
- operation : "add" ,
128
- srcFactor : "one" ,
129
- dstFactor : "one" ,
130
- } ,
131
- alpha : {
132
- operation : "add" ,
133
- srcFactor : "one" ,
134
- dstFactor : "one" ,
122
+ entryPoint : "fs_main" ,
123
+ targets : [
124
+ {
125
+ format : navigator . gpu . getPreferredCanvasFormat ( ) ,
126
+ blend : {
127
+ color : {
128
+ operation : "add" ,
129
+ srcFactor : "one" ,
130
+ dstFactor : "one" ,
131
+ } ,
132
+ alpha : {
133
+ operation : "add" ,
134
+ srcFactor : "one" ,
135
+ dstFactor : "one" ,
136
+ } ,
135
137
} ,
136
- }
137
- } ] ,
138
+ } ,
139
+ ] ,
138
140
} ,
139
141
primitive : {
140
- frontFace : 'cw' ,
141
- cullMode : ' none' ,
142
- topology : ' triangle-list' ,
142
+ frontFace : "cw" ,
143
+ cullMode : " none" ,
144
+ topology : " triangle-list" ,
143
145
} ,
144
146
layout : "auto" ,
145
147
} ) ;
@@ -148,9 +150,9 @@ function initPipelines() {
148
150
computePipeline = device . createComputePipeline ( {
149
151
compute : {
150
152
module : module ,
151
- entryPoint : ' cs_main' ,
153
+ entryPoint : " cs_main" ,
152
154
} ,
153
- layout : "auto"
155
+ layout : "auto" ,
154
156
} ) ;
155
157
}
156
158
@@ -159,25 +161,25 @@ function initBodies() {
159
161
positionsIn = device . createBuffer ( {
160
162
size : numBodies * 4 * 4 ,
161
163
usage : GPUBufferUsage . STORAGE | GPUBufferUsage . VERTEX ,
162
- mappedAtCreation : true
164
+ mappedAtCreation : true ,
163
165
} ) ;
164
166
positionsOut = device . createBuffer ( {
165
167
size : numBodies * 4 * 4 ,
166
168
usage : GPUBufferUsage . STORAGE | GPUBufferUsage . VERTEX ,
167
- mappedAtCreation : false
169
+ mappedAtCreation : false ,
168
170
} ) ;
169
171
velocities = device . createBuffer ( {
170
172
size : numBodies * 4 * 4 ,
171
173
usage : GPUBufferUsage . STORAGE ,
172
- mappedAtCreation : false
174
+ mappedAtCreation : false ,
173
175
} ) ;
174
176
175
177
// Generate initial positions on the surface of a sphere.
176
178
const kRadius = 0.6 ;
177
179
let positions = new Float32Array ( positionsIn . getMappedRange ( ) ) ;
178
180
for ( let i = 0 ; i < numBodies ; i ++ ) {
179
181
let longitude = 2.0 * Math . PI * Math . random ( ) ;
180
- let latitude = Math . acos ( ( 2.0 * Math . random ( ) - 1.0 ) ) ;
182
+ let latitude = Math . acos ( 2.0 * Math . random ( ) - 1.0 ) ;
181
183
positions [ i * 4 + 0 ] = kRadius * Math . sin ( latitude ) * Math . cos ( longitude ) ;
182
184
positions [ i * 4 + 1 ] = kRadius * Math . sin ( latitude ) * Math . sin ( longitude ) ;
183
185
positions [ i * 4 + 2 ] = kRadius * Math . cos ( latitude ) ;
@@ -203,7 +205,7 @@ function draw() {
203
205
const timeSinceLastLog = now - lastFpsUpdateTime ;
204
206
if ( timeSinceLastLog >= kFpsUpdateInterval ) {
205
207
const fps = numFramesSinceFpsUpdate / ( timeSinceLastLog / 1000.0 ) ;
206
- document . getElementById ( "fps" ) . innerHTML = fps . toFixed ( 1 ) + ' FPS' ;
208
+ document . getElementById ( "fps" ) . innerHTML = fps . toFixed ( 1 ) + " FPS" ;
207
209
lastFpsUpdateTime = performance . now ( ) ;
208
210
numFramesSinceFpsUpdate = 0 ;
209
211
}
@@ -215,9 +217,9 @@ function draw() {
215
217
// Update render parameters based on key presses.
216
218
if ( currentKey ) {
217
219
let zInc = 0.025 ;
218
- if ( currentKey . key == ' ArrowUp' ) {
220
+ if ( currentKey . key == " ArrowUp" ) {
219
221
eyePosition [ 2 ] += zInc ;
220
- } else if ( currentKey . key == ' ArrowDown' ) {
222
+ } else if ( currentKey . key == " ArrowDown" ) {
221
223
eyePosition [ 2 ] -= zInc ;
222
224
}
223
225
updateRenderParams ( ) ;
@@ -250,6 +252,31 @@ function draw() {
250
252
] ,
251
253
} ) ;
252
254
255
+ let image_a = device . createTexture ( {
256
+ format : "r32uint" ,
257
+ size : [ 16 , 16 ] ,
258
+ usage : GPUTextureUsage . STORAGE_BINDING ,
259
+ } ) ;
260
+ let image_b = device . createTexture ( {
261
+ format : "r32uint" ,
262
+ size : [ 16 , 16 ] ,
263
+ usage : GPUTextureUsage . STORAGE_BINDING ,
264
+ } ) ;
265
+
266
+ othergroup = device . createBindGroup ( {
267
+ layout : computePipeline . getBindGroupLayout ( 1 ) ,
268
+ entries : [
269
+ {
270
+ binding : 0 ,
271
+ resource : image_a . createView ( ) ,
272
+ } ,
273
+ {
274
+ binding : 1 ,
275
+ resource : image_b . createView ( ) ,
276
+ } ,
277
+ ] ,
278
+ } ) ;
279
+
253
280
// Create the bind group for the compute shader.
254
281
renderBindGroup = device . createBindGroup ( {
255
282
layout : renderPipeline . getBindGroupLayout ( 0 ) ,
@@ -268,6 +295,7 @@ function draw() {
268
295
const computePassEncoder = commandEncoder . beginComputePass ( ) ;
269
296
computePassEncoder . setPipeline ( computePipeline ) ;
270
297
computePassEncoder . setBindGroup ( 0 , computeBindGroup ) ;
298
+ computePassEncoder . setBindGroup ( 1 , othergroup ) ;
271
299
computePassEncoder . dispatchWorkgroups ( numBodies / workgroupSize ) ;
272
300
computePassEncoder . end ( ) ;
273
301
@@ -282,7 +310,7 @@ function draw() {
282
310
view : colorTextureView ,
283
311
loadOp : "clear" ,
284
312
clearValue : { r : 0 , g : 0 , b : 0.1 , a : 1 } ,
285
- storeOp : ' store'
313
+ storeOp : " store" ,
286
314
} ;
287
315
const renderPassEncoder = commandEncoder . beginRenderPass ( {
288
316
colorAttachments : [ colorAttachment ] ,
@@ -317,43 +345,43 @@ const reset = async () => {
317
345
initPipelines ( ) ;
318
346
319
347
paused = false ;
320
- }
348
+ } ;
321
349
322
350
function pause ( ) {
323
351
paused = ! paused ;
324
- document . getElementById ( "pause" ) . innerText = paused ? ' Unpause' : ' Pause' ;
352
+ document . getElementById ( "pause" ) . innerText = paused ? " Unpause" : " Pause" ;
325
353
}
326
354
327
355
reset ( ) ;
328
356
draw ( ) ;
329
357
330
358
// Set up button onclick handlers.
331
- document . querySelector ( ' #reset' ) . addEventListener ( ' click' , reset ) ;
332
- document . querySelector ( ' #pause' ) . addEventListener ( ' click' , pause ) ;
359
+ document . querySelector ( " #reset" ) . addEventListener ( " click" , reset ) ;
360
+ document . querySelector ( " #pause" ) . addEventListener ( " click" , pause ) ;
333
361
334
362
// Automatically reset when the number of bodies is changed.
335
- document . querySelector ( ' #numbodies' ) . addEventListener ( ' change' , reset ) ;
363
+ document . querySelector ( " #numbodies" ) . addEventListener ( " change" , reset ) ;
336
364
337
365
// Automatically reset when the power preference is changed.
338
- document . querySelector ( ' #powerpref' ) . addEventListener ( ' change' , ( ) => {
366
+ document . querySelector ( " #powerpref" ) . addEventListener ( " change" , ( ) => {
339
367
device = null ;
340
368
computePipeline = null ;
341
369
reset ( ) ;
342
370
} ) ;
343
371
344
372
// Automatically rebuild the pipelines when the workgroup size is changed.
345
- document . querySelector ( ' #wgsize' ) . addEventListener ( ' change' , initPipelines ) ;
373
+ document . querySelector ( " #wgsize" ) . addEventListener ( " change" , initPipelines ) ;
346
374
347
375
// Add an event handler to update render parameters when the window is resized.
348
- window . addEventListener ( ' resize' , updateRenderParams ) ;
376
+ window . addEventListener ( " resize" , updateRenderParams ) ;
349
377
350
378
// Handle key presses for user controls.
351
- document . addEventListener ( ' keydown' , ( e : KeyboardEvent ) => {
352
- if ( e . key == ' ' ) {
379
+ document . addEventListener ( " keydown" , ( e : KeyboardEvent ) => {
380
+ if ( e . key == " " ) {
353
381
pause ( ) ;
354
382
}
355
383
currentKey = e ;
356
384
} ) ;
357
- document . addEventListener ( ' keyup' , ( e : KeyboardEvent ) => {
385
+ document . addEventListener ( " keyup" , ( e : KeyboardEvent ) => {
358
386
currentKey = null ;
359
387
} ) ;
0 commit comments