@ludicon/spark.js 0.0.11 → 0.0.13
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 +4 -2
- package/dist/spark.esm.js +378 -607
- package/dist/spark_astc_rgb-DaSIMKXW.js +4 -0
- package/dist/spark_astc_rgba-BToA2Rcq.js +4 -0
- package/dist/spark_bc1_rgb-DZwuM1tO.js +4 -0
- package/dist/spark_bc4_r-DjThizCH.js +4 -0
- package/dist/spark_bc5_rg-6bO0Gvo9.js +4 -0
- package/dist/spark_bc7_rgb-FXpBw9fE.js +4 -0
- package/dist/spark_bc7_rgba-C8Hi2pUY.js +4 -0
- package/dist/spark_eac_r-D8HGiglc.js +4 -0
- package/dist/spark_eac_rg-DvsrLP9h.js +4 -0
- package/dist/spark_etc2_rgb-C-j5FZpn.js +4 -0
- package/dist/utils-Dw9jXlhw.js +169 -0
- package/package.json +2 -2
- package/src/three-gltf.js +6 -2
- package/dist/spark_astc_rgb-ylbf30mQ.js +0 -4
- package/dist/spark_astc_rgba-C4NuyfHw.js +0 -4
- package/dist/spark_bc1_rgb-L0ZV40FW.js +0 -4
- package/dist/spark_bc4_r-B4cqoXVq.js +0 -4
- package/dist/spark_bc5_rg-boGe-kBy.js +0 -4
- package/dist/spark_bc7_rgb-CYdL55pE.js +0 -4
- package/dist/spark_bc7_rgba-BFgOyqos.js +0 -4
- package/dist/spark_eac_r-BFwH430b.js +0 -4
- package/dist/spark_eac_rg--Gm5Gzmk.js +0 -4
- package/dist/spark_etc2_rgb-CWjBHhHQ.js +0 -4
- package/dist/utils-CqE_M33R.js +0 -4
package/dist/spark.esm.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const name = path.replace("./", "");
|
|
5
|
-
const fn = async () => (await module()).default;
|
|
6
|
-
return [name, fn];
|
|
7
|
-
})
|
|
8
|
-
);
|
|
9
|
-
const SparkFormat = {
|
|
1
|
+
const z = /* @__PURE__ */ Object.assign({ "./spark_astc_rgb.wgsl": () => import("./spark_astc_rgb-DaSIMKXW.js"), "./spark_astc_rgba.wgsl": () => import("./spark_astc_rgba-BToA2Rcq.js"), "./spark_bc1_rgb.wgsl": () => import("./spark_bc1_rgb-DZwuM1tO.js"), "./spark_bc4_r.wgsl": () => import("./spark_bc4_r-DjThizCH.js"), "./spark_bc5_rg.wgsl": () => import("./spark_bc5_rg-6bO0Gvo9.js"), "./spark_bc7_rgb.wgsl": () => import("./spark_bc7_rgb-FXpBw9fE.js"), "./spark_bc7_rgba.wgsl": () => import("./spark_bc7_rgba-C8Hi2pUY.js"), "./spark_eac_r.wgsl": () => import("./spark_eac_r-D8HGiglc.js"), "./spark_eac_rg.wgsl": () => import("./spark_eac_rg-DvsrLP9h.js"), "./spark_etc2_rgb.wgsl": () => import("./spark_etc2_rgb-C-j5FZpn.js"), "./utils.wgsl": () => import("./utils-Dw9jXlhw.js") }), k = Object.fromEntries(
|
|
2
|
+
Object.entries(z).map(([n, e]) => [n.replace("./", ""), async () => (await e()).default])
|
|
3
|
+
), s = {
|
|
10
4
|
ASTC_4x4_RGB: 0,
|
|
11
5
|
ASTC_4x4_RGBA: 1,
|
|
12
6
|
// ASTC_4x4_RGBM: 2,
|
|
@@ -25,8 +19,7 @@ const SparkFormat = {
|
|
|
25
19
|
// BC6H_RGB: 15,
|
|
26
20
|
BC7_RGB: 16,
|
|
27
21
|
BC7_RGBA: 17
|
|
28
|
-
}
|
|
29
|
-
const SparkFormatName = [
|
|
22
|
+
}, B = [
|
|
30
23
|
/* 0 */
|
|
31
24
|
"astc-4x4-rgb",
|
|
32
25
|
// ASTC_4x4_RGB
|
|
@@ -73,8 +66,7 @@ const SparkFormatName = [
|
|
|
73
66
|
/* 17 */
|
|
74
67
|
"bc7-rgba"
|
|
75
68
|
// BC7_RGBA
|
|
76
|
-
]
|
|
77
|
-
const SparkShaderFiles = [
|
|
69
|
+
], q = [
|
|
78
70
|
/* 0 */
|
|
79
71
|
"spark_astc_rgb.wgsl",
|
|
80
72
|
// ASTC_4x4_RGB
|
|
@@ -121,8 +113,7 @@ const SparkShaderFiles = [
|
|
|
121
113
|
/* 17 */
|
|
122
114
|
"spark_bc7_rgba.wgsl"
|
|
123
115
|
// BC7_RGBA
|
|
124
|
-
]
|
|
125
|
-
const SparkBlockSize = [
|
|
116
|
+
], Y = [
|
|
126
117
|
/* 0 */
|
|
127
118
|
16,
|
|
128
119
|
// ASTC_4x4_RGB
|
|
@@ -169,8 +160,7 @@ const SparkBlockSize = [
|
|
|
169
160
|
/* 17 */
|
|
170
161
|
16
|
|
171
162
|
// BC7_RGB
|
|
172
|
-
]
|
|
173
|
-
const SparkFormatRatio = [
|
|
163
|
+
], W = [
|
|
174
164
|
/* 0 */
|
|
175
165
|
4,
|
|
176
166
|
// ASTC_4x4_RGB
|
|
@@ -217,36 +207,34 @@ const SparkFormatRatio = [
|
|
|
217
207
|
/* 17 */
|
|
218
208
|
4
|
|
219
209
|
// BC7_RGB
|
|
220
|
-
]
|
|
221
|
-
|
|
222
|
-
"astc-4x4-
|
|
223
|
-
"
|
|
224
|
-
"eac-
|
|
225
|
-
"
|
|
226
|
-
"
|
|
227
|
-
"
|
|
228
|
-
"
|
|
229
|
-
"
|
|
230
|
-
"bc7-
|
|
231
|
-
"bc7-rgba": SparkFormat.BC7_RGBA,
|
|
210
|
+
], T = Object.freeze({
|
|
211
|
+
"astc-4x4-rgb": s.ASTC_4x4_RGB,
|
|
212
|
+
"astc-4x4-rgba": s.ASTC_4x4_RGBA,
|
|
213
|
+
"eac-r": s.EAC_R,
|
|
214
|
+
"eac-rg": s.EAC_RG,
|
|
215
|
+
"etc2-rgb": s.ETC2_RGB,
|
|
216
|
+
"bc1-rgb": s.BC1_RGB,
|
|
217
|
+
"bc4-r": s.BC4_R,
|
|
218
|
+
"bc5-rg": s.BC5_RG,
|
|
219
|
+
"bc7-rgb": s.BC7_RGB,
|
|
220
|
+
"bc7-rgba": s.BC7_RGBA,
|
|
232
221
|
// aliases:
|
|
233
|
-
"astc-rgb":
|
|
234
|
-
"astc-rgba":
|
|
222
|
+
"astc-rgb": s.ASTC_4x4_RGB,
|
|
223
|
+
"astc-rgba": s.ASTC_4x4_RGBA,
|
|
235
224
|
// webgpu aliases:
|
|
236
|
-
"bc1-rgba-unorm":
|
|
237
|
-
"bc1-rgba-unorm-srgb":
|
|
238
|
-
"bc4-r-unorm":
|
|
239
|
-
"bc5-rg-unorm":
|
|
240
|
-
"bc7-rgba-unorm":
|
|
241
|
-
"bc7-rgba-unorm-srgb":
|
|
242
|
-
"etc2-rgb8unorm":
|
|
243
|
-
"etc2-rgb8unorm-srgb":
|
|
244
|
-
"eac-r11unorm":
|
|
245
|
-
"eac-rg11unorm":
|
|
246
|
-
"astc-4x4-unorm":
|
|
247
|
-
"astc-4x4-unorm-srgb":
|
|
248
|
-
})
|
|
249
|
-
const SparkWebGPUFormats = [
|
|
225
|
+
"bc1-rgba-unorm": s.BC1_RGB,
|
|
226
|
+
"bc1-rgba-unorm-srgb": s.BC1_RGB,
|
|
227
|
+
"bc4-r-unorm": s.BC4_R,
|
|
228
|
+
"bc5-rg-unorm": s.BC5_RG,
|
|
229
|
+
"bc7-rgba-unorm": s.BC7_RGBA,
|
|
230
|
+
"bc7-rgba-unorm-srgb": s.BC7_RGBA,
|
|
231
|
+
"etc2-rgb8unorm": s.ETC2_RGB,
|
|
232
|
+
"etc2-rgb8unorm-srgb": s.ETC2_RGB,
|
|
233
|
+
"eac-r11unorm": s.EAC_R,
|
|
234
|
+
"eac-rg11unorm": s.EAC_RG,
|
|
235
|
+
"astc-4x4-unorm": s.ASTC_4x4_RGBA,
|
|
236
|
+
"astc-4x4-unorm-srgb": s.ASTC_4x4_RGBA
|
|
237
|
+
}), V = [
|
|
250
238
|
/* 0 */
|
|
251
239
|
"astc-4x4-unorm",
|
|
252
240
|
// ASTC_4x4_RGB
|
|
@@ -293,33 +281,32 @@ const SparkWebGPUFormats = [
|
|
|
293
281
|
/* 17 */
|
|
294
282
|
"bc7-rgba-unorm"
|
|
295
283
|
// BC7_RGB
|
|
296
|
-
]
|
|
297
|
-
const SparkFormatIsRGB = [
|
|
284
|
+
], $ = [
|
|
298
285
|
/* 0 */
|
|
299
|
-
|
|
286
|
+
!0,
|
|
300
287
|
// ASTC_4x4_RGB
|
|
301
288
|
/* 1 */
|
|
302
|
-
|
|
289
|
+
!0,
|
|
303
290
|
// ASTC_4x4_RGBA
|
|
304
291
|
/* 2 */
|
|
305
292
|
null,
|
|
306
293
|
/* 3 */
|
|
307
294
|
null,
|
|
308
295
|
/* 4 */
|
|
309
|
-
|
|
296
|
+
!1,
|
|
310
297
|
// EAC_R
|
|
311
298
|
/* 5 */
|
|
312
|
-
|
|
299
|
+
!1,
|
|
313
300
|
// EAC_RG
|
|
314
301
|
/* 6 */
|
|
315
|
-
|
|
302
|
+
!0,
|
|
316
303
|
// ETC2_RGB
|
|
317
304
|
/* 7 */
|
|
318
305
|
null,
|
|
319
306
|
/* 8 */
|
|
320
307
|
null,
|
|
321
308
|
/* 9 */
|
|
322
|
-
|
|
309
|
+
!0,
|
|
323
310
|
// BC1_RGB
|
|
324
311
|
/* 10 */
|
|
325
312
|
null,
|
|
@@ -328,135 +315,113 @@ const SparkFormatIsRGB = [
|
|
|
328
315
|
/* 12 */
|
|
329
316
|
null,
|
|
330
317
|
/* 13 */
|
|
331
|
-
|
|
318
|
+
!1,
|
|
332
319
|
// BC4_R
|
|
333
320
|
/* 14 */
|
|
334
|
-
|
|
321
|
+
!1,
|
|
335
322
|
// BC5_RG
|
|
336
323
|
/* 15 */
|
|
337
324
|
null,
|
|
338
325
|
/* 16 */
|
|
339
|
-
|
|
326
|
+
!0,
|
|
340
327
|
// BC7_RGB
|
|
341
328
|
/* 17 */
|
|
342
|
-
|
|
329
|
+
!0
|
|
343
330
|
// BC7_RGB
|
|
344
331
|
];
|
|
345
|
-
function
|
|
346
|
-
if (!
|
|
347
|
-
throw new Error(
|
|
348
|
-
}
|
|
332
|
+
function w(n, e) {
|
|
333
|
+
if (!n)
|
|
334
|
+
throw new Error(e);
|
|
349
335
|
}
|
|
350
|
-
function
|
|
351
|
-
return typeof GPUDevice
|
|
336
|
+
function X(n) {
|
|
337
|
+
return typeof GPUDevice < "u" && n instanceof GPUDevice;
|
|
352
338
|
}
|
|
353
|
-
function
|
|
339
|
+
function F() {
|
|
354
340
|
return ["iPad Simulator", "iPhone Simulator", "iPod Simulator", "iPad", "iPhone", "iPod"].includes(navigator.platform) || // iPad on iOS 13 detection
|
|
355
341
|
navigator.userAgent.includes("Mac") && "ontouchend" in document;
|
|
356
342
|
}
|
|
357
|
-
function
|
|
358
|
-
const
|
|
359
|
-
return
|
|
343
|
+
function j() {
|
|
344
|
+
const n = navigator.userAgent.match(/Safari\/(\d+(\.\d+)?)/);
|
|
345
|
+
return n && parseFloat(n[1]);
|
|
360
346
|
}
|
|
361
|
-
function
|
|
362
|
-
const
|
|
363
|
-
return
|
|
347
|
+
function Q() {
|
|
348
|
+
const n = navigator.userAgent.match(/Firefox\/(\d+(\.\d+)?)/);
|
|
349
|
+
return n && parseFloat(n[1]);
|
|
364
350
|
}
|
|
365
|
-
function
|
|
366
|
-
const
|
|
367
|
-
const formatMap = {
|
|
351
|
+
function H(n) {
|
|
352
|
+
const e = /* @__PURE__ */ new Set(), r = {
|
|
368
353
|
"texture-compression-bc": [
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
354
|
+
s.BC1_RGB,
|
|
355
|
+
s.BC4_R,
|
|
356
|
+
s.BC5_RG,
|
|
357
|
+
s.BC7_RGB,
|
|
358
|
+
s.BC7_RGBA
|
|
374
359
|
],
|
|
375
|
-
"texture-compression-etc2": [
|
|
376
|
-
"texture-compression-astc": [
|
|
360
|
+
"texture-compression-etc2": [s.ETC2_RGB, s.EAC_R, s.EAC_RG],
|
|
361
|
+
"texture-compression-astc": [s.ASTC_4x4_RGB, s.ASTC_4x4_RGBA]
|
|
377
362
|
};
|
|
378
|
-
for (const [
|
|
379
|
-
if (
|
|
380
|
-
for (const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
return supportedFormats;
|
|
363
|
+
for (const [t, i] of Object.entries(r))
|
|
364
|
+
if (n.features.has(t))
|
|
365
|
+
for (const a of i)
|
|
366
|
+
e.add(a);
|
|
367
|
+
return e;
|
|
386
368
|
}
|
|
387
|
-
function
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return new Uint8Array(imageData.data.buffer);
|
|
369
|
+
function Z(n) {
|
|
370
|
+
const e = document.createElement("canvas");
|
|
371
|
+
e.width = n.width, e.height = n.height;
|
|
372
|
+
const r = e.getContext("2d");
|
|
373
|
+
r.drawImage(n, 0, 0);
|
|
374
|
+
const t = r.getImageData(0, 0, n.width, n.height);
|
|
375
|
+
return new Uint8Array(t.data.buffer);
|
|
395
376
|
}
|
|
396
|
-
function
|
|
397
|
-
return /\.svg(?:$|\?)/i.test(
|
|
377
|
+
function J(n) {
|
|
378
|
+
return /\.svg(?:$|\?)/i.test(n) || /^data:image\/svg\+xml[,;]/i.test(n);
|
|
398
379
|
}
|
|
399
|
-
function
|
|
400
|
-
return new Promise((
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
img.decoding = "async";
|
|
404
|
-
img.onload = () => resolve(img);
|
|
405
|
-
img.onerror = reject;
|
|
406
|
-
img.src = url;
|
|
380
|
+
function K(n) {
|
|
381
|
+
return new Promise((e, r) => {
|
|
382
|
+
const t = new Image();
|
|
383
|
+
t.crossOrigin = "anonymous", t.decoding = "async", t.onload = () => e(t), t.onerror = r, t.src = n;
|
|
407
384
|
});
|
|
408
385
|
}
|
|
409
|
-
async function
|
|
410
|
-
const
|
|
411
|
-
if (!
|
|
412
|
-
const
|
|
413
|
-
return createImageBitmap(
|
|
414
|
-
imageOrientation:
|
|
415
|
-
colorSpaceConversion:
|
|
386
|
+
async function ee(n, e = {}) {
|
|
387
|
+
const r = await fetch(n, { mode: "cors" });
|
|
388
|
+
if (!r.ok) throw new Error(`HTTP ${r.status} for ${n}`);
|
|
389
|
+
const t = await r.blob();
|
|
390
|
+
return createImageBitmap(t, {
|
|
391
|
+
imageOrientation: e.flipY ? "flipY" : "none",
|
|
392
|
+
colorSpaceConversion: e.colorSpaceConversion ?? "none",
|
|
416
393
|
premultiplyAlpha: "none"
|
|
417
394
|
});
|
|
418
395
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
} else {
|
|
423
|
-
return loadImageBitmap(url);
|
|
424
|
-
}
|
|
396
|
+
const te = F();
|
|
397
|
+
function v(n) {
|
|
398
|
+
return J(n) || te ? K(n) : ee(n);
|
|
425
399
|
}
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
let offset = 0;
|
|
431
|
-
const bufferRanges = [];
|
|
400
|
+
const O = 256, N = 4;
|
|
401
|
+
function re(n, e, r, t) {
|
|
402
|
+
let i = 0, a = 0;
|
|
403
|
+
const o = [];
|
|
432
404
|
do {
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
mipmapCount++;
|
|
438
|
-
bufferRanges.push({ offset, alignedSize, w, h, bw, bh, bytesPerRow });
|
|
439
|
-
offset += alignedSize;
|
|
440
|
-
w = Math.max(1, Math.floor(w / 2));
|
|
441
|
-
h = Math.max(1, Math.floor(h / 2));
|
|
442
|
-
} while (mipmaps && (w >= MIN_MIP_SIZE || h >= MIN_MIP_SIZE));
|
|
443
|
-
return { mipmapCount, outputSize: offset, bufferRanges };
|
|
405
|
+
const c = Math.ceil(n / 4), u = Math.ceil(e / 4), f = Math.ceil(c * r / O) * O, l = u * f;
|
|
406
|
+
i++, o.push({ offset: a, alignedSize: l, w: n, h: e, bw: c, bh: u, bytesPerRow: f }), a += l, n = Math.max(1, Math.floor(n / 2)), e = Math.max(1, Math.floor(e / 2));
|
|
407
|
+
} while (t && (n >= N || e >= N));
|
|
408
|
+
return { mipmapCount: i, outputSize: a, bufferRanges: o };
|
|
444
409
|
}
|
|
445
|
-
class
|
|
446
|
-
#
|
|
447
|
-
#
|
|
448
|
-
#
|
|
449
|
-
#
|
|
450
|
-
#
|
|
451
|
-
#
|
|
452
|
-
#
|
|
453
|
-
#
|
|
454
|
-
#
|
|
455
|
-
#
|
|
456
|
-
#
|
|
457
|
-
#
|
|
458
|
-
#
|
|
459
|
-
#
|
|
410
|
+
class L {
|
|
411
|
+
#e;
|
|
412
|
+
#o;
|
|
413
|
+
#i = [];
|
|
414
|
+
#l;
|
|
415
|
+
#c;
|
|
416
|
+
#m;
|
|
417
|
+
#g;
|
|
418
|
+
#u;
|
|
419
|
+
#a;
|
|
420
|
+
#n = new Array(3);
|
|
421
|
+
#r;
|
|
422
|
+
#f;
|
|
423
|
+
#s;
|
|
424
|
+
#_ = 0;
|
|
460
425
|
/**
|
|
461
426
|
* Initialize the encoder by detecting available compression formats.
|
|
462
427
|
* @param {GPUDevice} device - WebGPU device.
|
|
@@ -464,10 +429,9 @@ class Spark {
|
|
|
464
429
|
* @param {boolean} options.preload - Whether to preload all encoder pipelines (false by default).
|
|
465
430
|
* @returns {Promise<void>} Resolves when initialization is complete.
|
|
466
431
|
*/
|
|
467
|
-
static async create(
|
|
468
|
-
const
|
|
469
|
-
await
|
|
470
|
-
return instance;
|
|
432
|
+
static async create(e, r = {}) {
|
|
433
|
+
const t = new L();
|
|
434
|
+
return await t.#B(e, r.preload ?? !1, r.useTimestampQueries ?? !1), t;
|
|
471
435
|
}
|
|
472
436
|
/**
|
|
473
437
|
* Returns a list of supported texture compression format names.
|
|
@@ -484,7 +448,7 @@ class Spark {
|
|
|
484
448
|
* console.log("Supported formats:", formats);
|
|
485
449
|
*/
|
|
486
450
|
enumerateSupportedFormats() {
|
|
487
|
-
const
|
|
451
|
+
const e = [
|
|
488
452
|
"astc-4x4-rgb",
|
|
489
453
|
"astc-4x4-rgba",
|
|
490
454
|
"eac-r",
|
|
@@ -495,16 +459,15 @@ class Spark {
|
|
|
495
459
|
"bc5-rg",
|
|
496
460
|
"bc7-rgb",
|
|
497
461
|
"bc7-rgba"
|
|
498
|
-
];
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
supported.push({ format, ratio });
|
|
462
|
+
], r = [];
|
|
463
|
+
for (const t of e) {
|
|
464
|
+
const i = T[t];
|
|
465
|
+
if (this.#t(i)) {
|
|
466
|
+
const a = W[i];
|
|
467
|
+
r.push({ format: t, ratio: a });
|
|
505
468
|
}
|
|
506
469
|
}
|
|
507
|
-
return
|
|
470
|
+
return r;
|
|
508
471
|
}
|
|
509
472
|
/**
|
|
510
473
|
* Determines the set of WebGPU features to request when initializing the device.
|
|
@@ -524,25 +487,9 @@ class Spark {
|
|
|
524
487
|
* // Create spark object for the given device.
|
|
525
488
|
* const spark = Spark.create(device)
|
|
526
489
|
*/
|
|
527
|
-
static getRequiredFeatures(
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
if (!IOS && adapter.features.has("texture-compression-bc")) {
|
|
531
|
-
features.push("texture-compression-bc");
|
|
532
|
-
}
|
|
533
|
-
if (adapter.features.has("texture-compression-etc2")) {
|
|
534
|
-
features.push("texture-compression-etc2");
|
|
535
|
-
}
|
|
536
|
-
if (adapter.features.has("texture-compression-astc")) {
|
|
537
|
-
features.push("texture-compression-astc");
|
|
538
|
-
}
|
|
539
|
-
if (adapter.features.has("shader-f16")) {
|
|
540
|
-
features.push("shader-f16");
|
|
541
|
-
}
|
|
542
|
-
if (adapter.features.has("timestamp-query")) {
|
|
543
|
-
features.push("timestamp-query");
|
|
544
|
-
}
|
|
545
|
-
return features;
|
|
490
|
+
static getRequiredFeatures(e) {
|
|
491
|
+
const r = [];
|
|
492
|
+
return !F() && e.features.has("texture-compression-bc") && r.push("texture-compression-bc"), e.features.has("texture-compression-etc2") && r.push("texture-compression-etc2"), e.features.has("texture-compression-astc") && r.push("texture-compression-astc"), e.features.has("shader-f16") && r.push("shader-f16"), e.features.has("timestamp-query") && r.push("timestamp-query"), r;
|
|
546
493
|
}
|
|
547
494
|
/**
|
|
548
495
|
* Try to determine the best compression options automatically. Do not use this in production, this is
|
|
@@ -552,20 +499,14 @@ class Spark {
|
|
|
552
499
|
* @param {Object} options - Encoding options.
|
|
553
500
|
* @returns {Object} - Recommended encoding options with an explicit encoding format.
|
|
554
501
|
*/
|
|
555
|
-
async selectPreferredOptions(
|
|
556
|
-
if (
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
if (image instanceof GPUTexture) {
|
|
562
|
-
if (image.format.endsWith("-srgb")) options.srgb = true;
|
|
563
|
-
}
|
|
564
|
-
if (format == SparkFormat.EAC_RG || format == SparkFormat.BC5_RG) {
|
|
565
|
-
options.normal = true;
|
|
566
|
-
}
|
|
502
|
+
async selectPreferredOptions(e, r = {}) {
|
|
503
|
+
if (r.format == null || r.format == "auto") {
|
|
504
|
+
const t = e instanceof Image || e instanceof ImageBitmap || e instanceof GPUTexture ? e : await v(e);
|
|
505
|
+
r.format = "auto";
|
|
506
|
+
const i = await this.#b(r, t);
|
|
507
|
+
r.format = B[i], t instanceof GPUTexture && t.format.endsWith("-srgb") && (r.srgb = !0), (i == s.EAC_RG || i == s.BC5_RG) && (r.normal = !0);
|
|
567
508
|
}
|
|
568
|
-
return
|
|
509
|
+
return r;
|
|
569
510
|
}
|
|
570
511
|
/**
|
|
571
512
|
* Load an image and encode it to a compressed GPU texture.
|
|
@@ -606,183 +547,105 @@ class Spark {
|
|
|
606
547
|
*
|
|
607
548
|
* @returns {Promise<GPUTexture>} A promise resolving to the encoded GPU texture.
|
|
608
549
|
*/
|
|
609
|
-
async encodeTexture(
|
|
610
|
-
|
|
611
|
-
const
|
|
612
|
-
console.log("Loaded image",
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
}
|
|
646
|
-
let tmpTexture;
|
|
647
|
-
if (needsProcessing) {
|
|
648
|
-
if (image instanceof GPUTexture) {
|
|
649
|
-
this.#processInputTexture(commandEncoder, image, inputTexture, width, height, srgb, options.flipY);
|
|
650
|
-
} else {
|
|
651
|
-
tmpTexture = this.#device.createTexture({
|
|
652
|
-
size: [image.width, image.height, 1],
|
|
653
|
-
mipLevelCount: 1,
|
|
654
|
-
format: "rgba8unorm",
|
|
655
|
-
// RENDER_ATTACHMENT usage is necessary for copyExternalImageToTexture
|
|
656
|
-
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
|
|
657
|
-
viewFormats
|
|
658
|
-
});
|
|
659
|
-
this.#device.queue.copyExternalImageToTexture(
|
|
660
|
-
{ source: image },
|
|
661
|
-
{ texture: tmpTexture },
|
|
662
|
-
{ width: image.width, height: image.height }
|
|
663
|
-
);
|
|
664
|
-
this.#processInputTexture(commandEncoder, tmpTexture, inputTexture, width, height, colorMode, options.flipY);
|
|
665
|
-
}
|
|
666
|
-
} else {
|
|
667
|
-
if (image instanceof GPUTexture) {
|
|
668
|
-
if (mipmaps) {
|
|
669
|
-
commandEncoder.copyTextureToTexture({ texture: image }, { texture: inputTexture }, { width, height });
|
|
670
|
-
} else {
|
|
671
|
-
inputTexture = image;
|
|
672
|
-
}
|
|
673
|
-
} else {
|
|
674
|
-
this.#device.queue.copyExternalImageToTexture({ source: image }, { texture: inputTexture }, { width, height });
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
if (mipmaps) {
|
|
678
|
-
this.#generateMipmaps(commandEncoder, inputTexture, mipmapCount, width, height, colorMode);
|
|
679
|
-
}
|
|
680
|
-
commandEncoder.popDebugGroup?.();
|
|
681
|
-
console.timeEnd("create input texture #" + counter);
|
|
682
|
-
const outputTexture = this.#device.createTexture({
|
|
683
|
-
size: [width, height, 1],
|
|
684
|
-
mipLevelCount: mipmapCount,
|
|
685
|
-
format: webgpuFormat,
|
|
550
|
+
async encodeTexture(e, r = {}) {
|
|
551
|
+
w(this.#e, "Spark is not initialized");
|
|
552
|
+
const t = e instanceof Image || e instanceof ImageBitmap || e instanceof GPUTexture ? e : await v(e);
|
|
553
|
+
console.log("Loaded image", t);
|
|
554
|
+
const i = await this.#b(r, t), a = this.#p(i), o = Math.ceil(t.width / 4) * 4, c = Math.ceil(t.height / 4) * 4, u = Y[i], f = r.generateMipmaps || r.mips, { mipmapCount: l, outputSize: d, bufferRanges: m } = re(o, c, u, f), h = (r.srgb || r.format?.endsWith("srgb")) && $[i], _ = h ? 1 : r.normal ? 2 : 0, G = V[i] + (h ? "-srgb" : ""), A = h ? ["rgba8unorm", "rgba8unorm-srgb"] : ["rgba8unorm"], x = this.#_++;
|
|
555
|
+
console.time("create input texture #" + x);
|
|
556
|
+
let S = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.STORAGE_BINDING;
|
|
557
|
+
const P = r.flipY || o != t.width || c != t.height;
|
|
558
|
+
!P && !(t instanceof GPUTexture) && (S |= GPUTextureUsage.RENDER_ATTACHMENT);
|
|
559
|
+
const g = this.#e.createCommandEncoder();
|
|
560
|
+
g.pushDebugGroup?.("spark process texture"), this.#r && typeof g.writeTimestamp == "function" && g.writeTimestamp(this.#r, 0);
|
|
561
|
+
let b;
|
|
562
|
+
(P || !(t instanceof GPUTexture && !f)) && (b = this.#e.createTexture({
|
|
563
|
+
size: [o, c, 1],
|
|
564
|
+
mipLevelCount: l,
|
|
565
|
+
format: "rgba8unorm",
|
|
566
|
+
usage: S,
|
|
567
|
+
viewFormats: A
|
|
568
|
+
}));
|
|
569
|
+
let C;
|
|
570
|
+
P ? t instanceof GPUTexture ? this.#d(g, t, b, o, c, h, r.flipY) : (C = this.#e.createTexture({
|
|
571
|
+
size: [t.width, t.height, 1],
|
|
572
|
+
mipLevelCount: 1,
|
|
573
|
+
format: "rgba8unorm",
|
|
574
|
+
// RENDER_ATTACHMENT usage is necessary for copyExternalImageToTexture
|
|
575
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
|
|
576
|
+
viewFormats: A
|
|
577
|
+
}), this.#e.queue.copyExternalImageToTexture(
|
|
578
|
+
{ source: t },
|
|
579
|
+
{ texture: C },
|
|
580
|
+
{ width: t.width, height: t.height }
|
|
581
|
+
), this.#d(g, C, b, o, c, _, r.flipY)) : t instanceof GPUTexture ? f ? g.copyTextureToTexture({ texture: t }, { texture: b }, { width: o, height: c }) : b = t : this.#e.queue.copyExternalImageToTexture({ source: t }, { texture: b }, { width: o, height: c }), f && this.#T(g, b, l, o, c, _), g.popDebugGroup?.(), console.timeEnd("create input texture #" + x);
|
|
582
|
+
const U = this.#e.createTexture({
|
|
583
|
+
size: [o, c, 1],
|
|
584
|
+
mipLevelCount: l,
|
|
585
|
+
format: G,
|
|
686
586
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
687
|
-
})
|
|
688
|
-
|
|
689
|
-
size: outputSize,
|
|
587
|
+
}), y = this.#e.createBuffer({
|
|
588
|
+
size: d,
|
|
690
589
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
|
|
590
|
+
}), E = `dispatch compute shader '${B[i]}' #${x}`;
|
|
591
|
+
console.time(E), g.pushDebugGroup?.("spark encode texture");
|
|
592
|
+
let M = {};
|
|
593
|
+
this.#r && typeof g.writeTimestamp != "function" && (M = {
|
|
594
|
+
writeTimestamps: {
|
|
595
|
+
querySet: this.#r,
|
|
596
|
+
beginningOfPassWriteIndex: 0,
|
|
597
|
+
endOfPassWriteIndex: 1
|
|
598
|
+
}
|
|
691
599
|
});
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
args = {
|
|
698
|
-
writeTimestamps: {
|
|
699
|
-
querySet: this.#querySet,
|
|
700
|
-
beginningOfPassWriteIndex: 0,
|
|
701
|
-
endOfPassWriteIndex: 1
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
const pipeline = await pipelinePromise;
|
|
706
|
-
const pass = commandEncoder.beginComputePass(args);
|
|
707
|
-
pass.setPipeline(pipeline);
|
|
708
|
-
for (let m = 0; m < mipmapCount; m++) {
|
|
709
|
-
const bindGroup = this.#device.createBindGroup({
|
|
710
|
-
layout: pipeline.getBindGroupLayout(0),
|
|
600
|
+
const I = await a, R = g.beginComputePass(M);
|
|
601
|
+
R.setPipeline(I);
|
|
602
|
+
for (let p = 0; p < l; p++) {
|
|
603
|
+
const D = this.#e.createBindGroup({
|
|
604
|
+
layout: I.getBindGroupLayout(0),
|
|
711
605
|
entries: [
|
|
712
606
|
{
|
|
713
607
|
binding: 0,
|
|
714
|
-
resource:
|
|
715
|
-
baseMipLevel:
|
|
608
|
+
resource: b.createView({
|
|
609
|
+
baseMipLevel: p,
|
|
716
610
|
mipLevelCount: 1
|
|
717
611
|
})
|
|
718
612
|
},
|
|
719
613
|
{
|
|
720
614
|
binding: 1,
|
|
721
|
-
resource: this.#
|
|
615
|
+
resource: this.#a
|
|
722
616
|
},
|
|
723
617
|
{
|
|
724
618
|
binding: 2,
|
|
725
619
|
resource: {
|
|
726
|
-
buffer:
|
|
727
|
-
offset:
|
|
728
|
-
size:
|
|
620
|
+
buffer: y,
|
|
621
|
+
offset: m[p].offset,
|
|
622
|
+
size: m[p].size
|
|
729
623
|
}
|
|
730
624
|
}
|
|
731
625
|
]
|
|
732
626
|
});
|
|
733
|
-
|
|
734
|
-
pass.dispatchWorkgroups(Math.ceil(bufferRanges[m].bw / 16), Math.ceil(bufferRanges[m].bh / 16));
|
|
627
|
+
R.setBindGroup(0, D), R.dispatchWorkgroups(Math.ceil(m[p].bw / 16), Math.ceil(m[p].bh / 16));
|
|
735
628
|
}
|
|
736
|
-
|
|
737
|
-
for (let
|
|
738
|
-
|
|
629
|
+
R.end();
|
|
630
|
+
for (let p = 0; p < l; p++)
|
|
631
|
+
g.copyBufferToTexture(
|
|
739
632
|
{
|
|
740
|
-
buffer:
|
|
741
|
-
offset:
|
|
742
|
-
bytesPerRow:
|
|
743
|
-
rowsPerImage:
|
|
633
|
+
buffer: y,
|
|
634
|
+
offset: m[p].offset,
|
|
635
|
+
bytesPerRow: m[p].bytesPerRow,
|
|
636
|
+
rowsPerImage: m[p].bh
|
|
744
637
|
},
|
|
745
638
|
{
|
|
746
|
-
texture:
|
|
747
|
-
mipLevel:
|
|
639
|
+
texture: U,
|
|
640
|
+
mipLevel: p
|
|
748
641
|
},
|
|
749
642
|
{
|
|
750
|
-
width:
|
|
751
|
-
height:
|
|
643
|
+
width: m[p].bw * 4,
|
|
644
|
+
height: m[p].bh * 4,
|
|
752
645
|
depthOrArrayLayers: 1
|
|
753
646
|
}
|
|
754
647
|
);
|
|
755
|
-
|
|
756
|
-
if (this.#querySet && typeof commandEncoder.writeTimestamp === "function") {
|
|
757
|
-
commandEncoder.writeTimestamp(this.#querySet, 1);
|
|
758
|
-
}
|
|
759
|
-
commandEncoder.popDebugGroup?.();
|
|
760
|
-
this.#device.queue.submit([commandEncoder.finish()]);
|
|
761
|
-
console.timeEnd(label);
|
|
762
|
-
tmpTexture?.destroy();
|
|
763
|
-
if (inputTexture != image) {
|
|
764
|
-
inputTexture?.destroy();
|
|
765
|
-
}
|
|
766
|
-
outputBuffer?.destroy();
|
|
767
|
-
return outputTexture;
|
|
768
|
-
}
|
|
769
|
-
/**
|
|
770
|
-
* Returns the size of the given image after compression with the given options. This is an approximation
|
|
771
|
-
* as it doesn't take into account the specific GPU memory layout.
|
|
772
|
-
*/
|
|
773
|
-
async estimateSize(image, options = {}) {
|
|
774
|
-
assert(this.#device, "Spark is not initialized");
|
|
775
|
-
assert(image instanceof Image || image instanceof ImageBitmap || image instanceof GPUTexture);
|
|
776
|
-
const format = this.#getPreferredFormat(options.format ?? "rgb", options.preferLowQuality);
|
|
777
|
-
if (format === void 0) {
|
|
778
|
-
throw new Error(`Unsupported format: ${options.format}`);
|
|
779
|
-
}
|
|
780
|
-
const width = Math.ceil(image.width / 4) * 4;
|
|
781
|
-
const height = Math.ceil(image.height / 4) * 4;
|
|
782
|
-
const blockSize = SparkBlockSize[format];
|
|
783
|
-
const mipmaps = options.generateMipmaps || options.mips;
|
|
784
|
-
const { outputSize } = computeMipmapLayout(width, height, blockSize, mipmaps);
|
|
785
|
-
return outputSize;
|
|
648
|
+
return this.#r && typeof g.writeTimestamp == "function" && g.writeTimestamp(this.#r, 1), g.popDebugGroup?.(), this.#e.queue.submit([g.finish()]), console.timeEnd(E), C?.destroy(), b != t && b?.destroy(), y?.destroy(), U;
|
|
786
649
|
}
|
|
787
650
|
/**
|
|
788
651
|
* Returns the time (in milliseconds) it took to perform the most recent `encodeTexture()` call.
|
|
@@ -804,170 +667,128 @@ class Spark {
|
|
|
804
667
|
* @throws {Error} If the GPU work has not been submitted, or if timestamp queries fail.
|
|
805
668
|
*/
|
|
806
669
|
async getTimeElapsed() {
|
|
807
|
-
if (!this.#
|
|
670
|
+
if (!this.#r)
|
|
808
671
|
return 0;
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
this.#device.queue.submit([commandEncoder.finish()]);
|
|
814
|
-
await this.#device.queue.onSubmittedWorkDone();
|
|
815
|
-
await this.#queryReadbackBuffer.mapAsync(GPUMapMode.READ);
|
|
816
|
-
const arrayBuffer = this.#queryReadbackBuffer.getMappedRange();
|
|
817
|
-
const timestamps = new BigUint64Array(arrayBuffer);
|
|
818
|
-
const t0 = timestamps[0];
|
|
819
|
-
const t1 = timestamps[1];
|
|
820
|
-
this.#queryReadbackBuffer.unmap();
|
|
821
|
-
const elapsedNanoseconds = Number(t1 - t0);
|
|
822
|
-
const elapsedMilliseconds = elapsedNanoseconds / 1e6;
|
|
823
|
-
return elapsedMilliseconds;
|
|
672
|
+
const e = this.#e.createCommandEncoder();
|
|
673
|
+
e.resolveQuerySet(this.#r, 0, 2, this.#f, 0), e.copyBufferToBuffer(this.#f, 0, this.#s, 0, 16), this.#e.queue.submit([e.finish()]), await this.#e.queue.onSubmittedWorkDone(), await this.#s.mapAsync(GPUMapMode.READ);
|
|
674
|
+
const r = this.#s.getMappedRange(), t = new BigUint64Array(r), i = t[0], a = t[1];
|
|
675
|
+
return this.#s.unmap(), Number(a - i) / 1e6;
|
|
824
676
|
}
|
|
825
|
-
async #
|
|
826
|
-
|
|
827
|
-
assert(isWebGPU(device), "device is not a WebGPU device");
|
|
828
|
-
this.#device = device;
|
|
829
|
-
this.#supportedFormats = detectWebGPUFormats(this.#device);
|
|
830
|
-
this.#defaultSampler = this.#device.createSampler({
|
|
677
|
+
async #B(e, r, t) {
|
|
678
|
+
w(e, "device is required"), w(X(e), "device is not a WebGPU device"), this.#e = e, this.#o = H(this.#e), this.#a = this.#e.createSampler({
|
|
831
679
|
magFilter: "linear",
|
|
832
680
|
minFilter: "linear"
|
|
833
681
|
});
|
|
834
|
-
for (let i = 0; i < 3; i++)
|
|
835
|
-
this.#
|
|
682
|
+
for (let i = 0; i < 3; i++)
|
|
683
|
+
this.#n[i] = this.#e.createBuffer({
|
|
836
684
|
size: 4,
|
|
837
685
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
838
|
-
});
|
|
839
|
-
|
|
686
|
+
}), this.#e.queue.writeBuffer(this.#n[i], 0, new Uint32Array([i]));
|
|
687
|
+
if (t && this.#e.features.has("timestamp-query")) {
|
|
688
|
+
const i = j(), a = Q();
|
|
689
|
+
(!i || i >= 26) && !a && (this.#r = this.#e.createQuerySet({
|
|
690
|
+
type: "timestamp",
|
|
691
|
+
count: 2
|
|
692
|
+
}), this.#f = this.#e.createBuffer({
|
|
693
|
+
size: 16,
|
|
694
|
+
// 2 timestamps × 8 bytes each
|
|
695
|
+
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.QUERY_RESOLVE
|
|
696
|
+
}), this.#s = this.#e.createBuffer({
|
|
697
|
+
size: 16,
|
|
698
|
+
// 2 timestamps × 8 bytes each
|
|
699
|
+
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
700
|
+
}));
|
|
840
701
|
}
|
|
841
|
-
if (
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
this.#
|
|
846
|
-
|
|
847
|
-
count: 2
|
|
848
|
-
});
|
|
849
|
-
this.#queryBuffer = this.#device.createBuffer({
|
|
850
|
-
size: 16,
|
|
851
|
-
// 2 timestamps × 8 bytes each
|
|
852
|
-
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.QUERY_RESOLVE
|
|
702
|
+
if (this.#l = this.#e.features.has("shader-f16"), await this.#G(), r) {
|
|
703
|
+
let i;
|
|
704
|
+
Array.isArray(r) ? i = r.map((a) => this.#h(a, !1)) : i = this.#o;
|
|
705
|
+
for (const a of i)
|
|
706
|
+
a !== void 0 && !this.#i[a] && this.#p(a).catch((o) => {
|
|
707
|
+
console.error(`Failed to preload pipeline for format ${a}:`, o);
|
|
853
708
|
});
|
|
854
|
-
this.#queryReadbackBuffer = this.#device.createBuffer({
|
|
855
|
-
size: 16,
|
|
856
|
-
// 2 timestamps × 8 bytes each
|
|
857
|
-
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
this.#supportsFloat16 = this.#device.features.has("shader-f16");
|
|
862
|
-
await this.#loadUtilPipelines();
|
|
863
|
-
if (preload) {
|
|
864
|
-
let formatsToLoad;
|
|
865
|
-
if (Array.isArray(preload)) {
|
|
866
|
-
formatsToLoad = preload.map((n) => this.#getPreferredFormat(n, false));
|
|
867
|
-
} else {
|
|
868
|
-
formatsToLoad = this.#supportedFormats;
|
|
869
|
-
}
|
|
870
|
-
for (const format of formatsToLoad) {
|
|
871
|
-
if (format !== void 0 && !this.#pipelines[format]) {
|
|
872
|
-
this.#loadPipeline(format).catch((err) => {
|
|
873
|
-
console.error(`Failed to preload pipeline for format ${format}:`, err);
|
|
874
|
-
});
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
709
|
}
|
|
878
710
|
}
|
|
879
|
-
async #
|
|
880
|
-
const
|
|
881
|
-
code: await
|
|
711
|
+
async #G() {
|
|
712
|
+
const e = this.#e.createShaderModule({
|
|
713
|
+
code: await k["utils.wgsl"](),
|
|
882
714
|
label: "utils"
|
|
883
715
|
});
|
|
884
|
-
if (typeof
|
|
885
|
-
const
|
|
886
|
-
if (
|
|
716
|
+
if (typeof e.compilationInfo == "function") {
|
|
717
|
+
const r = await e.compilationInfo();
|
|
718
|
+
if (r.messages.some((t) => t.type == "error")) {
|
|
887
719
|
console.error("WGSL compilation errors:");
|
|
888
|
-
for (const
|
|
889
|
-
console.error(
|
|
890
|
-
}
|
|
720
|
+
for (const t of r.messages)
|
|
721
|
+
console.error(t);
|
|
891
722
|
throw new Error("Shader compilation failed");
|
|
892
723
|
}
|
|
893
724
|
}
|
|
894
|
-
this.#
|
|
725
|
+
this.#c = await this.#e.createComputePipelineAsync({
|
|
895
726
|
layout: "auto",
|
|
896
727
|
compute: {
|
|
897
|
-
module:
|
|
728
|
+
module: e,
|
|
898
729
|
entryPoint: "mipmap"
|
|
899
730
|
}
|
|
900
|
-
})
|
|
901
|
-
this.#resizePipeline = await this.#device.createComputePipelineAsync({
|
|
731
|
+
}), this.#m = await this.#e.createComputePipelineAsync({
|
|
902
732
|
layout: "auto",
|
|
903
733
|
compute: {
|
|
904
|
-
module:
|
|
734
|
+
module: e,
|
|
905
735
|
entryPoint: "resize"
|
|
906
736
|
}
|
|
907
|
-
})
|
|
908
|
-
this.#flipYPipeline = await this.#device.createComputePipelineAsync({
|
|
737
|
+
}), this.#g = await this.#e.createComputePipelineAsync({
|
|
909
738
|
layout: "auto",
|
|
910
739
|
compute: {
|
|
911
|
-
module:
|
|
740
|
+
module: e,
|
|
912
741
|
entryPoint: "flipy"
|
|
913
742
|
}
|
|
914
|
-
})
|
|
915
|
-
this.#detectChannelCountPipeline = await this.#device.createComputePipelineAsync({
|
|
743
|
+
}), this.#u = await this.#e.createComputePipelineAsync({
|
|
916
744
|
layout: "auto",
|
|
917
745
|
compute: {
|
|
918
|
-
module:
|
|
746
|
+
module: e,
|
|
919
747
|
entryPoint: "detect_channel_count"
|
|
920
748
|
}
|
|
921
749
|
});
|
|
922
750
|
}
|
|
923
|
-
async #
|
|
924
|
-
if (this.#
|
|
925
|
-
return this.#
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
const shaderModule = this.#device.createShaderModule({
|
|
936
|
-
code: shaderCode,
|
|
937
|
-
label: SparkFormatName[format]
|
|
751
|
+
async #p(e) {
|
|
752
|
+
if (this.#i[e])
|
|
753
|
+
return this.#i[e];
|
|
754
|
+
const r = (async () => {
|
|
755
|
+
console.time("loadPipeline " + B[e]);
|
|
756
|
+
const t = q[e];
|
|
757
|
+
w(t, `No shader available for format ${B[e]}`);
|
|
758
|
+
let i = await k[t]();
|
|
759
|
+
this.#l || (i = i.replace(/^enable f16;\s*/m, "").replace(/\bf16\b/g, "f32").replace(/\bvec([234])h\b/g, "vec$1f").replace(/\bmat([234]x[234])h/g, "mat$1f").replace(/\b(\d*\.\d+|\d+\.)h\b/g, "$1"));
|
|
760
|
+
const a = this.#e.createShaderModule({
|
|
761
|
+
code: i,
|
|
762
|
+
label: B[e]
|
|
938
763
|
});
|
|
939
|
-
if (typeof
|
|
940
|
-
const
|
|
941
|
-
if (
|
|
764
|
+
if (typeof a.getCompilationInfo == "function") {
|
|
765
|
+
const c = await a.getCompilationInfo();
|
|
766
|
+
if (c.messages.some((u) => u.type == "error")) {
|
|
942
767
|
console.error("WGSL compilation errors:");
|
|
943
|
-
for (const
|
|
944
|
-
console.error(
|
|
945
|
-
}
|
|
768
|
+
for (const u of c.messages)
|
|
769
|
+
console.error(u);
|
|
946
770
|
throw new Error("Shader compilation failed");
|
|
947
771
|
}
|
|
948
772
|
}
|
|
949
|
-
const
|
|
773
|
+
const o = await this.#e.createComputePipelineAsync({
|
|
950
774
|
layout: "auto",
|
|
951
775
|
compute: {
|
|
952
|
-
module:
|
|
776
|
+
module: a,
|
|
953
777
|
entryPoint: "main"
|
|
954
778
|
}
|
|
955
779
|
});
|
|
956
|
-
console.timeEnd("loadPipeline " +
|
|
957
|
-
return pipeline;
|
|
780
|
+
return console.timeEnd("loadPipeline " + B[e]), o;
|
|
958
781
|
})();
|
|
959
|
-
this.#
|
|
960
|
-
return pipelinePromise;
|
|
782
|
+
return this.#i[e] = r, r;
|
|
961
783
|
}
|
|
962
|
-
#
|
|
963
|
-
return this.#
|
|
784
|
+
#t(e) {
|
|
785
|
+
return this.#o.has(e);
|
|
964
786
|
}
|
|
965
|
-
#
|
|
966
|
-
const
|
|
967
|
-
if (
|
|
968
|
-
return
|
|
969
|
-
|
|
970
|
-
const preferenceOrder = preferLowQuality ? [
|
|
787
|
+
#h(e, r) {
|
|
788
|
+
const t = T[e];
|
|
789
|
+
if (t != null && this.#t(t))
|
|
790
|
+
return t;
|
|
791
|
+
const i = r ? [
|
|
971
792
|
"bc4-r",
|
|
972
793
|
"eac-r",
|
|
973
794
|
"bc5-rg",
|
|
@@ -994,149 +815,106 @@ class Spark {
|
|
|
994
815
|
"astc-rgba",
|
|
995
816
|
"astc-4x4-rgba"
|
|
996
817
|
];
|
|
997
|
-
for (const
|
|
998
|
-
if (
|
|
999
|
-
return
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
818
|
+
for (const a of i)
|
|
819
|
+
if (a.includes(e) && this.#t(T[a]))
|
|
820
|
+
return T[a];
|
|
1002
821
|
}
|
|
1003
|
-
async #
|
|
1004
|
-
if (
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
if (
|
|
1008
|
-
if (this.#
|
|
1009
|
-
if (this.#
|
|
1010
|
-
} else if (
|
|
1011
|
-
if (this.#
|
|
1012
|
-
if (this.#
|
|
1013
|
-
if (this.#
|
|
1014
|
-
if (this.#
|
|
1015
|
-
} else if (
|
|
1016
|
-
if (this.#
|
|
1017
|
-
if (this.#
|
|
822
|
+
async #b(e, r) {
|
|
823
|
+
if (e.format == null)
|
|
824
|
+
e.format = "rgb";
|
|
825
|
+
else if (e.format == "auto") {
|
|
826
|
+
if (e.alpha) {
|
|
827
|
+
if (this.#t(s.BC7_RGBA)) return s.BC7_RGBA;
|
|
828
|
+
if (this.#t(s.ASTC_4x4_RGBA)) return s.ASTC_4x4_RGBA;
|
|
829
|
+
} else if (e.srgb) {
|
|
830
|
+
if (this.#t(s.BC7_RGB)) return s.BC7_RGB;
|
|
831
|
+
if (this.#t(s.ASTC_4x4_RGB)) return s.ASTC_4x4_RGB;
|
|
832
|
+
if (this.#t(s.BC1_RGB)) return s.BC1_RGB;
|
|
833
|
+
if (this.#t(s.ETC2_RGB)) return s.ETC2_RGB;
|
|
834
|
+
} else if (e.normal) {
|
|
835
|
+
if (this.#t(s.BC5_RG)) return s.BC5_RG;
|
|
836
|
+
if (this.#t(s.EAC_RG)) return s.EAC_RG;
|
|
1018
837
|
} else {
|
|
1019
|
-
let
|
|
1020
|
-
if (
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
}
|
|
1026
|
-
} else {
|
|
1027
|
-
const buffer = imageToByteArray(image);
|
|
1028
|
-
channelCount = this.#detectChannelCount(buffer);
|
|
838
|
+
let i;
|
|
839
|
+
if (r instanceof GPUTexture)
|
|
840
|
+
r.format == "r8unorm" || r.format == "r16unorm" ? i = 1 : r.format == "rg8unorm" || r.format == "rg16unorm" ? i = 2 : i = await this.#R(r);
|
|
841
|
+
else {
|
|
842
|
+
const a = Z(r);
|
|
843
|
+
i = this.#C(a);
|
|
1029
844
|
}
|
|
1030
|
-
if (
|
|
1031
|
-
if (this.#
|
|
1032
|
-
if (this.#
|
|
1033
|
-
} else if (
|
|
1034
|
-
if (this.#
|
|
1035
|
-
if (this.#
|
|
1036
|
-
if (this.#
|
|
1037
|
-
if (this.#
|
|
1038
|
-
} else if (
|
|
1039
|
-
if (this.#
|
|
1040
|
-
if (this.#
|
|
1041
|
-
} else if (
|
|
1042
|
-
if (this.#
|
|
1043
|
-
if (this.#
|
|
845
|
+
if (i == 4) {
|
|
846
|
+
if (this.#t(s.BC7_RGBA)) return s.BC7_RGBA;
|
|
847
|
+
if (this.#t(s.ASTC_4x4_RGBA)) return s.ASTC_4x4_RGBA;
|
|
848
|
+
} else if (i == 3) {
|
|
849
|
+
if (this.#t(s.BC7_RGB)) return s.BC7_RGB;
|
|
850
|
+
if (this.#t(s.ASTC_4x4_RGB)) return s.ASTC_4x4_RGB;
|
|
851
|
+
if (this.#t(s.BC1_RGB)) return s.BC1_RGB;
|
|
852
|
+
if (this.#t(s.ETC2_RGB)) return s.ETC2_RGB;
|
|
853
|
+
} else if (i == 2) {
|
|
854
|
+
if (this.#t(s.BC5_RG)) return s.BC5_RG;
|
|
855
|
+
if (this.#t(s.EAC_RG)) return s.EAC_RG;
|
|
856
|
+
} else if (i == 1) {
|
|
857
|
+
if (this.#t(s.BC4_R)) return s.BC4_R;
|
|
858
|
+
if (this.#t(s.EAC_R)) return s.EAC_R;
|
|
1044
859
|
}
|
|
1045
860
|
}
|
|
1046
861
|
throw new Error("No supported format found.");
|
|
1047
862
|
}
|
|
1048
|
-
const
|
|
1049
|
-
if (
|
|
1050
|
-
throw new Error(`Unsupported format: ${
|
|
1051
|
-
|
|
1052
|
-
return format;
|
|
863
|
+
const t = this.#h(e.format, e.preferLowQuality);
|
|
864
|
+
if (t === void 0)
|
|
865
|
+
throw new Error(`Unsupported format: ${e.format}`);
|
|
866
|
+
return t;
|
|
1053
867
|
}
|
|
1054
|
-
#
|
|
1055
|
-
let
|
|
1056
|
-
|
|
1057
|
-
let
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
const
|
|
1061
|
-
|
|
1062
|
-
const b = imageData[i + 2] / 255;
|
|
1063
|
-
const a = imageData[i + 3];
|
|
1064
|
-
if (a < 255) opaque = false;
|
|
1065
|
-
if (r != g || g != b) grayscale = false;
|
|
1066
|
-
const x = 2 * r - 1;
|
|
1067
|
-
const y = 2 * g - 1;
|
|
1068
|
-
const z = 2 * b - 1;
|
|
1069
|
-
const len2 = x * x + y * y + z * z;
|
|
1070
|
-
const len = Math.sqrt(len2);
|
|
1071
|
-
if (Math.abs(len - 1) > 0.2 || z < -0.1) invalidNormalCount += 1;
|
|
868
|
+
#C(e) {
|
|
869
|
+
let r = !0, t = !0, i = 0;
|
|
870
|
+
const a = Math.min(1024 * 128, e.length);
|
|
871
|
+
for (let o = 0; o < a; o += 4) {
|
|
872
|
+
const c = e[o] / 255, u = e[o + 1] / 255, f = e[o + 2] / 255;
|
|
873
|
+
e[o + 3] < 255 && (r = !1), (c != u || u != f) && (t = !1);
|
|
874
|
+
const d = 2 * c - 1, m = 2 * u - 1, h = 2 * f - 1, _ = d * d + m * m + h * h, G = Math.sqrt(_);
|
|
875
|
+
(Math.abs(G - 1) > 0.2 || h < -0.1) && (i += 1);
|
|
1072
876
|
}
|
|
1073
|
-
|
|
1074
|
-
if (grayscale) return 1;
|
|
1075
|
-
if (4 * 4 * invalidNormalCount < count) return 2;
|
|
1076
|
-
return 3;
|
|
877
|
+
return r ? t ? 1 : 16 * i < a ? 2 : 3 : 4;
|
|
1077
878
|
}
|
|
1078
|
-
async #
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
size: counterSize,
|
|
879
|
+
async #R(e) {
|
|
880
|
+
const t = this.#e.createBuffer({
|
|
881
|
+
size: 12,
|
|
1082
882
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
|
|
1083
|
-
})
|
|
1084
|
-
|
|
1085
|
-
size: counterSize,
|
|
883
|
+
}), i = this.#e.createBuffer({
|
|
884
|
+
size: 12,
|
|
1086
885
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
1087
|
-
})
|
|
1088
|
-
|
|
1089
|
-
layout: this.#detectChannelCountPipeline.getBindGroupLayout(0),
|
|
886
|
+
}), a = this.#e.createBindGroup({
|
|
887
|
+
layout: this.#u.getBindGroupLayout(0),
|
|
1090
888
|
entries: [
|
|
1091
|
-
{ binding: 0, resource:
|
|
1092
|
-
{ binding: 1, resource: { buffer:
|
|
889
|
+
{ binding: 0, resource: e.createView() },
|
|
890
|
+
{ binding: 1, resource: { buffer: t } }
|
|
1093
891
|
]
|
|
1094
|
-
});
|
|
1095
|
-
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
const dispatchX = Math.ceil(width / 8);
|
|
1101
|
-
const dispatchY = Math.ceil(height / 8);
|
|
1102
|
-
pass.dispatchWorkgroups(dispatchX, dispatchY);
|
|
1103
|
-
pass.end();
|
|
1104
|
-
encoder.copyBufferToBuffer(counterBuffer, 0, readbackBuffer, 0, counterSize);
|
|
1105
|
-
this.#device.queue.submit([encoder.finish()]);
|
|
1106
|
-
await this.#device.queue.onSubmittedWorkDone();
|
|
1107
|
-
await readbackBuffer.mapAsync(GPUMapMode.READ);
|
|
1108
|
-
const view = new Uint32Array(readbackBuffer.getMappedRange());
|
|
1109
|
-
const opaque = view[0] == 0;
|
|
1110
|
-
const grayscale = view[1] == 0;
|
|
1111
|
-
const invalidNormalCount = view[2];
|
|
1112
|
-
readbackBuffer.unmap();
|
|
1113
|
-
readbackBuffer.destroy();
|
|
1114
|
-
counterBuffer.destroy();
|
|
1115
|
-
if (!opaque) return 4;
|
|
1116
|
-
if (grayscale) return 1;
|
|
1117
|
-
if (4 * invalidNormalCount < width * height) return 2;
|
|
1118
|
-
return 3;
|
|
892
|
+
}), o = this.#e.createCommandEncoder(), c = o.beginComputePass();
|
|
893
|
+
c.setPipeline(this.#u), c.setBindGroup(0, a);
|
|
894
|
+
const { width: u, height: f } = e, l = Math.ceil(u / 8), d = Math.ceil(f / 8);
|
|
895
|
+
c.dispatchWorkgroups(l, d), c.end(), o.copyBufferToBuffer(t, 0, i, 0, 12), this.#e.queue.submit([o.finish()]), await this.#e.queue.onSubmittedWorkDone(), await i.mapAsync(GPUMapMode.READ);
|
|
896
|
+
const m = new Uint32Array(i.getMappedRange()), h = m[0] == 0, _ = m[1] == 0, G = m[2];
|
|
897
|
+
return i.unmap(), i.destroy(), t.destroy(), h ? _ ? 1 : 4 * G < u * f ? 2 : 3 : 4;
|
|
1119
898
|
}
|
|
1120
899
|
// Apply scaling and flipY transform.
|
|
1121
|
-
async #
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
layout: pipeline.getBindGroupLayout(0),
|
|
900
|
+
async #d(e, r, t, i, a, o, c) {
|
|
901
|
+
const u = e.beginComputePass(), f = c ? this.#g : this.#m;
|
|
902
|
+
u.setPipeline(f);
|
|
903
|
+
const l = this.#e.createBindGroup({
|
|
904
|
+
layout: f.getBindGroupLayout(0),
|
|
1127
905
|
entries: [
|
|
1128
906
|
{
|
|
1129
907
|
binding: 0,
|
|
1130
|
-
resource:
|
|
908
|
+
resource: r.createView({
|
|
1131
909
|
baseMipLevel: 0,
|
|
1132
910
|
mipLevelCount: 1,
|
|
1133
|
-
format:
|
|
911
|
+
format: o == 1 ? "rgba8unorm-srgb" : "rgba8unorm",
|
|
1134
912
|
usage: GPUTextureUsage.TEXTURE_BINDING
|
|
1135
913
|
})
|
|
1136
914
|
},
|
|
1137
915
|
{
|
|
1138
916
|
binding: 1,
|
|
1139
|
-
resource:
|
|
917
|
+
resource: t.createView({
|
|
1140
918
|
baseMipLevel: 0,
|
|
1141
919
|
mipLevelCount: 1,
|
|
1142
920
|
dimension: "2d",
|
|
@@ -1146,47 +924,41 @@ class Spark {
|
|
|
1146
924
|
},
|
|
1147
925
|
{
|
|
1148
926
|
binding: 2,
|
|
1149
|
-
resource: this.#
|
|
927
|
+
resource: this.#a
|
|
1150
928
|
},
|
|
1151
929
|
{
|
|
1152
930
|
binding: 3,
|
|
1153
|
-
resource: { buffer: this.#
|
|
931
|
+
resource: { buffer: this.#n[o] }
|
|
1154
932
|
}
|
|
1155
933
|
]
|
|
1156
934
|
});
|
|
1157
|
-
|
|
1158
|
-
pass.dispatchWorkgroups(Math.ceil(width / 8), Math.ceil(height / 8));
|
|
1159
|
-
pass.end();
|
|
935
|
+
u.setBindGroup(0, l), u.dispatchWorkgroups(Math.ceil(i / 8), Math.ceil(a / 8)), u.end();
|
|
1160
936
|
}
|
|
1161
|
-
async #
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
let
|
|
1165
|
-
let
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
h = Math.max(1, Math.floor(h / 2));
|
|
1169
|
-
this.#generateMipLevel(pass, texture, i, i + 1, w, h, colorMode);
|
|
1170
|
-
}
|
|
1171
|
-
pass.end();
|
|
937
|
+
async #T(e, r, t, i, a, o) {
|
|
938
|
+
const c = e.beginComputePass();
|
|
939
|
+
c.setPipeline(this.#c);
|
|
940
|
+
let u = i, f = a;
|
|
941
|
+
for (let l = 0; l < t - 1; l++)
|
|
942
|
+
u = Math.max(1, Math.floor(u / 2)), f = Math.max(1, Math.floor(f / 2)), this.#w(c, r, l, l + 1, u, f, o);
|
|
943
|
+
c.end();
|
|
1172
944
|
}
|
|
1173
|
-
#
|
|
1174
|
-
const
|
|
1175
|
-
layout: this.#
|
|
945
|
+
#w(e, r, t, i, a, o, c) {
|
|
946
|
+
const u = this.#e.createBindGroup({
|
|
947
|
+
layout: this.#c.getBindGroupLayout(0),
|
|
1176
948
|
entries: [
|
|
1177
949
|
{
|
|
1178
950
|
binding: 0,
|
|
1179
|
-
resource:
|
|
1180
|
-
baseMipLevel:
|
|
951
|
+
resource: r.createView({
|
|
952
|
+
baseMipLevel: t,
|
|
1181
953
|
mipLevelCount: 1,
|
|
1182
|
-
format:
|
|
954
|
+
format: c == 1 ? "rgba8unorm-srgb" : "rgba8unorm",
|
|
1183
955
|
usage: GPUTextureUsage.TEXTURE_BINDING
|
|
1184
956
|
})
|
|
1185
957
|
},
|
|
1186
958
|
{
|
|
1187
959
|
binding: 1,
|
|
1188
|
-
resource:
|
|
1189
|
-
baseMipLevel:
|
|
960
|
+
resource: r.createView({
|
|
961
|
+
baseMipLevel: i,
|
|
1190
962
|
mipLevelCount: 1,
|
|
1191
963
|
dimension: "2d",
|
|
1192
964
|
format: "rgba8unorm",
|
|
@@ -1195,18 +967,17 @@ class Spark {
|
|
|
1195
967
|
},
|
|
1196
968
|
{
|
|
1197
969
|
binding: 2,
|
|
1198
|
-
resource: this.#
|
|
970
|
+
resource: this.#a
|
|
1199
971
|
},
|
|
1200
972
|
{
|
|
1201
973
|
binding: 3,
|
|
1202
|
-
resource: { buffer: this.#
|
|
974
|
+
resource: { buffer: this.#n[c] }
|
|
1203
975
|
}
|
|
1204
976
|
]
|
|
1205
977
|
});
|
|
1206
|
-
|
|
1207
|
-
pass.dispatchWorkgroups(Math.ceil(width / 8), Math.ceil(height / 8));
|
|
978
|
+
e.setBindGroup(0, u), e.dispatchWorkgroups(Math.ceil(a / 8), Math.ceil(o / 8));
|
|
1208
979
|
}
|
|
1209
980
|
}
|
|
1210
981
|
export {
|
|
1211
|
-
Spark
|
|
982
|
+
L as Spark
|
|
1212
983
|
};
|