webgpu-computed 0.0.5 → 0.0.6
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 +3 -17
- package/README.zh-CN.md +3 -13
- package/package.json +4 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +119 -99
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ A simplified WebGPU computing library that encapsulates tedious initialization a
|
|
|
12
12
|
- 🔧 Support for complex data structures (vectors, matrices)
|
|
13
13
|
- ⚡ High-performance GPU computing
|
|
14
14
|
- 📚 Built-in common WGSL functions
|
|
15
|
+
- ✅ Support node
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
@@ -19,23 +20,6 @@ A simplified WebGPU computing library that encapsulates tedious initialization a
|
|
|
19
20
|
npm install webgpu-computed
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
## Node.js Environment Configuration
|
|
23
|
-
|
|
24
|
-
You can install the webgpu package
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npm install webgpu
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Then initialize the environment
|
|
31
|
-
|
|
32
|
-
```js
|
|
33
|
-
import { create, globals } from 'webgpu'
|
|
34
|
-
|
|
35
|
-
Object.assign(globalThis, globals)
|
|
36
|
-
const navigator = { gpu: create([]) }
|
|
37
|
-
```
|
|
38
|
-
|
|
39
23
|
## Quick Start
|
|
40
24
|
|
|
41
25
|
### 1. Initialize WebGPU
|
|
@@ -47,6 +31,8 @@ import { GpuComputed } from 'webgpu-computed';
|
|
|
47
31
|
|
|
48
32
|
// Initialize WebGPU
|
|
49
33
|
await GpuComputed.init();
|
|
34
|
+
//After using the node environment, please call:
|
|
35
|
+
//GpuComputed.destroy()
|
|
50
36
|
```
|
|
51
37
|
|
|
52
38
|
### 2. Perform Simple Computation
|
package/README.zh-CN.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
- 🔧 支持复杂数据结构(向量、矩阵)
|
|
10
10
|
- ⚡ 高性能 GPU 计算
|
|
11
11
|
- 📚 内置常用 WGSL 函数
|
|
12
|
+
- ✅ 支持Node
|
|
12
13
|
|
|
13
14
|
## 安装
|
|
14
15
|
|
|
@@ -16,19 +17,6 @@
|
|
|
16
17
|
npm install webgpu-computed
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
## node 环境配置
|
|
20
|
-
可安装webgpu包
|
|
21
|
-
```bash
|
|
22
|
-
npm install webgpu
|
|
23
|
-
```
|
|
24
|
-
然后初始化环境
|
|
25
|
-
```js
|
|
26
|
-
import { create, globals } from 'webgpu'
|
|
27
|
-
|
|
28
|
-
Object.assign(globalThis, globals)
|
|
29
|
-
const navigator = { gpu: create([]) }
|
|
30
|
-
```
|
|
31
|
-
|
|
32
20
|
## 快速开始
|
|
33
21
|
|
|
34
22
|
### 1. 初始化 WebGPU
|
|
@@ -40,6 +28,8 @@ import { GpuComputed } from 'webgpu-computed';
|
|
|
40
28
|
|
|
41
29
|
// 初始化 WebGPU
|
|
42
30
|
await GpuComputed.init();
|
|
31
|
+
// node 环境使用完请调用:
|
|
32
|
+
// GpuComputed.destroy()
|
|
43
33
|
```
|
|
44
34
|
|
|
45
35
|
### 2. 执行简单计算
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webgpu-computed",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "对webgpu的封装,处理了繁琐的前置工作,只关注wgsl本身逻辑",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"keywords": [
|
|
10
10
|
"webgpu", "computed", "web", "js"
|
|
11
11
|
],
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"webgpu": "^0.3.8"
|
|
14
|
+
},
|
|
12
15
|
"author": "夏过初秋",
|
|
13
16
|
"license": "ISC"
|
|
14
17
|
}
|
package/src/index.d.ts
CHANGED
package/src/index.js
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
async function P(v, r = !0) {
|
|
2
|
+
if (typeof global < "u" && typeof require < "u")
|
|
3
|
+
return require(v);
|
|
4
|
+
{
|
|
5
|
+
let e = await import(
|
|
6
|
+
/* @vite-ignore */
|
|
7
|
+
v
|
|
8
|
+
);
|
|
9
|
+
return r && (e = e.default), e;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const w = {
|
|
2
13
|
f32: 1,
|
|
3
14
|
vec2: 2,
|
|
4
15
|
vec3: 3,
|
|
5
16
|
vec4: 4,
|
|
6
17
|
mat3x3: 12,
|
|
7
18
|
mat4x4: 16
|
|
8
|
-
},
|
|
19
|
+
}, h = {
|
|
9
20
|
f32: 1,
|
|
10
21
|
vec2: 2,
|
|
11
22
|
vec3: 4,
|
|
@@ -13,33 +24,42 @@ const m = {
|
|
|
13
24
|
mat3x3: 4,
|
|
14
25
|
mat4x4: 4
|
|
15
26
|
};
|
|
16
|
-
function
|
|
17
|
-
const
|
|
18
|
-
return (
|
|
27
|
+
function B(v, r) {
|
|
28
|
+
const e = h[r];
|
|
29
|
+
return (e - v % e) % e;
|
|
19
30
|
}
|
|
20
|
-
let
|
|
21
|
-
class
|
|
31
|
+
let y = null, m = null;
|
|
32
|
+
class A {
|
|
22
33
|
constructor() {
|
|
23
34
|
}
|
|
24
35
|
static async init() {
|
|
25
|
-
if (!
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
if (!(y && m)) {
|
|
37
|
+
if (typeof globalThis < "u" && typeof window > "u") {
|
|
38
|
+
const { create: r, globals: e } = await P("webgpu", !1);
|
|
39
|
+
Object.assign(globalThis, e), globalThis.navigator || (globalThis.navigator = {}), Object.assign(globalThis.navigator, { gpu: r([]) });
|
|
40
|
+
}
|
|
41
|
+
if (!navigator.gpu) throw new Error("该环境不支持webgpu");
|
|
42
|
+
if (y || (y = await navigator.gpu.requestAdapter({})), !y) throw new Error("获取适配器失败");
|
|
43
|
+
if (m = await y.requestDevice(), !y) throw new Error("获取设备失败");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
static destroy() {
|
|
47
|
+
m && m.destroy(), m = null;
|
|
28
48
|
}
|
|
29
49
|
async getDevice() {
|
|
30
|
-
if (!
|
|
31
|
-
return { adapter:
|
|
50
|
+
if (!y || !m) throw new Error("webgpu未初始化或不可用");
|
|
51
|
+
return { adapter: y, device: m };
|
|
32
52
|
}
|
|
33
|
-
async createPipeline(
|
|
34
|
-
const { device:
|
|
35
|
-
const
|
|
36
|
-
size:
|
|
53
|
+
async createPipeline(r, e) {
|
|
54
|
+
const { device: o } = await this.getDevice(), a = Object.keys(e).map((f, s) => {
|
|
55
|
+
const i = e[f], u = new Float32Array(Array.isArray(i) ? i : i.buffer), t = o.createBuffer({
|
|
56
|
+
size: u.byteLength,
|
|
37
57
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE
|
|
38
58
|
});
|
|
39
|
-
return
|
|
59
|
+
return o.queue.writeBuffer(t, 0, u), {
|
|
40
60
|
name: f,
|
|
41
|
-
buffer:
|
|
42
|
-
float32Array:
|
|
61
|
+
buffer: t,
|
|
62
|
+
float32Array: u,
|
|
43
63
|
groupLayoutItem: {
|
|
44
64
|
binding: s,
|
|
45
65
|
// 绑定到组里的0号位插槽
|
|
@@ -51,134 +71,134 @@ class h {
|
|
|
51
71
|
},
|
|
52
72
|
groupItem: {
|
|
53
73
|
binding: s,
|
|
54
|
-
resource: { buffer:
|
|
74
|
+
resource: { buffer: t }
|
|
55
75
|
}
|
|
56
76
|
};
|
|
57
|
-
}), l =
|
|
58
|
-
entries:
|
|
59
|
-
}), g =
|
|
77
|
+
}), l = o.createBindGroupLayout({
|
|
78
|
+
entries: a.map((f) => f.groupLayoutItem)
|
|
79
|
+
}), g = o.createBindGroup({
|
|
60
80
|
layout: l,
|
|
61
|
-
entries:
|
|
81
|
+
entries: a.map((f) => f.groupItem)
|
|
62
82
|
});
|
|
63
|
-
return { pipeline:
|
|
64
|
-
layout:
|
|
83
|
+
return { pipeline: o.createComputePipeline({
|
|
84
|
+
layout: o.createPipelineLayout({
|
|
65
85
|
bindGroupLayouts: [l]
|
|
66
86
|
}),
|
|
67
87
|
compute: {
|
|
68
|
-
module:
|
|
88
|
+
module: o.createShaderModule({ code: r, label: "" }),
|
|
69
89
|
entryPoint: "main"
|
|
70
90
|
}
|
|
71
|
-
}), group: g, device:
|
|
91
|
+
}), group: g, device: o, bufferInfoList: a };
|
|
72
92
|
}
|
|
73
|
-
buildCode(
|
|
93
|
+
buildCode(r, e, o) {
|
|
74
94
|
const {
|
|
75
|
-
workgroupSize:
|
|
95
|
+
workgroupSize: a = [32, 1, 1],
|
|
76
96
|
globalInvocationIdName: l = "grid",
|
|
77
97
|
workgroupIndexName: g = "index"
|
|
78
|
-
} =
|
|
79
|
-
const p =
|
|
80
|
-
return Array.isArray(p) ? void 0 : `struct ${
|
|
81
|
-
}).filter((
|
|
82
|
-
`), s = Object.keys(
|
|
83
|
-
const
|
|
84
|
-
return Array.isArray(
|
|
98
|
+
} = o ?? {}, d = (t) => t && t[0].toUpperCase() + t.slice(1), f = Object.keys(r).map((t) => {
|
|
99
|
+
const p = r[t];
|
|
100
|
+
return Array.isArray(p) ? void 0 : `struct ${d(t)}Struct { ${p.layout.map((n) => `${n.name}: ${n.type === "f32" ? "f32" : `${n.type}<f32>`}`).join(",")} };`;
|
|
101
|
+
}).filter((t) => !!t).join(`
|
|
102
|
+
`), s = Object.keys(r).map((t, p) => {
|
|
103
|
+
const c = r[t], n = "read_write";
|
|
104
|
+
return Array.isArray(c) ? `@group(0) @binding(${p}) var<storage, ${n}> ${t}: array<f32>;` : `@group(0) @binding(${p}) var<storage, ${n}> ${t}: array<${d(t)}Struct>;`;
|
|
85
105
|
}).join(`
|
|
86
|
-
`),
|
|
106
|
+
`), i = l;
|
|
87
107
|
return (
|
|
88
108
|
/*wgsl*/
|
|
89
109
|
`
|
|
90
110
|
${f}
|
|
91
111
|
${s}
|
|
92
112
|
|
|
93
|
-
${
|
|
113
|
+
${o?.beforeCodes?.join(" ") ?? ""}
|
|
94
114
|
|
|
95
|
-
@compute @workgroup_size(${
|
|
96
|
-
fn main(@builtin(global_invocation_id) ${
|
|
97
|
-
var ${g} = ${
|
|
98
|
-
${
|
|
115
|
+
@compute @workgroup_size(${a.join(",")})
|
|
116
|
+
fn main(@builtin(global_invocation_id) ${i}: vec3<u32>) {
|
|
117
|
+
var ${g} = ${i}.x;
|
|
118
|
+
${e}
|
|
99
119
|
}
|
|
100
120
|
`
|
|
101
121
|
);
|
|
102
122
|
}
|
|
103
|
-
buildBuffer(
|
|
104
|
-
if (!Array.isArray(
|
|
123
|
+
buildBuffer(r, e, o) {
|
|
124
|
+
if (!Array.isArray(r) || r.length === 0)
|
|
105
125
|
throw new Error("数据必须是非空数组");
|
|
106
|
-
if (
|
|
107
|
-
const
|
|
108
|
-
if (Array.isArray(
|
|
109
|
-
for (const
|
|
110
|
-
if (
|
|
111
|
-
throw new Error(`${s} 不支持的数组长度 ${
|
|
126
|
+
if (e || (e = Object.keys(r[0])), o || (o = e.map((s) => {
|
|
127
|
+
const i = r[0][s];
|
|
128
|
+
if (Array.isArray(i)) {
|
|
129
|
+
for (const u of ["vec2", "vec3", "vec4", "mat3x3", "mat4x4"])
|
|
130
|
+
if (w[u] === i.length) return u;
|
|
131
|
+
throw new Error(`${s} 不支持的数组长度 ${i.length}`);
|
|
112
132
|
}
|
|
113
|
-
if (typeof
|
|
133
|
+
if (typeof i == "number") return "f32";
|
|
114
134
|
throw new Error(`${s} 不支持的类型`);
|
|
115
|
-
})),
|
|
116
|
-
let
|
|
117
|
-
const l =
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
const
|
|
135
|
+
})), e.length !== o.length) throw new Error("keys 与 types 长度不一致");
|
|
136
|
+
let a = 0;
|
|
137
|
+
const l = e.map((s, i) => {
|
|
138
|
+
const u = o[i];
|
|
139
|
+
a += B(a, u);
|
|
140
|
+
const t = {
|
|
121
141
|
name: s,
|
|
122
|
-
type:
|
|
123
|
-
offset:
|
|
124
|
-
size:
|
|
142
|
+
type: u,
|
|
143
|
+
offset: a,
|
|
144
|
+
size: w[u]
|
|
125
145
|
};
|
|
126
|
-
return
|
|
127
|
-
}), g = Math.max(...
|
|
128
|
-
return
|
|
129
|
-
const
|
|
130
|
-
l.forEach(({ name:
|
|
131
|
-
let
|
|
132
|
-
Array.isArray(
|
|
133
|
-
for (let
|
|
134
|
-
f[
|
|
146
|
+
return a += w[u], t;
|
|
147
|
+
}), g = Math.max(...o.map((s) => h[s])), d = a + (g - a % g) % g, f = new Array(d * r.length).fill(0);
|
|
148
|
+
return r.forEach((s, i) => {
|
|
149
|
+
const u = i * d;
|
|
150
|
+
l.forEach(({ name: t, offset: p, size: c }) => {
|
|
151
|
+
let n = s[t];
|
|
152
|
+
Array.isArray(n) || (n = [n]);
|
|
153
|
+
for (let b = 0; b < c; b++)
|
|
154
|
+
f[u + p + b] = Number(n[b] ?? 0);
|
|
135
155
|
});
|
|
136
156
|
}), {
|
|
137
157
|
buffer: f,
|
|
138
|
-
stride:
|
|
158
|
+
stride: d,
|
|
139
159
|
layout: l,
|
|
140
|
-
count:
|
|
160
|
+
count: r.length
|
|
141
161
|
};
|
|
142
162
|
}
|
|
143
|
-
async computed(
|
|
144
|
-
let { code:
|
|
145
|
-
const l = Object.keys(
|
|
146
|
-
const
|
|
147
|
-
if (typeof
|
|
163
|
+
async computed(r) {
|
|
164
|
+
let { code: e, data: o, ...a } = r;
|
|
165
|
+
const l = Object.keys(o).reduce((c, n) => {
|
|
166
|
+
const b = o[n];
|
|
167
|
+
if (typeof b[0] == "number") c[n] = b;
|
|
148
168
|
else {
|
|
149
|
-
const $ = this.buildBuffer(
|
|
150
|
-
|
|
169
|
+
const $ = this.buildBuffer(b);
|
|
170
|
+
c[n] = $;
|
|
151
171
|
}
|
|
152
|
-
return
|
|
172
|
+
return c;
|
|
153
173
|
}, {});
|
|
154
|
-
|
|
155
|
-
const { pipeline: g, group:
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
if (
|
|
159
|
-
const
|
|
160
|
-
size:
|
|
174
|
+
e = this.buildCode(l, e, a);
|
|
175
|
+
const { pipeline: g, group: d, device: f, bufferInfoList: s } = await this.createPipeline(e, l), i = f.createCommandEncoder(), u = i.beginComputePass();
|
|
176
|
+
u.setPipeline(g), u.setBindGroup(0, d), u.dispatchWorkgroups(a.workgroupCount[0], a.workgroupCount[1], a.workgroupCount[2]), u.end();
|
|
177
|
+
const t = s?.map((c) => {
|
|
178
|
+
if (a.synchronize?.includes(c.name)) {
|
|
179
|
+
const n = f.createBuffer({
|
|
180
|
+
size: c.float32Array.byteLength,
|
|
161
181
|
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
|
|
162
182
|
});
|
|
163
|
-
return
|
|
183
|
+
return i.copyBufferToBuffer(c.buffer, 0, n, 0, n.size), { buffer: n, name: c.name };
|
|
164
184
|
}
|
|
165
|
-
}).filter((
|
|
166
|
-
f.queue.submit([
|
|
185
|
+
}).filter((c) => !!c);
|
|
186
|
+
f.queue.submit([i.finish()]), await f.queue.onSubmittedWorkDone();
|
|
167
187
|
const p = await Promise.all(
|
|
168
|
-
|
|
169
|
-
await
|
|
170
|
-
const
|
|
171
|
-
return [...new Float32Array(
|
|
188
|
+
t.map(async (c) => {
|
|
189
|
+
await c.buffer.mapAsync(GPUMapMode.READ);
|
|
190
|
+
const n = c.buffer.getMappedRange();
|
|
191
|
+
return [...new Float32Array(n)];
|
|
172
192
|
})
|
|
173
193
|
);
|
|
174
|
-
return
|
|
194
|
+
return a?.onSuccess && a.onSuccess({ code: e, bufferInfoList: s, results: p }), p;
|
|
175
195
|
}
|
|
176
196
|
static instance;
|
|
177
197
|
static get computed() {
|
|
178
|
-
return this.instance || (this.instance = new
|
|
198
|
+
return this.instance || (this.instance = new A()), this.instance.computed.bind(this.instance);
|
|
179
199
|
}
|
|
180
200
|
}
|
|
181
|
-
const
|
|
201
|
+
const q = Object.freeze({
|
|
182
202
|
quat_rotate: (
|
|
183
203
|
/* wgsl */
|
|
184
204
|
`
|
|
@@ -212,6 +232,6 @@ const B = Object.freeze({
|
|
|
212
232
|
)
|
|
213
233
|
});
|
|
214
234
|
export {
|
|
215
|
-
|
|
216
|
-
|
|
235
|
+
A as GpuComputed,
|
|
236
|
+
q as WGSL_Fun
|
|
217
237
|
};
|