wgpu-kit 0.9.11 → 1.0.0
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 +7 -0
- package/dist/core/buffer.d.ts +1 -1
- package/dist/core/buffer.js +109 -0
- package/dist/core/context.d.ts +2 -0
- package/dist/core/context.js +62 -0
- package/dist/core/errors.js +42 -0
- package/dist/core/kernel.js +197 -0
- package/dist/core/layout.d.ts +5 -1
- package/dist/core/layout.js +69 -0
- package/dist/core/pingpong.js +61 -0
- package/dist/core/raw.js +38 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -1510
- package/dist/interop/three.js +29 -0
- package/dist/media.js +70 -0
- package/dist/observe.d.ts +19 -0
- package/dist/observe.js +76 -0
- package/dist/packs/fields/index.js +230 -0
- package/dist/packs/image/index.js +250 -0
- package/dist/packs/life/boids.js +367 -0
- package/dist/packs/life/index.js +8 -0
- package/dist/packs/life/map.js +110 -0
- package/dist/packs/life/physarum.js +228 -0
- package/dist/packs/life/tentacles.js +238 -0
- package/dist/packs/life/turing.js +203 -0
- package/dist/packs/particles/config.d.ts +1 -0
- package/dist/packs/particles/config.js +36 -0
- package/dist/packs/particles/grid.js +248 -0
- package/dist/packs/particles/index.js +361 -0
- package/dist/packs/particles/presets.js +75 -0
- package/dist/packs/particles/render.js +116 -0
- package/dist/packs/particles/wgsl.js +175 -0
- package/dist/react/index.js +36 -0
- package/dist/vite.js +27 -28
- package/package.json +1 -1
- package/dist/fields.js +0 -585
- package/dist/image.js +0 -318
- package/dist/life.js +0 -1407
- package/dist/particles.js +0 -1245
- package/dist/react.js +0 -1289
- package/dist/three.js +0 -33
package/dist/image.js
DELETED
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
// src/core/errors.ts
|
|
2
|
-
var WgpuKitError = class extends Error {
|
|
3
|
-
constructor(message) {
|
|
4
|
-
super(message);
|
|
5
|
-
this.name = new.target.name;
|
|
6
|
-
}
|
|
7
|
-
};
|
|
8
|
-
var WebGPUUnavailableError = class extends WgpuKitError {
|
|
9
|
-
constructor(reason) {
|
|
10
|
-
super(
|
|
11
|
-
`\u5F53\u524D\u73AF\u5883\u4E0D\u53EF\u7528 WebGPU: ${reason}
|
|
12
|
-
\u6392\u67E5:\u2460 \u6D4F\u89C8\u5668\u9700 Chrome/Edge 113+ \u6216 Safari 18+;\u2461 \u65E0\u5934\u73AF\u5883\u9700\u5F00\u542F WebGPU;\u2462 \u68C0\u67E5 GPU \u9A71\u52A8\u4E0E\u786C\u4EF6\u52A0\u901F\u8BBE\u7F6E\u3002
|
|
13
|
-
\u53EF\u7528 navigator.gpu \u662F\u5426\u5B58\u5728\u5FEB\u901F\u5224\u65AD\u3002`
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
var CompileError = class extends WgpuKitError {
|
|
18
|
-
constructor(kernelName, messages, userCodeOffset) {
|
|
19
|
-
const mapped = messages.map((m) => {
|
|
20
|
-
const userLine = m.line - userCodeOffset;
|
|
21
|
-
const where = userLine > 0 ? `\u7528\u6237\u4EE3\u7801\u7B2C ${userLine} \u884C` : `\u751F\u6210\u4EE3\u7801\u7B2C ${m.line} \u884C(\u5E93\u7684\u95EE\u9898,\u6B22\u8FCE\u62A5 issue)`;
|
|
22
|
-
return ` ${where}: ${m.msg}`;
|
|
23
|
-
}).join("\n");
|
|
24
|
-
super(`kernel "${kernelName}" WGSL \u7F16\u8BD1\u5931\u8D25:
|
|
25
|
-
${mapped}`);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// src/core/context.ts
|
|
30
|
-
var GpuContext = class _GpuContext {
|
|
31
|
-
device;
|
|
32
|
-
adapterInfo;
|
|
33
|
-
constructor(device, adapterInfo) {
|
|
34
|
-
this.device = device;
|
|
35
|
-
this.adapterInfo = adapterInfo;
|
|
36
|
-
}
|
|
37
|
-
static #singleton = null;
|
|
38
|
-
static get() {
|
|
39
|
-
if (!_GpuContext.#singleton) {
|
|
40
|
-
_GpuContext.#singleton = _GpuContext.#create().catch((e) => {
|
|
41
|
-
_GpuContext.#singleton = null;
|
|
42
|
-
throw e;
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
return _GpuContext.#singleton;
|
|
46
|
-
}
|
|
47
|
-
static async #create() {
|
|
48
|
-
if (typeof navigator === "undefined" || !("gpu" in navigator) || !navigator.gpu) {
|
|
49
|
-
throw new WebGPUUnavailableError("navigator.gpu \u4E0D\u5B58\u5728");
|
|
50
|
-
}
|
|
51
|
-
const adapter = await navigator.gpu.requestAdapter({ powerPreference: "high-performance" });
|
|
52
|
-
if (!adapter) throw new WebGPUUnavailableError("requestAdapter() \u8FD4\u56DE null");
|
|
53
|
-
const info = adapter.info;
|
|
54
|
-
const label = info ? [info.vendor, info.architecture, info.description].filter(Boolean).join(" / ") || "unknown" : "unknown";
|
|
55
|
-
const requiredLimits = {};
|
|
56
|
-
const want = [
|
|
57
|
-
"maxStorageBuffersPerShaderStage",
|
|
58
|
-
"maxStorageBuffersInVertexStage",
|
|
59
|
-
"maxStorageBufferBindingSize"
|
|
60
|
-
];
|
|
61
|
-
for (const key of want) {
|
|
62
|
-
const supported = adapter.limits[key];
|
|
63
|
-
if (typeof supported === "number") requiredLimits[key] = supported;
|
|
64
|
-
}
|
|
65
|
-
const device = await adapter.requestDevice({ label: "wgpu-kit", requiredLimits });
|
|
66
|
-
return new _GpuContext(device, label);
|
|
67
|
-
}
|
|
68
|
-
/** device lost 时 reject;调用方可 await 做清理/提示 */
|
|
69
|
-
get lost() {
|
|
70
|
-
return this.device.lost;
|
|
71
|
-
}
|
|
72
|
-
/** 等待队列中已提交的全部 GPU 工作完成(测试/读回前同步用) */
|
|
73
|
-
async sync() {
|
|
74
|
-
await this.device.queue.onSubmittedWorkDone();
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// src/packs/image/index.ts
|
|
79
|
-
var OP_IDS = { grayscale: 0, invert: 1, edge: 2, blur: 3, sharpen: 4, brightness: 5, contrast: 6 };
|
|
80
|
-
async function applyImage(source, target, ops) {
|
|
81
|
-
if (ops.length === 0) throw new Error("applyImage \u9700\u8981\u81F3\u5C11\u4E00\u4E2A\u7B97\u5B50");
|
|
82
|
-
const width = "naturalWidth" in source ? source.naturalWidth : source.width;
|
|
83
|
-
const height = "naturalHeight" in source ? source.naturalHeight : source.height;
|
|
84
|
-
const ctx = await GpuContext.get();
|
|
85
|
-
const device = ctx.device;
|
|
86
|
-
const srcTex = device.createTexture({
|
|
87
|
-
size: [width, height],
|
|
88
|
-
format: "rgba8unorm",
|
|
89
|
-
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
|
|
90
|
-
});
|
|
91
|
-
const c2d = document.createElement("canvas");
|
|
92
|
-
c2d.width = width;
|
|
93
|
-
c2d.height = height;
|
|
94
|
-
const sctx = c2d.getContext("2d", { willReadFrequently: true });
|
|
95
|
-
sctx.drawImage(source, 0, 0);
|
|
96
|
-
const imageData = sctx.getImageData(0, 0, width, height);
|
|
97
|
-
device.queue.writeTexture({ texture: srcTex }, imageData.data, { bytesPerRow: width * 4, rowsPerImage: height }, [width, height]);
|
|
98
|
-
const pool = [];
|
|
99
|
-
const temp = () => {
|
|
100
|
-
const t = pool.pop() ?? device.createTexture({
|
|
101
|
-
size: [width, height],
|
|
102
|
-
format: "rgba8unorm",
|
|
103
|
-
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
|
|
104
|
-
});
|
|
105
|
-
return t;
|
|
106
|
-
};
|
|
107
|
-
const module = device.createShaderModule({ code: shader(), label: "image-filters" });
|
|
108
|
-
const info = await module.getCompilationInfo();
|
|
109
|
-
const errors = info.messages.filter((m) => m.type === "error");
|
|
110
|
-
if (errors.length > 0) throw new CompileError("image-filters", errors.map((m) => ({ line: m.lineNum, msg: m.message })), 0);
|
|
111
|
-
const pipeline = device.createRenderPipeline({
|
|
112
|
-
layout: "auto",
|
|
113
|
-
vertex: { module, entryPoint: "vs" },
|
|
114
|
-
fragment: { module, entryPoint: "fs", targets: [{ format: "rgba8unorm" }] },
|
|
115
|
-
primitive: { topology: "triangle-list" }
|
|
116
|
-
});
|
|
117
|
-
const makePass = (input, output, op) => {
|
|
118
|
-
const p = op;
|
|
119
|
-
const uniform = device.createBuffer({ size: 32, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
120
|
-
const ub = new ArrayBuffer(32);
|
|
121
|
-
const uv = new DataView(ub);
|
|
122
|
-
uv.setUint32(0, OP_IDS[op.op] ?? 0, true);
|
|
123
|
-
uv.setUint32(4, Math.max(1, Math.min(4, Math.round(p.radius ?? 1))), true);
|
|
124
|
-
uv.setFloat32(8, width, true);
|
|
125
|
-
uv.setFloat32(12, height, true);
|
|
126
|
-
uv.setFloat32(16, p.amount ?? 1, true);
|
|
127
|
-
uv.setFloat32(20, p.value ?? 0, true);
|
|
128
|
-
device.queue.writeBuffer(uniform, 0, ub);
|
|
129
|
-
const sampler = device.createSampler({ magFilter: "linear", minFilter: "linear" });
|
|
130
|
-
const bg = device.createBindGroup({
|
|
131
|
-
layout: pipeline.getBindGroupLayout(0),
|
|
132
|
-
entries: [
|
|
133
|
-
{ binding: 0, resource: { buffer: uniform } },
|
|
134
|
-
{ binding: 1, resource: sampler },
|
|
135
|
-
{ binding: 2, resource: input.createView() }
|
|
136
|
-
]
|
|
137
|
-
});
|
|
138
|
-
const enc2 = device.createCommandEncoder();
|
|
139
|
-
const pass2 = enc2.beginRenderPass({
|
|
140
|
-
colorAttachments: output ? [{ view: output.createView(), clearValue: { r: 0, g: 0, b: 0, a: 1 }, loadOp: "clear", storeOp: "store" }] : []
|
|
141
|
-
});
|
|
142
|
-
pass2.setPipeline(pipeline);
|
|
143
|
-
pass2.setBindGroup(0, bg);
|
|
144
|
-
pass2.draw(3);
|
|
145
|
-
pass2.end();
|
|
146
|
-
return enc2.finish();
|
|
147
|
-
};
|
|
148
|
-
let cur = srcTex;
|
|
149
|
-
let passes = 0;
|
|
150
|
-
for (const op of ops) {
|
|
151
|
-
const out = temp();
|
|
152
|
-
device.queue.submit([makePass(cur, out, op)]);
|
|
153
|
-
if (cur !== srcTex) pool.push(cur);
|
|
154
|
-
cur = out;
|
|
155
|
-
passes++;
|
|
156
|
-
}
|
|
157
|
-
const gpuCtx = target.getContext("webgpu");
|
|
158
|
-
if (!gpuCtx) throw new Error('\u76EE\u6807 canvas.getContext("webgpu") \u8FD4\u56DE\u7A7A');
|
|
159
|
-
const format = navigator.gpu.getPreferredCanvasFormat();
|
|
160
|
-
gpuCtx.configure({ device, format, alphaMode: "opaque" });
|
|
161
|
-
const blit = device.createShaderModule({
|
|
162
|
-
code: `
|
|
163
|
-
@group(0) @binding(0) var samp: sampler;
|
|
164
|
-
@group(0) @binding(1) var tex: texture_2d<f32>;
|
|
165
|
-
struct VsOut { @builtin(position) pos: vec4f, @location(0) uv: vec2f };
|
|
166
|
-
@vertex fn vs(@builtin(vertex_index) v: u32) -> VsOut {
|
|
167
|
-
var p = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
|
|
168
|
-
var o: VsOut;
|
|
169
|
-
o.pos = vec4f(p[v], 0.0, 1.0);
|
|
170
|
-
o.uv = (p[v] + vec2f(1.0)) * 0.5;
|
|
171
|
-
o.uv.y = 1.0 - o.uv.y;
|
|
172
|
-
return o;
|
|
173
|
-
}
|
|
174
|
-
@fragment fn fs(i: VsOut) -> @location(0) vec4f {
|
|
175
|
-
let c = textureSample(tex, samp, i.uv);
|
|
176
|
-
return vec4f(c.rgb, 1.0);
|
|
177
|
-
}
|
|
178
|
-
`,
|
|
179
|
-
label: "image-blit"
|
|
180
|
-
});
|
|
181
|
-
const blitPipeline = device.createRenderPipeline({
|
|
182
|
-
layout: "auto",
|
|
183
|
-
vertex: { module: blit, entryPoint: "vs" },
|
|
184
|
-
fragment: { module: blit, entryPoint: "fs", targets: [{ format }] },
|
|
185
|
-
primitive: { topology: "triangle-list" }
|
|
186
|
-
});
|
|
187
|
-
const enc = device.createCommandEncoder();
|
|
188
|
-
const pass = enc.beginRenderPass({
|
|
189
|
-
colorAttachments: [{ view: gpuCtx.getCurrentTexture().createView(), clearValue: { r: 0, g: 0, b: 0, a: 1 }, loadOp: "clear", storeOp: "store" }]
|
|
190
|
-
});
|
|
191
|
-
pass.setPipeline(blitPipeline);
|
|
192
|
-
pass.setBindGroup(0, device.createBindGroup({
|
|
193
|
-
layout: blitPipeline.getBindGroupLayout(0),
|
|
194
|
-
entries: [
|
|
195
|
-
{ binding: 0, resource: device.createSampler({ magFilter: "linear", minFilter: "linear" }) },
|
|
196
|
-
{ binding: 1, resource: cur.createView() }
|
|
197
|
-
]
|
|
198
|
-
}));
|
|
199
|
-
pass.draw(3);
|
|
200
|
-
pass.end();
|
|
201
|
-
device.queue.submit([enc.finish()]);
|
|
202
|
-
srcTex.destroy();
|
|
203
|
-
for (const t of pool) t.destroy();
|
|
204
|
-
const bytesPerRow = Math.ceil(width * 4 / 256) * 256;
|
|
205
|
-
const staging = device.createBuffer({ size: bytesPerRow * height, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ });
|
|
206
|
-
return {
|
|
207
|
-
width,
|
|
208
|
-
height,
|
|
209
|
-
passes,
|
|
210
|
-
async readback() {
|
|
211
|
-
const enc2 = device.createCommandEncoder();
|
|
212
|
-
enc2.copyTextureToBuffer({ texture: cur }, { buffer: staging, bytesPerRow, rowsPerImage: height }, [width, height]);
|
|
213
|
-
device.queue.submit([enc2.finish()]);
|
|
214
|
-
await staging.mapAsync(GPUMapMode.READ);
|
|
215
|
-
const ab = staging.getMappedRange().slice(0);
|
|
216
|
-
staging.unmap();
|
|
217
|
-
staging.destroy();
|
|
218
|
-
return new Uint8Array(ab);
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
function shader() {
|
|
223
|
-
return (
|
|
224
|
-
/* wgsl */
|
|
225
|
-
`
|
|
226
|
-
struct U {
|
|
227
|
-
op: u32, radius: u32,
|
|
228
|
-
w: f32, h: f32, amount: f32, value: f32,
|
|
229
|
-
_p0: u32, _p1: u32,
|
|
230
|
-
};
|
|
231
|
-
@group(0) @binding(0) var<uniform> u: U;
|
|
232
|
-
@group(0) @binding(1) var samp: sampler;
|
|
233
|
-
@group(0) @binding(2) var tex: texture_2d<f32>;
|
|
234
|
-
|
|
235
|
-
struct VsOut { @builtin(position) pos: vec4f, @location(0) uv: vec2f };
|
|
236
|
-
|
|
237
|
-
@vertex
|
|
238
|
-
fn vs(@builtin(vertex_index) v: u32) -> VsOut {
|
|
239
|
-
var p = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
|
|
240
|
-
var o: VsOut;
|
|
241
|
-
o.pos = vec4f(p[v], 0.0, 1.0);
|
|
242
|
-
o.uv = (p[v] + vec2f(1.0)) * 0.5;
|
|
243
|
-
o.uv.y = 1.0 - o.uv.y;
|
|
244
|
-
return o;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
fn lum(c: vec3f) -> f32 {
|
|
248
|
-
return dot(c, vec3f(0.2126, 0.7152, 0.0722));
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
@fragment
|
|
252
|
-
fn fs(i: VsOut) -> @location(0) vec4f {
|
|
253
|
-
let c = textureSample(tex, samp, i.uv).rgb;
|
|
254
|
-
let op = u.op;
|
|
255
|
-
|
|
256
|
-
if (op == 0u) { // grayscale
|
|
257
|
-
let g = vec3f(lum(c));
|
|
258
|
-
return vec4f(g, 1.0);
|
|
259
|
-
}
|
|
260
|
-
if (op == 1u) { // invert
|
|
261
|
-
return vec4f(1.0 - c, 1.0);
|
|
262
|
-
}
|
|
263
|
-
if (op == 2u) { // edge (sobel \u5E45\u503C)
|
|
264
|
-
let e = 1.0 / vec2f(u.w, u.h);
|
|
265
|
-
let t00 = textureSample(tex, samp, i.uv + vec2f(-e.x, -e.y)).rgb;
|
|
266
|
-
let t10 = textureSample(tex, samp, i.uv + vec2f(0.0, -e.y)).rgb;
|
|
267
|
-
let t20 = textureSample(tex, samp, i.uv + vec2f(e.x, -e.y)).rgb;
|
|
268
|
-
let t01 = textureSample(tex, samp, i.uv + vec2f(-e.x, 0.0)).rgb;
|
|
269
|
-
let t21 = textureSample(tex, samp, i.uv + vec2f(e.x, 0.0)).rgb;
|
|
270
|
-
let t02 = textureSample(tex, samp, i.uv + vec2f(-e.x, e.y)).rgb;
|
|
271
|
-
let t12 = textureSample(tex, samp, i.uv + vec2f(0.0, e.y)).rgb;
|
|
272
|
-
let t22 = textureSample(tex, samp, i.uv + vec2f(e.x, e.y)).rgb;
|
|
273
|
-
let sx = (t22 + 2.0 * t21 + t02) - (t00 + 2.0 * t01 + t20);
|
|
274
|
-
let sy = (t02 + 2.0 * t12 + t22) - (t00 + 2.0 * t10 + t20);
|
|
275
|
-
let g = sqrt(vec3f(dot(sx, sx) / 3.0 + dot(sy, sy) / 3.0));
|
|
276
|
-
return vec4f(clamp(g * u.amount, vec3f(0.0), vec3f(1.0)), 1.0);
|
|
277
|
-
}
|
|
278
|
-
if (op == 3u) { // box blur,radius \u6298\u53E0\u6210\u6B65\u957F\u91C7\u6837
|
|
279
|
-
let r = f32(u.radius);
|
|
280
|
-
let e = vec2f(1.0) / vec2f(u.w, u.h) * r;
|
|
281
|
-
var s = vec3f(0.0);
|
|
282
|
-
var n = 0.0;
|
|
283
|
-
for (var dy = -2; dy <= 2; dy++) {
|
|
284
|
-
for (var dx = -2; dx <= 2; dx++) {
|
|
285
|
-
s = s + textureSample(tex, samp, i.uv + vec2f(f32(dx), f32(dy)) * e * 0.6).rgb;
|
|
286
|
-
n = n + 1.0;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return vec4f(s / n, 1.0);
|
|
290
|
-
}
|
|
291
|
-
if (op == 4u) { // sharpen(3x3 \u5377\u79EF)
|
|
292
|
-
let e = vec2f(1.0) / vec2f(u.w, u.h);
|
|
293
|
-
let c0 = textureSample(tex, samp, i.uv).rgb;
|
|
294
|
-
let t00 = textureSample(tex, samp, i.uv + vec2f(-e.x, -e.y)).rgb;
|
|
295
|
-
let t10 = textureSample(tex, samp, i.uv + vec2f(0.0, -e.y)).rgb;
|
|
296
|
-
let t20 = textureSample(tex, samp, i.uv + vec2f(e.x, -e.y)).rgb;
|
|
297
|
-
let t01 = textureSample(tex, samp, i.uv + vec2f(-e.x, 0.0)).rgb;
|
|
298
|
-
let t21 = textureSample(tex, samp, i.uv + vec2f(e.x, 0.0)).rgb;
|
|
299
|
-
let t02 = textureSample(tex, samp, i.uv + vec2f(-e.x, e.y)).rgb;
|
|
300
|
-
let t12 = textureSample(tex, samp, i.uv + vec2f(0.0, e.y)).rgb;
|
|
301
|
-
let t22 = textureSample(tex, samp, i.uv + vec2f(e.x, e.y)).rgb;
|
|
302
|
-
let k = u.amount;
|
|
303
|
-
let acc = t00 + t10 + t20 + t01 + t21 + t02 + t12 + t22;
|
|
304
|
-
return vec4f(clamp(c0 * (1.0 + 8.0 * k) - acc * k, vec3f(0.0), vec3f(1.0)), 1.0);
|
|
305
|
-
}
|
|
306
|
-
if (op == 5u) { // brightness
|
|
307
|
-
return vec4f(clamp(c + vec3f(u.value), vec3f(0.0), vec3f(1.0)), 1.0);
|
|
308
|
-
}
|
|
309
|
-
// contrast
|
|
310
|
-
let g = vec3f(0.5);
|
|
311
|
-
return vec4f(clamp(g + (c - g) * u.value, vec3f(0.0), vec3f(1.0)), 1.0);
|
|
312
|
-
}
|
|
313
|
-
`
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
export {
|
|
317
|
-
applyImage
|
|
318
|
-
};
|