webgpu-computed 0.0.1

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 ADDED
@@ -0,0 +1,380 @@
1
+ # webgpu-computed
2
+
3
+ 一个简化的 WebGPU 计算库,封装了繁琐的初始化和缓冲区管理,让开发者专注于 WGSL 着色器逻辑。
4
+
5
+ ## 特性
6
+
7
+ - 🚀 简化 WebGPU 初始化
8
+ - 📦 自动缓冲区管理和布局计算
9
+ - 🔧 支持复杂数据结构(向量、矩阵)
10
+ - ⚡ 高性能 GPU 计算
11
+ - 📚 内置常用 WGSL 函数
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ npm install webgpu-computed
17
+ ```
18
+
19
+ ## 快速开始
20
+
21
+ ### 1. 初始化 WebGPU
22
+
23
+ 在使用任何计算功能前,需要先初始化 WebGPU 环境:
24
+
25
+ ```javascript
26
+ import { GpuComputed } from 'webgpu-computed';
27
+
28
+ // 初始化 WebGPU
29
+ await GpuComputed.init();
30
+ ```
31
+
32
+ ### 2. 执行简单计算
33
+
34
+ 以下是一个简单的向量加法示例:
35
+
36
+ ```javascript
37
+ import { GpuComputed } from 'webgpu-computed';
38
+
39
+ // 准备数据
40
+ const data = {
41
+ inputA: [1.0, 2.0, 3.0, 4.0],
42
+ inputB: [0.5, 1.5, 2.5, 3.5],
43
+ output: new Array(4).fill(0) // 输出缓冲区
44
+ };
45
+
46
+ // WGSL 计算代码
47
+ const code = `
48
+ output[index] = inputA[index] + inputB[index];
49
+ `;
50
+
51
+ // 执行计算
52
+ const results = await GpuComputed.computed({
53
+ code,
54
+ data,
55
+ workgroupCount: [1] // 工作组数量
56
+ });
57
+
58
+ console.log(results); // [[1.5, 3.5, 5.5, 7.5]]
59
+ ```
60
+
61
+ ### 3. 使用复杂数据结构
62
+
63
+ 库支持向量和矩阵类型:
64
+
65
+ ```javascript
66
+ const data = {
67
+ positions: [
68
+ { pos: [1.0, 2.0, 3.0], vel: [0.1, 0.2, 0.3] },
69
+ { pos: [4.0, 5.0, 6.0], vel: [0.4, 0.5, 0.6] }
70
+ ],
71
+ output: new Array(2).fill({ pos: [0,0,0], vel: [0,0,0] })
72
+ };
73
+
74
+ const code = `
75
+ output[index].pos = positions[index].pos + positions[index].vel;
76
+ output[index].vel = positions[index].vel * 2.0;
77
+ `;
78
+
79
+ const results = await GpuComputed.computed({
80
+ code,
81
+ data,
82
+ workgroupCount: [1]
83
+ });
84
+ ```
85
+
86
+ ## API 参考
87
+
88
+ ### GpuComputed 类
89
+
90
+ #### 静态方法
91
+
92
+ ##### `GpuComputed.init()`
93
+
94
+ 初始化 WebGPU 环境。必须在使用其他功能前调用。
95
+
96
+ **返回值**: `Promise<void>`
97
+
98
+ **抛出**: 如果浏览器不支持 WebGPU 或获取适配器/设备失败
99
+
100
+ ##### `GpuComputed.computed(options)`
101
+
102
+ 执行 GPU 计算任务。
103
+
104
+ **参数**:
105
+
106
+ - `code` (string): WGSL 计算代码
107
+ - `data` (object): 输入/输出数据对象
108
+ - `workgroupCount` (array): 工作组数量 [x, y?, z?]
109
+ - `workgroupSize` (array, 可选): 工作组大小,默认 [32, 1, 1]
110
+ - `globalInvocationIdName` (string, 可选): 全局调用 ID 变量名,默认 "grid"
111
+ - `workgroupIndexName` (string, 可选): 工作组索引变量名,默认 "index"
112
+ - `synchronize` (array, 可选): 需要同步回 CPU 的缓冲区名称数组
113
+ - `beforeCodes` (array, 可选): 计算函数前的 WGSL 代码片段
114
+ - `onSuccess` (function, 可选): 计算成功回调函数
115
+
116
+ **返回值**: `Promise<Array<Float32Array>>` - 同步缓冲区的数据
117
+
118
+ ### 数据类型
119
+
120
+ 支持以下 WGSL 类型:
121
+
122
+ - `f32`: 单精度浮点数
123
+ - `vec2`: 二维向量
124
+ - `vec3`: 三维向量
125
+ - `vec4`: 四维向量
126
+ - `mat3x3`: 3x3 矩阵
127
+ - `mat4x4`: 4x4 矩阵
128
+
129
+ ### 内置 WGSL 函数
130
+
131
+ 库提供了一些常用的 WGSL 辅助函数:
132
+
133
+ #### 四元数旋转
134
+
135
+ ```wgsl
136
+ fn quat_rotate(q: vec4<f32>, v: vec3<f32>) -> vec3<f32>
137
+ ```
138
+
139
+ 使用示例:
140
+
141
+ ```javascript
142
+ import { WGSL_Fun } from 'webgpu-computed';
143
+
144
+ await GpuComputed.computed({
145
+ code: "",
146
+ data: {....},
147
+ beforeCodes:[WGSL_Fun.quat_rotate]
148
+ })
149
+
150
+ ```
151
+
152
+ #### 点在 OBB 中的检测
153
+
154
+ ```wgsl
155
+ fn point_in_obb(point: vec3<f32>, center: vec3<f32>, halfSize: vec3<f32>, quat: vec4<f32>) -> bool
156
+ ```
157
+
158
+ ## 高级用法
159
+
160
+ ### 自定义工作组配置
161
+
162
+ ```javascript
163
+ await GpuComputed.computed({
164
+ code: '...',
165
+ data: {...},
166
+ workgroupCount: [4, 4], // 16 个工作组
167
+ workgroupSize: [16, 16], // 每个工作组 256 个线程
168
+ });
169
+ ```
170
+
171
+ ### 同步数据回 CPU
172
+
173
+ ```javascript
174
+ const results = await GpuComputed.computed({
175
+ code: '...',
176
+ data: {...},
177
+ synchronize: ['output'], // 指定需要同步的缓冲区
178
+ workgroupCount: [1]
179
+ });
180
+
181
+ // results 包含同步回的数据
182
+ ```
183
+
184
+ ### 回调函数
185
+
186
+ ```javascript
187
+ await GpuComputed.computed({
188
+ code: '...',
189
+ data: {...},
190
+ workgroupCount: [1],
191
+ onSuccess: ({ code, bufferInfoList, results }) => {
192
+ console.log('计算完成', results);
193
+ }
194
+ });
195
+ ```
196
+
197
+ ## 浏览器支持
198
+
199
+ - Chrome 113+
200
+ - Edge 113+
201
+ - Firefox (部分支持)
202
+ - Safari (部分支持)
203
+
204
+ 确保浏览器支持 WebGPU API。
205
+
206
+ ## 示例项目
207
+
208
+ ```js
209
+ import { GpuComputed } from "webgpu-computed"
210
+
211
+ // 1. 初始化 WebGPU
212
+ console.log('初始化 WebGPU...');
213
+ await GpuComputed.init();
214
+ console.log('WebGPU 初始化成功');
215
+
216
+ // 2. 简单数组计算示例
217
+ console.log('\n=== 简单数组计算 ===');
218
+ const simpleData = {
219
+ inputA: [1.0, 2.0, 3.0, 4.0],
220
+ inputB: [0.5, 1.5, 2.5, 3.5],
221
+ output: new Array(4).fill(0)
222
+ };
223
+
224
+ const simpleCode = `
225
+ output[index] = inputA[index] + inputB[index];
226
+ `;
227
+
228
+ const simpleResults = await GpuComputed.computed({
229
+ code: simpleCode,
230
+ data: simpleData,
231
+ workgroupCount: [1],
232
+ synchronize: ['output']
233
+ });
234
+
235
+ console.log('简单计算结果:', simpleResults[0]); // [1.5, 3.5, 5.5, 7.5]
236
+
237
+ // 3. 复杂数据结构示例(结构体)
238
+ console.log('\n=== 复杂数据结构计算 ===');
239
+ const complexData = {
240
+ particles: [
241
+ { position: [1.0, 2.0, 3.0], velocity: [0.1, 0.2, 0.3], mass: 1.0 },
242
+ { position: [4.0, 5.0, 6.0], velocity: [0.4, 0.5, 0.6], mass: 2.0 }
243
+ ],
244
+ output: [
245
+ { position: [0, 0, 0], velocity: [0, 0, 0], mass: 0 },
246
+ { position: [0, 0, 0], velocity: [0, 0, 0], mass: 0 }
247
+ ]
248
+ };
249
+
250
+ const complexCode = `
251
+ output[index].position = particles[index].position + particles[index].velocity;
252
+ output[index].velocity = particles[index].velocity * 2.0;
253
+ output[index].mass = particles[index].mass * 1.5;
254
+ `;
255
+
256
+ const complexResults = await GpuComputed.computed({
257
+ code: complexCode,
258
+ data: complexData,
259
+ workgroupCount: [1],
260
+ synchronize: ['output']
261
+ });
262
+
263
+ console.log('复杂计算结果:', complexResults[0]);
264
+
265
+ // 4. 使用内置 WGSL 函数示例
266
+ console.log('\n=== 使用内置 WGSL 函数 ===');
267
+ const wgslFunData = {
268
+ points: [
269
+ {
270
+ x: 1.0, y: 0.0, z: 0.0
271
+ },
272
+ {
273
+ x: 0.0, y: 1.0, z: 0.0
274
+ },
275
+ {
276
+ x: -1.0, y: 0.0, z: 0.0
277
+ }
278
+ ],
279
+ obbCenter: [0.0, 0.0, 0.0],
280
+ obbHalfSize: [2.0, 2.0, 2.0],
281
+ obbRotation: [0.0, 0.0, 0.0, 1.0], // 单位四元数,无旋转
282
+ results: new Array(3).fill(0)
283
+ };
284
+
285
+ const wgslFunCode = `
286
+ let point = vec3(points[index].x, points[index].y, points[index].z);
287
+ let center = vec3<f32>(obbCenter[0], obbCenter[1], obbCenter[2]);
288
+ let halfSize = vec3<f32>(obbHalfSize[0], obbHalfSize[1], obbHalfSize[2]);
289
+ let quat = vec4<f32>(obbRotation[0], obbRotation[1], obbRotation[2], obbRotation[3]);
290
+
291
+ if (point_in_obb(point, center, halfSize, quat)) {
292
+ results[index] = 1.0;
293
+ } else {
294
+ results[index] = 0.0;
295
+ }
296
+ `;
297
+
298
+ const wgslFunResults = await GpuComputed.computed({
299
+ code: wgslFunCode,
300
+ data: wgslFunData,
301
+ workgroupCount: [1],
302
+ beforeCodes: [WGSL_Fun.quat_rotate, WGSL_Fun.point_in_obb, /** 可添加自己的函数代码 */],
303
+ synchronize: ['results']
304
+ });
305
+
306
+ console.log('OBB 检测结果:', wgslFunResults[0]); // [1, 1, 1] 所有点都在 OBB 内
307
+
308
+ // 5. 自定义工作组配置示例
309
+ console.log('\n=== 自定义工作组配置 ===');
310
+ const largeData = {
311
+ largeArray: new Array(1024).fill(0).map((_, i) => i * 1.0),
312
+ output: new Array(1024).fill(0)
313
+ };
314
+
315
+ const largeCode = `
316
+ output[index] = largeArray[index] * 2.0;
317
+ `;
318
+
319
+ const largeResults = await GpuComputed.computed({
320
+ code: largeCode,
321
+ data: largeData,
322
+ workgroupCount: [32], // 32 个工作组
323
+ workgroupSize: [32, 1, 1], // 每个工作组 32 个线程,总共 1024 个线程
324
+ synchronize: ['output']
325
+ });
326
+
327
+ console.log('大数组计算结果 (前10个):', largeResults[0].slice(0, 10));
328
+
329
+ // 6. 使用回调函数示例
330
+ console.log('\n=== 使用回调函数 ===');
331
+ const callbackData = {
332
+ values: [10.0, 20.0, 30.0],
333
+ squares: new Array(3).fill(0)
334
+ };
335
+
336
+ const callbackCode = `
337
+ squares[index] = values[index] * values[index];
338
+ `;
339
+
340
+ await GpuComputed.computed({
341
+ code: callbackCode,
342
+ data: callbackData,
343
+ workgroupCount: [1],
344
+ synchronize: ['squares'],
345
+ onSuccess: ({ code, bufferInfoList, results }) => {
346
+ console.log('回调函数触发,计算平方结果:', results[0]); // [100, 400, 900]
347
+ }
348
+ });
349
+
350
+ // 7. 多维工作组示例
351
+ console.log('\n=== 多维工作组 ===');
352
+ const matrixData = {
353
+ matrixA: new Array(16).fill(0).map((_, i) => i * 1.0),
354
+ matrixB: new Array(16).fill(0).map((_, i) => (i + 1) * 1.0),
355
+ result: new Array(16).fill(0)
356
+ };
357
+
358
+ const matrixCode = `
359
+ let x = index % 4u;
360
+ let y = index / 4u;
361
+ let idx = y * 4u + x;
362
+ result[idx] = matrixA[idx] + matrixB[idx];
363
+ `;
364
+
365
+ const matrixResults = await GpuComputed.computed({
366
+ code: matrixCode,
367
+ data: matrixData,
368
+ workgroupCount: [4, 4], // 4x4 工作组网格
369
+ workgroupSize: [1, 1, 1], // 每个工作组 1 个线程
370
+ synchronize: ['result']
371
+ });
372
+
373
+ console.log('矩阵计算结果:', matrixResults[0]);
374
+
375
+ console.log('\n所有功能示例运行完成!');
376
+ ```
377
+
378
+ ## 许可证
379
+
380
+ ISC License
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "webgpu-computed",
3
+ "version": "0.0.1",
4
+ "description": "对webgpu的封装,处理了繁琐的前置工作,只关注wgsl本身逻辑",
5
+ "main": "./src/index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [
10
+ "webgpu"
11
+ ],
12
+ "author": "夏过初秋",
13
+ "license": "ISC"
14
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,76 @@
1
+ declare type BufferType = {
2
+ buffer: number[];
3
+ stride: number;
4
+ layout: {
5
+ name: string;
6
+ type: ValueType;
7
+ offset: number;
8
+ size: number;
9
+ }[];
10
+ count: number;
11
+ };
12
+
13
+ export declare class GpuComputed {
14
+ constructor();
15
+ static init(): Promise<void>;
16
+ getDevice(): Promise<{
17
+ adapter: GPUAdapter;
18
+ device: GPUDevice;
19
+ }>;
20
+ createPipeline(code: string, buffers: Record<string, BufferType | number[]>): Promise<{
21
+ pipeline: GPUComputePipeline;
22
+ group: GPUBindGroup;
23
+ device: GPUDevice;
24
+ bufferInfoList: {
25
+ name: string;
26
+ buffer: GPUBuffer;
27
+ float32Array: Float32Array<ArrayBuffer>;
28
+ groupLayoutItem: GPUBindGroupLayoutEntry;
29
+ groupItem: {
30
+ binding: number;
31
+ resource: {
32
+ buffer: GPUBuffer;
33
+ };
34
+ };
35
+ }[];
36
+ }>;
37
+ private buildCode;
38
+ private buildBuffer;
39
+ computed(opt: {
40
+ code: string;
41
+ data: Record<string, any[]>;
42
+ onSuccess?: (opt: {
43
+ code: string;
44
+ bufferInfoList: any[];
45
+ results: any;
46
+ }) => void;
47
+ } & OptionType): Promise<number[][]>;
48
+ static instance: GpuComputed;
49
+ static get computed(): (opt: {
50
+ code: string;
51
+ data: Record<string, any[]>;
52
+ onSuccess?: (opt: {
53
+ code: string;
54
+ bufferInfoList: any[];
55
+ results: any;
56
+ }) => void;
57
+ } & OptionType) => Promise<number[][]>;
58
+ }
59
+
60
+ declare type OptionType = {
61
+ workgroupSize?: [number, number, number];
62
+ workgroupCount: [number, number?, number?];
63
+ globalInvocationIdName?: string;
64
+ workgroupIndexName?: string;
65
+ synchronize?: string[];
66
+ beforeCodes?: string[];
67
+ };
68
+
69
+ declare type ValueType = "f32" | "vec2" | "vec3" | "vec4" | "mat3x3" | "mat4x4";
70
+
71
+ export declare const WGSL_Fun: Readonly<{
72
+ quat_rotate: "\n fn quat_rotate(q: vec4<f32>, v: vec3<f32>) -> vec3<f32> {\n // q.xyz = vector part, q.w = scalar part\n let t = 2.0 * cross(q.xyz, v);\n return v + q.w * t + cross(q.xyz, t);\n }\n ";
73
+ point_in_obb: "\n fn point_in_obb(\n point: vec3<f32>,\n center: vec3<f32>,\n halfSize: vec3<f32>,\n quat: vec4<f32>\n ) -> bool {\n // 世界空间 → OBB 局部空间\n let local = point - center;\n\n // 逆旋转(共轭四元数)\n let invQuat = vec4<f32>(-quat.xyz, quat.w);\n let pLocal = quat_rotate(invQuat, local);\n\n // AABB 判断\n return all(abs(pLocal) <= halfSize);\n }\n ";
74
+ }>;
75
+
76
+ export { }
package/src/index.js ADDED
@@ -0,0 +1,217 @@
1
+ const m = {
2
+ f32: 1,
3
+ vec2: 2,
4
+ vec3: 3,
5
+ vec4: 4,
6
+ mat3x3: 12,
7
+ mat4x4: 16
8
+ }, w = {
9
+ f32: 1,
10
+ vec2: 2,
11
+ vec3: 4,
12
+ vec4: 4,
13
+ mat3x3: 4,
14
+ mat4x4: 4
15
+ };
16
+ function P(A, c) {
17
+ const o = w[c];
18
+ return (o - A % o) % o;
19
+ }
20
+ let b = null, v = null;
21
+ class h {
22
+ constructor() {
23
+ }
24
+ static async init() {
25
+ if (!navigator.gpu) throw new Error("该环境不支持webgpu");
26
+ if (b = await navigator.gpu.requestAdapter({}), !b) throw new Error("获取适配器失败");
27
+ if (v = await b.requestDevice(), !b) throw new Error("获取设备失败");
28
+ }
29
+ async getDevice() {
30
+ if (!b || !v) throw new Error("webgpu未初始化或不可用");
31
+ return { adapter: b, device: v };
32
+ }
33
+ async createPipeline(c, o) {
34
+ const { device: r } = await this.getDevice(), n = Object.keys(o).map((f, s) => {
35
+ const a = o[f], i = new Float32Array(Array.isArray(a) ? a : a.buffer), e = r.createBuffer({
36
+ size: i.byteLength,
37
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE
38
+ });
39
+ return r.queue.writeBuffer(e, 0, i), {
40
+ name: f,
41
+ buffer: e,
42
+ float32Array: i,
43
+ groupLayoutItem: {
44
+ binding: s,
45
+ // 绑定到组里的0号位插槽
46
+ visibility: GPUShaderStage.COMPUTE,
47
+ // 数据在哪些阶段可以使用, 计算着色器、片元着色器、顶点着色器
48
+ buffer: {
49
+ type: "storage"
50
+ }
51
+ },
52
+ groupItem: {
53
+ binding: s,
54
+ resource: { buffer: e }
55
+ }
56
+ };
57
+ }), l = r.createBindGroupLayout({
58
+ entries: n.map((f) => f.groupLayoutItem)
59
+ }), g = r.createBindGroup({
60
+ layout: l,
61
+ entries: n.map((f) => f.groupItem)
62
+ });
63
+ return { pipeline: r.createComputePipeline({
64
+ layout: r.createPipelineLayout({
65
+ bindGroupLayouts: [l]
66
+ }),
67
+ compute: {
68
+ module: r.createShaderModule({ code: c, label: "" }),
69
+ entryPoint: "main"
70
+ }
71
+ }), group: g, device: r, bufferInfoList: n };
72
+ }
73
+ buildCode(c, o, r) {
74
+ const {
75
+ workgroupSize: n = [32, 1, 1],
76
+ globalInvocationIdName: l = "grid",
77
+ workgroupIndexName: g = "index"
78
+ } = r ?? {}, y = (e) => e && e[0].toUpperCase() + e.slice(1), f = Object.keys(c).map((e) => {
79
+ const p = c[e];
80
+ return Array.isArray(p) ? void 0 : `struct ${y(e)}Struct { ${p.layout.map((t) => `${t.name}: ${t.type === "f32" ? "f32" : `${t.type}<f32>`}`).join(",")} };`;
81
+ }).filter((e) => !!e).join(`
82
+ `), s = Object.keys(c).map((e, p) => {
83
+ const u = c[e], t = "read_write";
84
+ return Array.isArray(u) ? `@group(0) @binding(${p}) var<storage, ${t}> ${e}: array<f32>;` : `@group(0) @binding(${p}) var<storage, ${t}> ${e}: array<${y(e)}Struct>;`;
85
+ }).join(`
86
+ `), a = l;
87
+ return (
88
+ /*wgsl*/
89
+ `
90
+ ${f}
91
+ ${s}
92
+
93
+ ${r?.beforeCodes?.join(" ") ?? ""}
94
+
95
+ @compute @workgroup_size(${n.join(",")})
96
+ fn main(@builtin(global_invocation_id) ${a}: vec3<u32>) {
97
+ var ${g} = ${a}.x;
98
+ ${o}
99
+ }
100
+ `
101
+ );
102
+ }
103
+ buildBuffer(c, o, r) {
104
+ if (!Array.isArray(c) || c.length === 0)
105
+ throw new Error("数据必须是非空数组");
106
+ if (o || (o = Object.keys(c[0])), r || (r = o.map((s) => {
107
+ const a = c[0][s];
108
+ if (Array.isArray(a)) {
109
+ for (const i of ["vec2", "vec3", "vec4", "mat3x3", "mat4x4"])
110
+ if (m[i] === a.length) return i;
111
+ throw new Error(`${s} 不支持的数组长度 ${a.length}`);
112
+ }
113
+ if (typeof a == "number") return "f32";
114
+ throw new Error(`${s} 不支持的类型`);
115
+ })), o.length !== r.length) throw new Error("keys 与 types 长度不一致");
116
+ let n = 0;
117
+ const l = o.map((s, a) => {
118
+ const i = r[a];
119
+ n += P(n, i);
120
+ const e = {
121
+ name: s,
122
+ type: i,
123
+ offset: n,
124
+ size: m[i]
125
+ };
126
+ return n += m[i], e;
127
+ }), g = Math.max(...r.map((s) => w[s])), y = n + (g - n % g) % g, f = new Array(y * c.length).fill(0);
128
+ return c.forEach((s, a) => {
129
+ const i = a * y;
130
+ l.forEach(({ name: e, offset: p, size: u }) => {
131
+ let t = s[e];
132
+ Array.isArray(t) || (t = [t]);
133
+ for (let d = 0; d < u; d++)
134
+ f[i + p + d] = Number(t[d] ?? 0);
135
+ });
136
+ }), {
137
+ buffer: f,
138
+ stride: y,
139
+ layout: l,
140
+ count: c.length
141
+ };
142
+ }
143
+ async computed(c) {
144
+ let { code: o, data: r, ...n } = c;
145
+ const l = Object.keys(r).reduce((u, t) => {
146
+ const d = r[t];
147
+ if (typeof d[0] == "number") u[t] = d;
148
+ else {
149
+ const $ = this.buildBuffer(d);
150
+ u[t] = $;
151
+ }
152
+ return u;
153
+ }, {});
154
+ o = this.buildCode(l, o, n);
155
+ const { pipeline: g, group: y, device: f, bufferInfoList: s } = await this.createPipeline(o, l), a = f.createCommandEncoder(), i = a.beginComputePass();
156
+ i.setPipeline(g), i.setBindGroup(0, y), i.dispatchWorkgroups(n.workgroupCount[0], n.workgroupCount[1], n.workgroupCount[2]), i.end();
157
+ const e = s?.map((u) => {
158
+ if (n.synchronize?.includes(u.name)) {
159
+ const t = f.createBuffer({
160
+ size: u.float32Array.byteLength,
161
+ usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
162
+ });
163
+ return a.copyBufferToBuffer(u.buffer, 0, t, 0, t.size), { buffer: t, name: u.name };
164
+ }
165
+ }).filter((u) => !!u);
166
+ f.queue.submit([a.finish()]), await f.queue.onSubmittedWorkDone();
167
+ const p = await Promise.all(
168
+ e.map(async (u) => {
169
+ await u.buffer.mapAsync(GPUMapMode.READ);
170
+ const t = u.buffer.getMappedRange();
171
+ return [...new Float32Array(t)];
172
+ })
173
+ );
174
+ return n?.onSuccess && n.onSuccess({ code: o, bufferInfoList: s, results: p }), p;
175
+ }
176
+ static instance;
177
+ static get computed() {
178
+ return this.instance || (this.instance = new h()), this.instance.computed.bind(this.instance);
179
+ }
180
+ }
181
+ const B = Object.freeze({
182
+ quat_rotate: (
183
+ /* wgsl */
184
+ `
185
+ fn quat_rotate(q: vec4<f32>, v: vec3<f32>) -> vec3<f32> {
186
+ // q.xyz = vector part, q.w = scalar part
187
+ let t = 2.0 * cross(q.xyz, v);
188
+ return v + q.w * t + cross(q.xyz, t);
189
+ }
190
+ `
191
+ ),
192
+ point_in_obb: (
193
+ /* wgsl */
194
+ `
195
+ fn point_in_obb(
196
+ point: vec3<f32>,
197
+ center: vec3<f32>,
198
+ halfSize: vec3<f32>,
199
+ quat: vec4<f32>
200
+ ) -> bool {
201
+ // 世界空间 → OBB 局部空间
202
+ let local = point - center;
203
+
204
+ // 逆旋转(共轭四元数)
205
+ let invQuat = vec4<f32>(-quat.xyz, quat.w);
206
+ let pLocal = quat_rotate(invQuat, local);
207
+
208
+ // AABB 判断
209
+ return all(abs(pLocal) <= halfSize);
210
+ }
211
+ `
212
+ )
213
+ });
214
+ export {
215
+ h as GpuComputed,
216
+ B as WGSL_Fun
217
+ };