webgpu-computed 0.0.15 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +177 -37
- package/package.json +1 -1
- package/src/index.js +126 -124
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# webgpu-computed
|
|
2
2
|
|
|
3
3
|
🌐 Other language versions:
|
|
4
|
-
- [
|
|
4
|
+
- [中文](https://github.com/xiaguochuqiu/webgpu-computed/blob/main/README.zh.md)
|
|
5
5
|
|
|
6
|
-
A simplified WebGPU
|
|
6
|
+
A simplified WebGPU compute library that encapsulates tedious initialization and buffer management, allowing developers to focus on WGSL shader logic.
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
@@ -14,7 +14,9 @@ A simplified WebGPU computing library that encapsulates tedious initialization a
|
|
|
14
14
|
- 📚 Built-in common WGSL functions
|
|
15
15
|
- ✅ Support for Node.js environment
|
|
16
16
|
- 🛠️ TypeScript support
|
|
17
|
-
- 📖 Detailed
|
|
17
|
+
- 📖 Detailed Chinese documentation and examples
|
|
18
|
+
- 🔄 Support for buffer reuse
|
|
19
|
+
- ⚛️ Support for atomic operations (u32)
|
|
18
20
|
|
|
19
21
|
## Installation
|
|
20
22
|
|
|
@@ -26,7 +28,7 @@ npm install webgpu-computed
|
|
|
26
28
|
|
|
27
29
|
### 1. Initialize WebGPU
|
|
28
30
|
|
|
29
|
-
Before using any
|
|
31
|
+
Before using any compute features, you need to initialize the WebGPU environment:
|
|
30
32
|
|
|
31
33
|
```javascript
|
|
32
34
|
import { GpuComputed } from 'webgpu-computed';
|
|
@@ -34,13 +36,13 @@ import { GpuComputed } from 'webgpu-computed';
|
|
|
34
36
|
// Initialize WebGPU
|
|
35
37
|
await GpuComputed.init();
|
|
36
38
|
|
|
37
|
-
//
|
|
39
|
+
// In Node.js environment, call after use:
|
|
38
40
|
// GpuComputed.destroy()
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
### 2. Perform Simple Computation
|
|
42
44
|
|
|
43
|
-
Here
|
|
45
|
+
Here's a simple vector addition example:
|
|
44
46
|
|
|
45
47
|
```javascript
|
|
46
48
|
import { GpuComputed } from 'webgpu-computed';
|
|
@@ -52,7 +54,7 @@ const data = {
|
|
|
52
54
|
output: new Array(4).fill(0) // Output buffer
|
|
53
55
|
};
|
|
54
56
|
|
|
55
|
-
// WGSL
|
|
57
|
+
// WGSL compute code
|
|
56
58
|
const code = `
|
|
57
59
|
output[index] = inputA[index] + inputB[index];
|
|
58
60
|
`;
|
|
@@ -62,7 +64,7 @@ GpuComputed.computed({
|
|
|
62
64
|
code,
|
|
63
65
|
data,
|
|
64
66
|
synchronize: ["output"], // Fields to return
|
|
65
|
-
workgroupCount: [1] //
|
|
67
|
+
workgroupCount: [1] // Workgroup count
|
|
66
68
|
}).then(results => {
|
|
67
69
|
console.log(results); // [[1.5, 3.5, 5.5, 7.5]]
|
|
68
70
|
})
|
|
@@ -91,15 +93,66 @@ GpuComputed.computed({
|
|
|
91
93
|
code,
|
|
92
94
|
data,
|
|
93
95
|
synchronize: ["output"], // Fields to return
|
|
94
|
-
workgroupCount: [1] //
|
|
96
|
+
workgroupCount: [1] // Workgroup count
|
|
95
97
|
}).then(results => {
|
|
96
98
|
console.log(results); // [[1.100000023841858,2.200000047683716,3.299999952316284,0,0.20000000298023224,0.4000000059604645,0.6000000238418579,0,4.400000095367432,5.5,6.599999904632568,0,0.800000011920929,1,1.2000000476837158,0]]
|
|
97
99
|
})
|
|
98
100
|
```
|
|
99
101
|
|
|
100
|
-
### 4.
|
|
102
|
+
### 4. Using Different Data Types
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
#### Using Unsigned Integers (u32)
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
import { GpuComputed } from 'webgpu-computed';
|
|
108
|
+
|
|
109
|
+
const data = {
|
|
110
|
+
counters: new Uint32Array([0, 1, 2, 3]), // u32 array
|
|
111
|
+
output: new Uint32Array(4)
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const code = `
|
|
115
|
+
output[index] = counters[index] * 2u;
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
const results = await GpuComputed.computed({
|
|
119
|
+
code,
|
|
120
|
+
data,
|
|
121
|
+
synchronize: ["output"],
|
|
122
|
+
workgroupCount: [1]
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
console.log(results[0]); // [0, 2, 4, 6]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Using Atomic Operations
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
import { GpuComputed, AtomicUint32Array } from 'webgpu-computed';
|
|
132
|
+
|
|
133
|
+
const data = {
|
|
134
|
+
atomicCounter: new AtomicUint32Array([0]), // Atomic counter
|
|
135
|
+
output: new Uint32Array(4)
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const code = `
|
|
139
|
+
let old = atomicAdd(&atomicCounter[0], 1u);
|
|
140
|
+
output[index] = old + 1u;
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
const results = await GpuComputed.computed({
|
|
144
|
+
code,
|
|
145
|
+
data,
|
|
146
|
+
synchronize: ["output"],
|
|
147
|
+
workgroupCount: [1]
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
console.log(results[0]); // [1, 2, 3, 4]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 5. Manually Creating a GpuComputed Instance
|
|
154
|
+
|
|
155
|
+
If you need finer control, you can create a GpuComputed instance directly:
|
|
103
156
|
|
|
104
157
|
```javascript
|
|
105
158
|
import { GpuComputed } from 'webgpu-computed';
|
|
@@ -116,7 +169,7 @@ const gpuComputed = new GpuComputed(template, {
|
|
|
116
169
|
code: `
|
|
117
170
|
output[index] = inputA[index] + inputB[index];
|
|
118
171
|
`,
|
|
119
|
-
workgroupSize: [32, 1, 1] // Optional:
|
|
172
|
+
workgroupSize: [32, 1, 1] // Optional: Custom workgroup size
|
|
120
173
|
});
|
|
121
174
|
|
|
122
175
|
// 3. Initialize pipeline
|
|
@@ -138,6 +191,27 @@ const results = await gpuComputed.computed(bindGroup, [1], ['output']);
|
|
|
138
191
|
console.log(results[0]); // [1.5, 3.5, 5.5, 7.5]
|
|
139
192
|
```
|
|
140
193
|
|
|
194
|
+
#### Buffer Reuse
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
// First computation
|
|
198
|
+
const data1 = { input: [1, 2, 3], output: [0, 0, 0] };
|
|
199
|
+
const bindGroup1 = gpuComputed.createBindGroup(data1);
|
|
200
|
+
const results1 = await gpuComputed.computed(bindGroup1, [1], ['output']);
|
|
201
|
+
|
|
202
|
+
// Reuse the output buffer from the first computation for the second
|
|
203
|
+
// Note: The template must still include output to build the compute code
|
|
204
|
+
// const template = {
|
|
205
|
+
// input: [] as number[],
|
|
206
|
+
// output: [] as number[]
|
|
207
|
+
// };
|
|
208
|
+
const data2 = { input: [4, 5, 6]};
|
|
209
|
+
const bindGroup2 = gpuComputed.createBindGroup(data2, bindGroup1); // Reuse output buffer
|
|
210
|
+
const results2 = await gpuComputed.computed(bindGroup2, [1], ['output']);
|
|
211
|
+
|
|
212
|
+
console.log(results2[0]); // [4, 5, 6] output buffer reused
|
|
213
|
+
```
|
|
214
|
+
|
|
141
215
|
#### Using Struct Data
|
|
142
216
|
|
|
143
217
|
```javascript
|
|
@@ -207,40 +281,49 @@ Initializes the WebGPU environment. Must be called before using other features.
|
|
|
207
281
|
|
|
208
282
|
**Returns**: `Promise<void>`
|
|
209
283
|
|
|
210
|
-
**Throws**: If the browser does not support WebGPU or fails to
|
|
284
|
+
**Throws**: If the browser does not support WebGPU or fails to get adapter/device
|
|
211
285
|
|
|
212
286
|
##### `GpuComputed.computed(options)`
|
|
213
287
|
|
|
214
|
-
Executes a GPU
|
|
288
|
+
Executes a GPU compute task.
|
|
215
289
|
|
|
216
290
|
**Parameters**:
|
|
217
291
|
|
|
218
|
-
- `code` (string): WGSL
|
|
292
|
+
- `code` (string): WGSL compute code
|
|
219
293
|
- `data` (object): Input/output data object
|
|
220
|
-
- `workgroupCount` (array):
|
|
294
|
+
- `workgroupCount` (array): Workgroup count [x, y?, z?]
|
|
221
295
|
- `workgroupSize` (array, optional): Workgroup size, default [32, 1, 1]
|
|
222
296
|
- `globalInvocationIdName` (string, optional): Global invocation ID variable name, default "grid"
|
|
223
297
|
- `workgroupIndexName` (string, optional): Workgroup index variable name, default "index"
|
|
224
|
-
- `synchronize` (array, optional): Array of buffer names to
|
|
225
|
-
- `beforeCodes` (array, optional): WGSL code snippets before the
|
|
298
|
+
- `synchronize` (array, optional): Array of buffer names to sync back to CPU
|
|
299
|
+
- `beforeCodes` (array, optional): WGSL code snippets before the compute function
|
|
226
300
|
- `onSuccess` (function, optional): Success callback function
|
|
227
301
|
|
|
228
|
-
**Returns**: `Promise<Array<Float32Array>>` - Data
|
|
302
|
+
**Returns**: `Promise<Array<Float32Array | Uint32Array | Int32Array>>` - Data of synchronized buffers
|
|
229
303
|
|
|
230
304
|
### Data Types
|
|
231
305
|
|
|
232
306
|
Supports the following WGSL types:
|
|
233
307
|
|
|
234
308
|
- `f32`: Single-precision float
|
|
309
|
+
- `u32`: Unsigned 32-bit integer
|
|
235
310
|
- `vec2`: 2D vector
|
|
236
311
|
- `vec3`: 3D vector
|
|
237
312
|
- `vec4`: 4D vector
|
|
238
313
|
- `mat3x3`: 3x3 matrix
|
|
239
314
|
- `mat4x4`: 4x4 matrix
|
|
240
315
|
|
|
316
|
+
### Supported JavaScript Types
|
|
317
|
+
|
|
318
|
+
- `number[]`: Number array (automatically converted to Float32Array)
|
|
319
|
+
- `Float32Array`: 32-bit float array
|
|
320
|
+
- `Uint32Array`: 32-bit unsigned integer array
|
|
321
|
+
- `Int32Array`: 32-bit signed integer array
|
|
322
|
+
- `AtomicUint32Array`: Atomic unsigned integer array
|
|
323
|
+
|
|
241
324
|
### Built-in WGSL Functions
|
|
242
325
|
|
|
243
|
-
The library provides some
|
|
326
|
+
The library provides some common WGSL helper functions:
|
|
244
327
|
|
|
245
328
|
#### Quaternion Rotation
|
|
246
329
|
|
|
@@ -279,20 +362,36 @@ await GpuComputed.computed({
|
|
|
279
362
|
});
|
|
280
363
|
```
|
|
281
364
|
|
|
365
|
+
### Buffer Reuse
|
|
366
|
+
|
|
367
|
+
```javascript
|
|
368
|
+
// Create initial bind group
|
|
369
|
+
const initialData = { buffer: new Float32Array(1000) };
|
|
370
|
+
const bindGroup = gpuComputed.createBindGroup(initialData);
|
|
371
|
+
|
|
372
|
+
// Reuse buffer for multiple computations
|
|
373
|
+
for (let i = 0; i < 10; i++) {
|
|
374
|
+
const newData = { buffer: new Float32Array(1000).fill(i) };
|
|
375
|
+
const reusedBindGroup = gpuComputed.createBindGroup(newData, bindGroup);
|
|
376
|
+
const results = await gpuComputed.computed(reusedBindGroup, [32], ['buffer']);
|
|
377
|
+
// Process results...
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
282
381
|
### Synchronizing Data Back to CPU
|
|
283
382
|
|
|
284
383
|
```javascript
|
|
285
384
|
const results = await GpuComputed.computed({
|
|
286
385
|
code: '...',
|
|
287
386
|
data: {...},
|
|
288
|
-
synchronize: ['output'], // Specify buffers to
|
|
387
|
+
synchronize: ['output'], // Specify buffers to sync
|
|
289
388
|
workgroupCount: [1]
|
|
290
389
|
});
|
|
291
390
|
|
|
292
391
|
// results contains synchronized data
|
|
293
392
|
```
|
|
294
393
|
|
|
295
|
-
### Callback
|
|
394
|
+
### Callback Functions
|
|
296
395
|
|
|
297
396
|
```javascript
|
|
298
397
|
await GpuComputed.computed({
|
|
@@ -300,7 +399,7 @@ await GpuComputed.computed({
|
|
|
300
399
|
data: {...},
|
|
301
400
|
workgroupCount: [1],
|
|
302
401
|
onSuccess: ({ gpuComputed, group, results }) => {
|
|
303
|
-
console.log('Computation
|
|
402
|
+
console.log('Computation complete', results);
|
|
304
403
|
}
|
|
305
404
|
});
|
|
306
405
|
```
|
|
@@ -335,9 +434,50 @@ const simpleResults = await GpuComputed.computed({
|
|
|
335
434
|
synchronize: ['output']
|
|
336
435
|
});
|
|
337
436
|
|
|
338
|
-
console.log('Simple computation
|
|
437
|
+
console.log('Simple computation results:', simpleResults[0]); // [1.5, 3.5, 5.5, 7.5]
|
|
438
|
+
|
|
439
|
+
// 3. Using u32 type example
|
|
440
|
+
console.log('\n=== Using u32 Type ===');
|
|
441
|
+
const u32Data = {
|
|
442
|
+
counters: new Uint32Array([10, 20, 30, 40]),
|
|
443
|
+
output: new Uint32Array(4)
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const u32Code = `
|
|
447
|
+
output[index] = counters[index] + 5u;
|
|
448
|
+
`;
|
|
449
|
+
|
|
450
|
+
const u32Results = await GpuComputed.computed({
|
|
451
|
+
code: u32Code,
|
|
452
|
+
data: u32Data,
|
|
453
|
+
workgroupCount: [1],
|
|
454
|
+
synchronize: ['output']
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
console.log('u32 computation results:', u32Results[0]); // [15, 25, 35, 45]
|
|
458
|
+
|
|
459
|
+
// 4. Atomic operations example
|
|
460
|
+
console.log('\n=== Atomic Operations Example ===');
|
|
461
|
+
const atomicData = {
|
|
462
|
+
counter: new AtomicUint32Array([0]),
|
|
463
|
+
results: new Uint32Array(4)
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const atomicCode = `
|
|
467
|
+
let oldValue = atomicAdd(&counter[0], 1u);
|
|
468
|
+
results[index] = oldValue;
|
|
469
|
+
`;
|
|
470
|
+
|
|
471
|
+
const atomicResults = await GpuComputed.computed({
|
|
472
|
+
code: atomicCode,
|
|
473
|
+
data: atomicData,
|
|
474
|
+
workgroupCount: [1],
|
|
475
|
+
synchronize: ['results']
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
console.log('Atomic operation results:', atomicResults[0]); // [0, 1, 2, 3]
|
|
339
479
|
|
|
340
|
-
//
|
|
480
|
+
// 5. Complex data structure example (structs)
|
|
341
481
|
console.log('\n=== Complex Data Structure Computation ===');
|
|
342
482
|
const complexData = {
|
|
343
483
|
particles: [
|
|
@@ -363,9 +503,9 @@ const complexResults = await GpuComputed.computed({
|
|
|
363
503
|
synchronize: ['output']
|
|
364
504
|
});
|
|
365
505
|
|
|
366
|
-
console.log('Complex computation
|
|
506
|
+
console.log('Complex computation results:', complexResults[0]);
|
|
367
507
|
|
|
368
|
-
//
|
|
508
|
+
// 6. Using built-in WGSL functions example
|
|
369
509
|
console.log('\n=== Using Built-in WGSL Functions ===');
|
|
370
510
|
const wgslFunData = {
|
|
371
511
|
points: [
|
|
@@ -406,9 +546,9 @@ const wgslFunResults = await GpuComputed.computed({
|
|
|
406
546
|
synchronize: ['results']
|
|
407
547
|
});
|
|
408
548
|
|
|
409
|
-
console.log('OBB detection
|
|
549
|
+
console.log('OBB detection results:', wgslFunResults[0]); // [1, 1, 1] All points inside OBB
|
|
410
550
|
|
|
411
|
-
//
|
|
551
|
+
// 7. Custom workgroup configuration example
|
|
412
552
|
console.log('\n=== Custom Workgroup Configuration ===');
|
|
413
553
|
const largeData = {
|
|
414
554
|
largeArray: new Array(1024).fill(0).map((_, i) => i * 1.0),
|
|
@@ -427,10 +567,10 @@ const largeResults = await GpuComputed.computed({
|
|
|
427
567
|
synchronize: ['output']
|
|
428
568
|
});
|
|
429
569
|
|
|
430
|
-
console.log('Large array computation
|
|
570
|
+
console.log('Large array computation results (first 10):', largeResults[0].slice(0, 10));
|
|
431
571
|
|
|
432
|
-
//
|
|
433
|
-
console.log('\n=== Using Callback
|
|
572
|
+
// 8. Using callback functions example
|
|
573
|
+
console.log('\n=== Using Callback Functions ===');
|
|
434
574
|
const callbackData = {
|
|
435
575
|
values: [10.0, 20.0, 30.0],
|
|
436
576
|
squares: new Array(3).fill(0)
|
|
@@ -446,12 +586,12 @@ await GpuComputed.computed({
|
|
|
446
586
|
workgroupCount: [1],
|
|
447
587
|
synchronize: ['squares'],
|
|
448
588
|
onSuccess: ({ gpuComputed, group, results }) => {
|
|
449
|
-
console.log('Callback triggered, square computation
|
|
589
|
+
console.log('Callback triggered, square computation results:', results[0]); // [100, 400, 900]
|
|
450
590
|
}
|
|
451
591
|
});
|
|
452
592
|
|
|
453
|
-
//
|
|
454
|
-
console.log('\n=== Multi-dimensional
|
|
593
|
+
// 9. Multi-dimensional workgroups example
|
|
594
|
+
console.log('\n=== Multi-dimensional Workgroups ===');
|
|
455
595
|
const matrixData = {
|
|
456
596
|
matrixA: new Array(16).fill(0).map((_, i) => i * 1.0),
|
|
457
597
|
matrixB: new Array(16).fill(0).map((_, i) => (i + 1) * 1.0),
|
|
@@ -473,7 +613,7 @@ const matrixResults = await GpuComputed.computed({
|
|
|
473
613
|
synchronize: ['result']
|
|
474
614
|
});
|
|
475
615
|
|
|
476
|
-
console.log('Matrix computation
|
|
616
|
+
console.log('Matrix computation results:', matrixResults[0]);
|
|
477
617
|
|
|
478
618
|
console.log('\nAll feature examples completed!');
|
|
479
619
|
```
|
|
@@ -489,7 +629,7 @@ Ensure the browser supports the WebGPU API.
|
|
|
489
629
|
|
|
490
630
|
## Contributing
|
|
491
631
|
|
|
492
|
-
|
|
632
|
+
Issues and Pull Requests are welcome!
|
|
493
633
|
|
|
494
634
|
## License
|
|
495
635
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
async function z(
|
|
1
|
+
async function z(f, e = !0) {
|
|
2
2
|
if (typeof global < "u" && typeof require < "u")
|
|
3
|
-
return require(
|
|
3
|
+
return require(f);
|
|
4
4
|
{
|
|
5
5
|
let r = await import(
|
|
6
6
|
/* @vite-ignore */
|
|
7
|
-
|
|
7
|
+
f
|
|
8
8
|
);
|
|
9
9
|
return e && (r = r.default), r;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
function q(
|
|
13
|
-
if (
|
|
14
|
-
if (
|
|
15
|
-
if (
|
|
12
|
+
function q(f) {
|
|
13
|
+
if (f instanceof Float32Array) return "f32";
|
|
14
|
+
if (f instanceof Int32Array) return "i32";
|
|
15
|
+
if (f instanceof Uint32Array) return "u32";
|
|
16
16
|
throw new Error(
|
|
17
|
-
`Unsupported ArrayBufferView type: ${
|
|
17
|
+
`Unsupported ArrayBufferView type: ${f.constructor.name}`
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
|
-
function S(
|
|
21
|
-
return
|
|
20
|
+
function S(f) {
|
|
21
|
+
return f && f[0].toUpperCase() + f.slice(1);
|
|
22
22
|
}
|
|
23
|
-
function x(
|
|
24
|
-
return
|
|
23
|
+
function x(f) {
|
|
24
|
+
return G.includes(f);
|
|
25
25
|
}
|
|
26
|
-
function m(
|
|
27
|
-
return
|
|
26
|
+
function m(f) {
|
|
27
|
+
return f && Array.isArray(f) && f.length ? f.every((e) => e && typeof e == "object" && "name" in e && "type" in e && x(e.type)) : !1;
|
|
28
28
|
}
|
|
29
|
-
function
|
|
30
|
-
return
|
|
29
|
+
function B(f) {
|
|
30
|
+
return f && "layout" in f && m(f.layout);
|
|
31
31
|
}
|
|
32
|
-
function _(
|
|
32
|
+
function _(f, e) {
|
|
33
33
|
const r = E[e];
|
|
34
|
-
return (r -
|
|
34
|
+
return (r - f % r) % r;
|
|
35
35
|
}
|
|
36
|
-
function P(
|
|
37
|
-
return
|
|
36
|
+
function P(f, e) {
|
|
37
|
+
return f + (e - f % e) % e;
|
|
38
38
|
}
|
|
39
|
-
function U(
|
|
40
|
-
const e = Object.keys(
|
|
41
|
-
const l =
|
|
39
|
+
function U(f) {
|
|
40
|
+
const e = Object.keys(f), r = e.map((c) => {
|
|
41
|
+
const l = f[c];
|
|
42
42
|
if (Array.isArray(l)) {
|
|
43
43
|
for (const d of ["vec2", "vec3", "vec4", "mat3x3", "mat4x4"])
|
|
44
|
-
if (
|
|
44
|
+
if ($[d] === l.length) return d;
|
|
45
45
|
throw new Error(`${c} 不支持的数组长度 ${l.length}`);
|
|
46
46
|
}
|
|
47
47
|
if (typeof l == "number") return "f32";
|
|
48
48
|
throw new Error(`${c} 不支持的类型`);
|
|
49
49
|
});
|
|
50
50
|
if (e.length !== r.length) throw new Error("keys 与 types 长度不一致");
|
|
51
|
-
let
|
|
52
|
-
const
|
|
51
|
+
let s = 0;
|
|
52
|
+
const u = e.map((c, l) => {
|
|
53
53
|
const d = r[l];
|
|
54
|
-
|
|
55
|
-
const
|
|
54
|
+
s += _(s, d);
|
|
55
|
+
const a = {
|
|
56
56
|
name: c,
|
|
57
57
|
type: d,
|
|
58
|
-
offset:
|
|
59
|
-
size:
|
|
58
|
+
offset: s,
|
|
59
|
+
size: $[d]
|
|
60
60
|
};
|
|
61
|
-
return
|
|
61
|
+
return s += $[d], a;
|
|
62
62
|
}), t = Math.max(...r.map((c) => E[c]));
|
|
63
63
|
return {
|
|
64
|
-
stride:
|
|
65
|
-
layout:
|
|
64
|
+
stride: s + (t - s % t) % t,
|
|
65
|
+
layout: u
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
|
-
const
|
|
68
|
+
const $ = {
|
|
69
69
|
f32: 1,
|
|
70
70
|
u32: 1,
|
|
71
71
|
vec2: 2,
|
|
@@ -86,8 +86,8 @@ const B = {
|
|
|
86
86
|
};
|
|
87
87
|
class D extends Uint32Array {
|
|
88
88
|
}
|
|
89
|
-
const
|
|
90
|
-
let w = null,
|
|
89
|
+
const G = ["f32", "u32", "vec2", "vec3", "vec4", "mat3x3", "mat4x4"];
|
|
90
|
+
let w = null, A = null;
|
|
91
91
|
class T {
|
|
92
92
|
template;
|
|
93
93
|
option;
|
|
@@ -105,9 +105,9 @@ class T {
|
|
|
105
105
|
const r = (t) => {
|
|
106
106
|
let n = 0;
|
|
107
107
|
t.forEach((c) => {
|
|
108
|
-
n += _(n, c.type), c.offset = n, c.size =
|
|
108
|
+
n += _(n, c.type), c.offset = n, c.size = $[c.type], n += c.size;
|
|
109
109
|
});
|
|
110
|
-
},
|
|
110
|
+
}, s = (t) => {
|
|
111
111
|
r(t.layout);
|
|
112
112
|
const n = t.layout[t.layout.length - 1], c = n.offset + n.size;
|
|
113
113
|
let l = 1;
|
|
@@ -116,15 +116,15 @@ class T {
|
|
|
116
116
|
};
|
|
117
117
|
Object.keys(e).forEach((t) => {
|
|
118
118
|
const n = e[t];
|
|
119
|
-
m(n) ? r(n) :
|
|
119
|
+
m(n) ? r(n) : B(n) ? s(n) : Array.isArray(n) && typeof n[0] == "number" && (e[t] = new Float32Array());
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
/** 获取Gpu设备
|
|
123
123
|
* @returns
|
|
124
124
|
*/
|
|
125
125
|
async getDevice() {
|
|
126
|
-
if (!w || !
|
|
127
|
-
return { adapter: w, device:
|
|
126
|
+
if (!w || !A) throw new Error("webgpu未初始化或不可用");
|
|
127
|
+
return { adapter: w, device: A };
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
130
|
* 初始化计算管线
|
|
@@ -132,8 +132,8 @@ class T {
|
|
|
132
132
|
async initPipeline() {
|
|
133
133
|
if (!this.template) throw new Error("初始化计算管线错误,未找到可用数据模版");
|
|
134
134
|
await T.init();
|
|
135
|
-
const e = this.template, { device: r } = await this.getDevice(),
|
|
136
|
-
this.device = r, Object.keys(e).forEach((
|
|
135
|
+
const e = this.template, { device: r } = await this.getDevice(), s = [], u = [], t = [];
|
|
136
|
+
this.device = r, Object.keys(e).forEach((i, y) => {
|
|
137
137
|
if (t.push({
|
|
138
138
|
binding: y,
|
|
139
139
|
// 绑定到组里的0号位插槽
|
|
@@ -142,15 +142,15 @@ class T {
|
|
|
142
142
|
buffer: {
|
|
143
143
|
type: "storage"
|
|
144
144
|
}
|
|
145
|
-
}), m(e[
|
|
146
|
-
const g = e[
|
|
147
|
-
|
|
148
|
-
} else if (
|
|
149
|
-
const g = e[
|
|
150
|
-
|
|
151
|
-
} else if (ArrayBuffer.isView(e[
|
|
152
|
-
const g = e[
|
|
153
|
-
g instanceof D ?
|
|
145
|
+
}), m(e[i])) {
|
|
146
|
+
const g = e[i], h = g.map((b) => `${b.name}:${b.type === "f32" ? "f32" : b.type + "<f32>"}`).join(","), v = `${S(i)}Struct`;
|
|
147
|
+
s.push(`struct ${S(i)}Struct {${h}};`), u.push(`@group(0) @binding(${y}) var<storage, read_write> ${i}: ${v};`);
|
|
148
|
+
} else if (B(e[i])) {
|
|
149
|
+
const g = e[i], h = g.layout.map((b) => `${b.name}:${b.type === "f32" ? "f32" : b.type + "<f32>"}`).join(","), v = `${S(i)}Struct`;
|
|
150
|
+
s.push(`struct ${v} {${h}};`), u.push(`@group(0) @binding(${y}) var<storage, read_write> ${i}: array<${v}>;`);
|
|
151
|
+
} else if (ArrayBuffer.isView(e[i]) && !(e[i] instanceof DataView)) {
|
|
152
|
+
const g = e[i], h = q(g);
|
|
153
|
+
g instanceof D ? u.push(`@group(0) @binding(${y}) var<storage, read_write> ${i}: array<atomic<${h}>>;`) : u.push(`@group(0) @binding(${y}) var<storage, read_write> ${i}: array<${h}>;`);
|
|
154
154
|
}
|
|
155
155
|
});
|
|
156
156
|
const {
|
|
@@ -158,18 +158,18 @@ class T {
|
|
|
158
158
|
workgroupSize: c = [32, 1, 1],
|
|
159
159
|
globalInvocationIdName: l = "grid",
|
|
160
160
|
workgroupIndexName: d = "index",
|
|
161
|
-
code:
|
|
161
|
+
code: a = ""
|
|
162
162
|
} = this.option ?? {}, o = (
|
|
163
163
|
/*wgsl*/
|
|
164
164
|
`
|
|
165
|
-
${
|
|
166
|
-
${
|
|
165
|
+
${s.join("")}
|
|
166
|
+
${u.join("")}
|
|
167
167
|
${n.join(" ") ?? ""}
|
|
168
168
|
|
|
169
169
|
@compute @workgroup_size(${c.join(",")})
|
|
170
170
|
fn main(@builtin(global_invocation_id) ${l}: vec3<u32>) {
|
|
171
171
|
var ${d} = ${l}.x;
|
|
172
|
-
${
|
|
172
|
+
${a}
|
|
173
173
|
}
|
|
174
174
|
`
|
|
175
175
|
);
|
|
@@ -196,50 +196,52 @@ ${f.join("")}
|
|
|
196
196
|
createBindGroup(e, r) {
|
|
197
197
|
if (!this.template) throw new Error("创建buffer组错误,未找到可用数据模版");
|
|
198
198
|
if (!this.device) throw new Error("创建buffer组错误,未找到可用的gpu设备,请确保初始化完计算管线");
|
|
199
|
-
const
|
|
200
|
-
function c(
|
|
201
|
-
if (
|
|
199
|
+
const s = this.device, u = this.template, t = [], n = r?.buffers?.reduce((a, o) => (a.set(o.name, o.buffer), a), /* @__PURE__ */ new Map());
|
|
200
|
+
function c(a, o, p = 0, i) {
|
|
201
|
+
if (ArrayBuffer.isView(a) || Array.isArray(a)) return a;
|
|
202
|
+
if (!i) {
|
|
202
203
|
const y = o[o.length - 1], g = y.offset + y.size;
|
|
203
204
|
let h = 1;
|
|
204
|
-
for (const
|
|
205
|
-
|
|
205
|
+
for (const v of o) h = Math.max(h, E[v.type]);
|
|
206
|
+
i = i ?? new Float32Array(P(g, h)).fill(0);
|
|
206
207
|
}
|
|
207
208
|
return o.forEach((y) => {
|
|
208
|
-
let g =
|
|
209
|
+
let g = a[y.name];
|
|
209
210
|
Array.isArray(g) || (g = [g]);
|
|
210
211
|
for (let h = 0; h < y.size; h++)
|
|
211
|
-
|
|
212
|
-
}),
|
|
212
|
+
i[p + y.offset + h] = Number(g[h] ?? 0);
|
|
213
|
+
}), i;
|
|
213
214
|
}
|
|
214
|
-
function l(
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
function l(a, o) {
|
|
216
|
+
if (ArrayBuffer.isView(a) || typeof a[0] == "number") return a;
|
|
217
|
+
const p = new Float32Array(o.stride * a.length).fill(0);
|
|
218
|
+
return a.forEach((i, y) => {
|
|
217
219
|
const g = y * o.stride;
|
|
218
|
-
c(
|
|
220
|
+
c(i, o.layout, g, p);
|
|
219
221
|
}), p;
|
|
220
222
|
}
|
|
221
|
-
return Object.keys(
|
|
222
|
-
if (!(
|
|
223
|
-
if (n && n.has(
|
|
224
|
-
return t.push({ name:
|
|
225
|
-
throw new Error(`传入的数据中,不存在${
|
|
223
|
+
return Object.keys(u).forEach((a) => {
|
|
224
|
+
if (!(a in e)) {
|
|
225
|
+
if (n && n.has(a))
|
|
226
|
+
return t.push({ name: a, buffer: n.get(a) });
|
|
227
|
+
throw new Error(`传入的数据中,不存在${a}字段`);
|
|
226
228
|
}
|
|
227
|
-
const o =
|
|
228
|
-
let
|
|
229
|
-
m(o) ?
|
|
229
|
+
const o = u[a], p = e[a];
|
|
230
|
+
let i = [];
|
|
231
|
+
m(o) ? i = c(p, o) : B(o) ? i = l(p, o) : (Array.isArray(p) || ArrayBuffer.isView(p)) && (i = p);
|
|
230
232
|
let y = null;
|
|
231
|
-
if (o instanceof Float32Array ? y = new Float32Array(
|
|
232
|
-
const g =
|
|
233
|
+
if (o instanceof Float32Array ? y = new Float32Array(i) : o instanceof Uint32Array ? y = new Uint32Array(i) : o instanceof Int32Array ? y = new Int32Array(i) : y = new Float32Array(i), !y) throw new Error("不支持的数组类型" + o);
|
|
234
|
+
const g = s.createBuffer({
|
|
233
235
|
size: y.byteLength,
|
|
234
236
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE
|
|
235
237
|
});
|
|
236
|
-
|
|
238
|
+
s.queue.writeBuffer(g, 0, y), t.push({ name: a, buffer: g });
|
|
237
239
|
}), {
|
|
238
|
-
group:
|
|
240
|
+
group: s.createBindGroup({
|
|
239
241
|
layout: this.groupLayout,
|
|
240
|
-
entries: t.map((
|
|
242
|
+
entries: t.map((a, o) => ({
|
|
241
243
|
binding: o,
|
|
242
|
-
resource: { buffer:
|
|
244
|
+
resource: { buffer: a.buffer }
|
|
243
245
|
}))
|
|
244
246
|
}),
|
|
245
247
|
buffers: t
|
|
@@ -251,23 +253,23 @@ ${f.join("")}
|
|
|
251
253
|
*/
|
|
252
254
|
dataMap(e, r) {
|
|
253
255
|
if (!(r in this.template)) throw new Error("未找到数据字段:" + r);
|
|
254
|
-
if (
|
|
255
|
-
const
|
|
256
|
-
for (let n = 0; n <
|
|
257
|
-
const c = n *
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
l[d.name] =
|
|
256
|
+
if (B(this.template[r])) {
|
|
257
|
+
const s = this.template[r], u = e.length / s.stride, t = [];
|
|
258
|
+
for (let n = 0; n < u; n++) {
|
|
259
|
+
const c = n * s.stride, l = {};
|
|
260
|
+
s.layout.forEach((d) => {
|
|
261
|
+
const a = e.slice(c + d.offset, c + d.offset + d.size);
|
|
262
|
+
l[d.name] = a.length === 1 ? a[0] : a;
|
|
261
263
|
}), t.push(l);
|
|
262
264
|
}
|
|
263
265
|
return t;
|
|
264
266
|
}
|
|
265
267
|
if (m(this.template[r])) {
|
|
266
|
-
const
|
|
267
|
-
return
|
|
268
|
+
const s = this.template[r], u = {};
|
|
269
|
+
return s.forEach((t) => {
|
|
268
270
|
const n = e.slice(t.offset, t.offset + t.size);
|
|
269
|
-
|
|
270
|
-
}),
|
|
271
|
+
u[t.name] = n.length === 1 ? n[0] : n;
|
|
272
|
+
}), u;
|
|
271
273
|
}
|
|
272
274
|
return e;
|
|
273
275
|
}
|
|
@@ -277,69 +279,69 @@ ${f.join("")}
|
|
|
277
279
|
* @param synchronize 需要同步的数据字段
|
|
278
280
|
* @returns
|
|
279
281
|
*/
|
|
280
|
-
async computed(e, r,
|
|
282
|
+
async computed(e, r, s = []) {
|
|
281
283
|
if (!this.pipeline) throw new Error("未找到可用计算管线,请确保计算管线已经创建成功");
|
|
282
|
-
const
|
|
284
|
+
const u = this.device, t = this.pipeline, n = u.createCommandEncoder(), c = n.beginComputePass();
|
|
283
285
|
c.setPipeline(t), c.setBindGroup(0, e.group), c.dispatchWorkgroups(r[0], r[1], r[2]), c.end();
|
|
284
286
|
const l = e.buffers?.map((o) => {
|
|
285
|
-
if (
|
|
286
|
-
const p =
|
|
287
|
+
if (s?.includes(o.name)) {
|
|
288
|
+
const p = u.createBuffer({
|
|
287
289
|
size: o.buffer.size,
|
|
288
290
|
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
|
|
289
291
|
});
|
|
290
292
|
return n.copyBufferToBuffer(o.buffer, 0, p, 0, p.size), { buffer: p, name: o.name };
|
|
291
293
|
}
|
|
292
294
|
}).filter((o) => !!o);
|
|
293
|
-
|
|
295
|
+
u.queue.submit([n.finish()]), await u.queue.onSubmittedWorkDone();
|
|
294
296
|
const d = /* @__PURE__ */ new Map();
|
|
295
297
|
return await Promise.all(
|
|
296
298
|
l.map(async (o) => {
|
|
297
299
|
await o.buffer.mapAsync(GPUMapMode.READ);
|
|
298
300
|
const p = o.buffer.getMappedRange();
|
|
299
|
-
let
|
|
300
|
-
this.template[o.name] instanceof Float32Array ?
|
|
301
|
-
const y = [...
|
|
301
|
+
let i = null;
|
|
302
|
+
this.template[o.name] instanceof Float32Array ? i = new Float32Array(p) : this.template[o.name] instanceof Uint32Array ? i = new Uint32Array(p) : this.template[o.name] instanceof Int32Array ? i = new Int32Array(p) : i = new Float32Array(p);
|
|
303
|
+
const y = [...i];
|
|
302
304
|
d.set(o.name, y);
|
|
303
305
|
})
|
|
304
|
-
),
|
|
306
|
+
), s.map((o) => d.get(o));
|
|
305
307
|
}
|
|
306
308
|
/** 初始化gpu设备
|
|
307
309
|
* @returns
|
|
308
310
|
*/
|
|
309
311
|
static async init() {
|
|
310
|
-
if (!(w &&
|
|
312
|
+
if (!(w && A)) {
|
|
311
313
|
if (typeof globalThis < "u" && typeof window > "u") {
|
|
312
314
|
const { create: e, globals: r } = await z("webgpu", !1);
|
|
313
315
|
Object.assign(globalThis, r), globalThis.navigator || (globalThis.navigator = {}), Object.assign(globalThis.navigator, { gpu: e([]) });
|
|
314
316
|
}
|
|
315
317
|
if (!navigator.gpu) throw new Error("该环境不支持webgpu");
|
|
316
318
|
if (w || (w = await navigator.gpu.requestAdapter({})), !w) throw new Error("获取适配器失败");
|
|
317
|
-
if (
|
|
319
|
+
if (A = await w.requestDevice(), !w) throw new Error("获取设备失败");
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
322
|
/** 注销gpu设备
|
|
321
323
|
*/
|
|
322
324
|
static destroy() {
|
|
323
|
-
|
|
325
|
+
A && A.destroy(), A = null;
|
|
324
326
|
}
|
|
325
327
|
/**
|
|
326
328
|
* @param data
|
|
327
329
|
*/
|
|
328
330
|
static buildBufferTypeByData(e) {
|
|
329
|
-
return Object.keys(e).reduce((
|
|
330
|
-
let t = e[
|
|
331
|
+
return Object.keys(e).reduce((s, u) => {
|
|
332
|
+
let t = e[u];
|
|
331
333
|
if (Array.isArray(t) && typeof t[0] == "number" && (t = new Float32Array()), Array.isArray(t))
|
|
332
334
|
if (typeof t[0] == "object" || t.length) {
|
|
333
335
|
const n = U(t[0]);
|
|
334
|
-
|
|
335
|
-
} else console.log(`字段:${
|
|
336
|
+
s[u] = n;
|
|
337
|
+
} else console.log(`字段:${u}, 不支持该值对应数据类型或数组为空`);
|
|
336
338
|
else if (ArrayBuffer.isView(t) && !(t instanceof DataView))
|
|
337
|
-
|
|
339
|
+
s[u] = t;
|
|
338
340
|
else if (typeof t == "object") {
|
|
339
341
|
const n = U(t);
|
|
340
|
-
|
|
341
|
-
} else console.log(`字段:${
|
|
342
|
-
return
|
|
342
|
+
s[u] = n.layout;
|
|
343
|
+
} else console.log(`字段:${u}, 不支持的数据类型`);
|
|
344
|
+
return s;
|
|
343
345
|
}, {});
|
|
344
346
|
}
|
|
345
347
|
/** 通过数据创建
|
|
@@ -347,8 +349,8 @@ ${f.join("")}
|
|
|
347
349
|
* @returns
|
|
348
350
|
*/
|
|
349
351
|
static async fromByData(e) {
|
|
350
|
-
let { data: r, ...
|
|
351
|
-
const
|
|
352
|
+
let { data: r, ...s } = e;
|
|
353
|
+
const u = this.buildBufferTypeByData(r), t = new T(u, s);
|
|
352
354
|
return await t.initPipeline(), t;
|
|
353
355
|
}
|
|
354
356
|
/** 快捷计算方法
|
|
@@ -356,12 +358,12 @@ ${f.join("")}
|
|
|
356
358
|
* @returns
|
|
357
359
|
*/
|
|
358
360
|
static async computed(e) {
|
|
359
|
-
let { data: r, map:
|
|
360
|
-
const l = await this.fromByData({ data: r, ...c }), d = l.createBindGroup(r),
|
|
361
|
-
return n && n({ gpuComputed: l, group: d, results:
|
|
361
|
+
let { data: r, map: s = !1, workgroupCount: u, synchronize: t, onSuccess: n, ...c } = e;
|
|
362
|
+
const l = await this.fromByData({ data: r, ...c }), d = l.createBindGroup(r), a = await l.computed(d, u, t);
|
|
363
|
+
return n && n({ gpuComputed: l, group: d, results: a }), s ? a.map((o, p) => l.dataMap(o, t[p])) : a;
|
|
362
364
|
}
|
|
363
365
|
}
|
|
364
|
-
const
|
|
366
|
+
const M = (
|
|
365
367
|
/* wgsl */
|
|
366
368
|
`
|
|
367
369
|
fn quat_rotate(q: vec4<f32>, v: vec3<f32>) -> vec3<f32> {
|
|
@@ -370,7 +372,7 @@ const j = (
|
|
|
370
372
|
return v + q.w * t + cross(q.xyz, t);
|
|
371
373
|
}
|
|
372
374
|
`
|
|
373
|
-
),
|
|
375
|
+
), O = (
|
|
374
376
|
/* wgsl */
|
|
375
377
|
`
|
|
376
378
|
fn point_in_obb(
|
|
@@ -390,13 +392,13 @@ const j = (
|
|
|
390
392
|
return all(abs(pLocal) <= halfSize);
|
|
391
393
|
}
|
|
392
394
|
`
|
|
393
|
-
),
|
|
395
|
+
), j = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
394
396
|
__proto__: null,
|
|
395
|
-
point_in_obb:
|
|
396
|
-
quat_rotate:
|
|
397
|
+
point_in_obb: O,
|
|
398
|
+
quat_rotate: M
|
|
397
399
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
398
400
|
export {
|
|
399
401
|
D as AtomicUint32Array,
|
|
400
402
|
T as GpuComputed,
|
|
401
|
-
|
|
403
|
+
j as WGSL_Fun
|
|
402
404
|
};
|