@ludicon/spark.js 0.1.0 → 0.1.2
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/dist/spark.esm.js +204 -185
- package/package.json +11 -3
- package/src/index.d.ts +301 -0
- package/src/three-gltf.d.ts +62 -0
- package/src/three-gltf.js +1 -1
package/dist/spark.esm.js
CHANGED
|
@@ -1,33 +1,43 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
)
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
const Z = {
|
|
2
|
+
"spark_astc_rgb.wgsl": () => import("./spark_astc_rgb-DaSIMKXW.js").then((s) => s.default),
|
|
3
|
+
"spark_astc_rgba.wgsl": () => import("./spark_astc_rgba-BToA2Rcq.js").then((s) => s.default),
|
|
4
|
+
"spark_bc1_rgb.wgsl": () => import("./spark_bc1_rgb-DZwuM1tO.js").then((s) => s.default),
|
|
5
|
+
"spark_bc4_r.wgsl": () => import("./spark_bc4_r-DjThizCH.js").then((s) => s.default),
|
|
6
|
+
"spark_bc5_rg.wgsl": () => import("./spark_bc5_rg-6bO0Gvo9.js").then((s) => s.default),
|
|
7
|
+
"spark_bc7_rgb.wgsl": () => import("./spark_bc7_rgb-FXpBw9fE.js").then((s) => s.default),
|
|
8
|
+
"spark_bc7_rgba.wgsl": () => import("./spark_bc7_rgba-C8Hi2pUY.js").then((s) => s.default),
|
|
9
|
+
"spark_eac_r.wgsl": () => import("./spark_eac_r-D8HGiglc.js").then((s) => s.default),
|
|
10
|
+
"spark_eac_rg.wgsl": () => import("./spark_eac_rg-DvsrLP9h.js").then((s) => s.default),
|
|
11
|
+
"spark_etc2_rgb.wgsl": () => import("./spark_etc2_rgb-C-j5FZpn.js").then((s) => s.default),
|
|
12
|
+
"utils.wgsl": () => import("./utils-D_dYVlAC.js").then((s) => s.default)
|
|
13
|
+
};
|
|
14
|
+
function O(s, t) {
|
|
15
|
+
if (!s)
|
|
6
16
|
throw new Error(t);
|
|
7
17
|
}
|
|
8
18
|
function he() {
|
|
9
|
-
const
|
|
10
|
-
if (
|
|
19
|
+
const s = navigator.userAgent;
|
|
20
|
+
if (s.includes("Chrome") || s.includes("Chromium"))
|
|
11
21
|
return null;
|
|
12
|
-
const t =
|
|
22
|
+
const t = s.match(/Safari\/(\d+(\.\d+)?)/);
|
|
13
23
|
return t && parseFloat(t[1]);
|
|
14
24
|
}
|
|
15
25
|
function J() {
|
|
16
|
-
const
|
|
17
|
-
return
|
|
26
|
+
const s = navigator.userAgent.match(/Firefox\/(\d+(\.\d+)?)/);
|
|
27
|
+
return s && parseFloat(s[1]);
|
|
18
28
|
}
|
|
19
|
-
function
|
|
20
|
-
return /\.svg(?:$|\?)/i.test(
|
|
29
|
+
function ge(s) {
|
|
30
|
+
return /\.svg(?:$|\?)/i.test(s) || /^data:image\/svg\+xml[,;]/i.test(s);
|
|
21
31
|
}
|
|
22
|
-
function ee(
|
|
32
|
+
function ee(s) {
|
|
23
33
|
return new Promise((t, r) => {
|
|
24
34
|
const e = new Image();
|
|
25
|
-
e.crossOrigin = "anonymous", e.decoding = "async", e.onload = () => t(e), e.onerror = r, e.src =
|
|
35
|
+
e.crossOrigin = "anonymous", e.decoding = "async", e.onload = () => t(e), e.onerror = r, e.src = s;
|
|
26
36
|
});
|
|
27
37
|
}
|
|
28
|
-
async function
|
|
29
|
-
const r = await fetch(
|
|
30
|
-
if (!r.ok) throw new Error(`HTTP ${r.status} for ${
|
|
38
|
+
async function pe(s, t = {}) {
|
|
39
|
+
const r = await fetch(s, { mode: "cors" });
|
|
40
|
+
if (!r.ok) throw new Error(`HTTP ${r.status} for ${s}`);
|
|
31
41
|
const e = await r.blob();
|
|
32
42
|
return createImageBitmap(e, {
|
|
33
43
|
imageOrientation: t.flipY ? "flipY" : "none",
|
|
@@ -35,17 +45,17 @@ async function _e(i, t = {}) {
|
|
|
35
45
|
premultiplyAlpha: "none"
|
|
36
46
|
});
|
|
37
47
|
}
|
|
38
|
-
const
|
|
39
|
-
async function
|
|
48
|
+
const K = he(), _e = 619.1, Te = K && K < _e;
|
|
49
|
+
async function Ee(s) {
|
|
40
50
|
const t = document.createElement("canvas");
|
|
41
|
-
return t.width =
|
|
51
|
+
return t.width = s.naturalWidth || s.width, t.height = s.naturalHeight || s.height, t.getContext("2d").drawImage(s, 0, 0), createImageBitmap(t);
|
|
42
52
|
}
|
|
43
|
-
async function
|
|
44
|
-
const r =
|
|
45
|
-
if (r &&
|
|
46
|
-
const e = await ee(
|
|
47
|
-
return
|
|
48
|
-
} else return r ||
|
|
53
|
+
async function Q(s, t = {}) {
|
|
54
|
+
const r = ge(s);
|
|
55
|
+
if (r && Te) {
|
|
56
|
+
const e = await ee(s);
|
|
57
|
+
return Ee(e);
|
|
58
|
+
} else return r || K ? ee(s) : pe(s, t);
|
|
49
59
|
}
|
|
50
60
|
const a = {
|
|
51
61
|
ASTC_4x4_RGB: 0,
|
|
@@ -113,7 +123,7 @@ const a = {
|
|
|
113
123
|
/* 17 */
|
|
114
124
|
"bc7-rgba"
|
|
115
125
|
// BC7_RGBA
|
|
116
|
-
],
|
|
126
|
+
], be = [
|
|
117
127
|
/* 0 */
|
|
118
128
|
"spark_astc_rgb.wgsl",
|
|
119
129
|
// ASTC_4x4_RGB
|
|
@@ -160,7 +170,7 @@ const a = {
|
|
|
160
170
|
/* 17 */
|
|
161
171
|
"spark_bc7_rgba.wgsl"
|
|
162
172
|
// BC7_RGBA
|
|
163
|
-
],
|
|
173
|
+
], Re = [
|
|
164
174
|
/* 0 */
|
|
165
175
|
16,
|
|
166
176
|
// ASTC_4x4_RGB
|
|
@@ -239,7 +249,7 @@ const a = {
|
|
|
239
249
|
"eac-rg11unorm": a.EAC_RG,
|
|
240
250
|
"astc-4x4-unorm": a.ASTC_4x4_RGBA,
|
|
241
251
|
"astc-4x4-unorm-srgb": a.ASTC_4x4_RGBA
|
|
242
|
-
}),
|
|
252
|
+
}), Be = [
|
|
243
253
|
/* 0 */
|
|
244
254
|
"astc-4x4-unorm",
|
|
245
255
|
// ASTC_4x4_RGB
|
|
@@ -286,7 +296,7 @@ const a = {
|
|
|
286
296
|
/* 17 */
|
|
287
297
|
"bc7-rgba-unorm"
|
|
288
298
|
// BC7_RGB
|
|
289
|
-
],
|
|
299
|
+
], xe = [
|
|
290
300
|
/* 0 */
|
|
291
301
|
!0,
|
|
292
302
|
// ASTC_4x4_RGB
|
|
@@ -334,48 +344,48 @@ const a = {
|
|
|
334
344
|
!0
|
|
335
345
|
// BC7_RGB
|
|
336
346
|
];
|
|
337
|
-
function
|
|
338
|
-
return
|
|
347
|
+
function Ge(s) {
|
|
348
|
+
return xe[s];
|
|
339
349
|
}
|
|
340
|
-
function Se(
|
|
341
|
-
return
|
|
350
|
+
function Se(s) {
|
|
351
|
+
return s == a.ASTC_4x4_RGBA || s == a.BC7_RGBA;
|
|
342
352
|
}
|
|
343
|
-
function
|
|
344
|
-
return typeof GPUDevice < "u" &&
|
|
353
|
+
function Ce(s) {
|
|
354
|
+
return typeof GPUDevice < "u" && s instanceof GPUDevice;
|
|
345
355
|
}
|
|
346
|
-
function
|
|
356
|
+
function Pe() {
|
|
347
357
|
return ["iPad Simulator", "iPhone Simulator", "iPod Simulator", "iPad", "iPhone", "iPod"].includes(navigator.platform) || // iPad on iOS 13 detection
|
|
348
358
|
navigator.userAgent.includes("Mac") && "ontouchend" in document;
|
|
349
359
|
}
|
|
350
|
-
function
|
|
360
|
+
function Ae(s) {
|
|
351
361
|
const t = /* @__PURE__ */ new Set(), r = {
|
|
352
362
|
"texture-compression-bc": [a.BC1_RG, a.BC4_R, a.BC5_RG, a.BC7_RGB, a.BC7_RGBA],
|
|
353
363
|
"texture-compression-etc2": [a.ETC2_RGB, a.EAC_R, a.EAC_RG],
|
|
354
364
|
"texture-compression-astc": [a.ASTC_4x4_RGB, a.ASTC_4x4_RGBA]
|
|
355
365
|
};
|
|
356
|
-
for (const [e,
|
|
357
|
-
if (
|
|
358
|
-
for (const o of
|
|
366
|
+
for (const [e, i] of Object.entries(r))
|
|
367
|
+
if (s.features.has(e))
|
|
368
|
+
for (const o of i)
|
|
359
369
|
t.add(o);
|
|
360
370
|
return t;
|
|
361
371
|
}
|
|
362
|
-
function
|
|
372
|
+
function Ue(s) {
|
|
363
373
|
const t = document.createElement("canvas");
|
|
364
|
-
t.width =
|
|
374
|
+
t.width = s.width, t.height = s.height;
|
|
365
375
|
const r = t.getContext("2d");
|
|
366
|
-
r.drawImage(
|
|
367
|
-
const e = r.getImageData(0, 0,
|
|
376
|
+
r.drawImage(s, 0, 0);
|
|
377
|
+
const e = r.getImageData(0, 0, s.width, s.height);
|
|
368
378
|
return new Uint8Array(e.data.buffer);
|
|
369
379
|
}
|
|
370
380
|
const te = 256, re = 4;
|
|
371
|
-
function
|
|
372
|
-
let
|
|
381
|
+
function we(s, t, r, e) {
|
|
382
|
+
let i = 0, o = 0;
|
|
373
383
|
const c = [];
|
|
374
384
|
do {
|
|
375
|
-
const n = Math.ceil(
|
|
376
|
-
|
|
377
|
-
} while (e && (
|
|
378
|
-
return { mipmapCount:
|
|
385
|
+
const n = Math.ceil(s / 4), f = Math.ceil(t / 4), u = Math.ceil(n * r / te) * te, l = f * u;
|
|
386
|
+
i++, c.push({ offset: o, alignedSize: l, w: s, h: t, bw: n, bh: f, bytesPerRow: u }), o += l, s = Math.max(1, Math.floor(s / 2)), t = Math.max(1, Math.floor(t / 2));
|
|
387
|
+
} while (e && (s >= re || t >= re));
|
|
388
|
+
return { mipmapCount: i, outputSize: o, bufferRanges: c };
|
|
379
389
|
}
|
|
380
390
|
class me {
|
|
381
391
|
#e;
|
|
@@ -432,7 +442,7 @@ class me {
|
|
|
432
442
|
#G(t) {
|
|
433
443
|
this.#b && console.time(t);
|
|
434
444
|
}
|
|
435
|
-
#
|
|
445
|
+
#S(t) {
|
|
436
446
|
this.#b && console.timeEnd(t);
|
|
437
447
|
}
|
|
438
448
|
/**
|
|
@@ -489,7 +499,7 @@ class me {
|
|
|
489
499
|
*/
|
|
490
500
|
static getRequiredFeatures(t) {
|
|
491
501
|
const r = [];
|
|
492
|
-
return !
|
|
502
|
+
return !Pe() && t.features.has("texture-compression-bc") && r.push("texture-compression-bc"), t.features.has("texture-compression-etc2") && r.push("texture-compression-etc2"), t.features.has("texture-compression-astc") && r.push("texture-compression-astc"), t.features.has("shader-f16") && r.push("shader-f16"), t.features.has("timestamp-query") && r.push("timestamp-query"), r;
|
|
493
503
|
}
|
|
494
504
|
/**
|
|
495
505
|
* Try to determine the best compression options automatically. Do not use this in production, this is
|
|
@@ -501,10 +511,10 @@ class me {
|
|
|
501
511
|
*/
|
|
502
512
|
async selectPreferredOptions(t, r = {}) {
|
|
503
513
|
if (r.format == null || r.format == "auto") {
|
|
504
|
-
const e = t instanceof Image || t instanceof ImageBitmap || t instanceof GPUTexture ? t : await
|
|
514
|
+
const e = t instanceof Image || t instanceof ImageBitmap || t instanceof GPUTexture ? t : await Q(t);
|
|
505
515
|
r.format = "auto";
|
|
506
|
-
const
|
|
507
|
-
r.format = N[
|
|
516
|
+
const i = await this.#A(r, e);
|
|
517
|
+
r.format = N[i], e instanceof GPUTexture && e.format.endsWith("-srgb") && (r.srgb = !0), (i == a.EAC_RG || i == a.BC5_RG) && (r.normal = !0);
|
|
508
518
|
}
|
|
509
519
|
return r;
|
|
510
520
|
}
|
|
@@ -554,21 +564,21 @@ class me {
|
|
|
554
564
|
*/
|
|
555
565
|
async encodeTexture(t, r = {}) {
|
|
556
566
|
O(this.#e, "Spark is not initialized");
|
|
557
|
-
const e = t instanceof Image || t instanceof ImageBitmap || t instanceof GPUTexture ? t : await
|
|
567
|
+
const e = t instanceof Image || t instanceof ImageBitmap || t instanceof GPUTexture ? t : await Q(t);
|
|
558
568
|
this.#x("Loaded image", e);
|
|
559
|
-
const
|
|
569
|
+
const i = await this.#A(r, e), o = this.#C(i), c = Math.ceil(e.width / 4) * 4, n = Math.ceil(e.height / 4) * 4, f = Re[i], u = r.generateMipmaps || r.mips, { mipmapCount: l, outputSize: h, bufferRanges: m } = we(c, n, f, u), R = (r.srgb || r.format?.endsWith("srgb")) && Ge(i);
|
|
560
570
|
let T = R ? A.sRGB : A.Linear;
|
|
561
|
-
Se(
|
|
562
|
-
const d =
|
|
571
|
+
Se(i) && (T |= A.Alpha), r.normal && (T = A.Normal);
|
|
572
|
+
const d = Be[i] + (R ? "-srgb" : ""), V = R ? ["rgba8unorm", "rgba8unorm-srgb"] : ["rgba8unorm"], x = this.#B++;
|
|
563
573
|
this.#G("create input texture #" + x);
|
|
564
574
|
let y = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
|
|
565
575
|
this.#h ? y |= GPUTextureUsage.RENDER_ATTACHMENT : y |= GPUTextureUsage.STORAGE_BINDING;
|
|
566
|
-
const
|
|
567
|
-
!
|
|
576
|
+
const C = r.flipY || c != e.width || n != e.height;
|
|
577
|
+
!C && !(e instanceof GPUTexture) && (y |= GPUTextureUsage.RENDER_ATTACHMENT);
|
|
568
578
|
const _ = this.#e.createCommandEncoder();
|
|
569
579
|
_.pushDebugGroup?.("spark process texture"), this.#n && typeof _.writeTimestamp == "function" && _.writeTimestamp(this.#n, 0);
|
|
570
580
|
let E;
|
|
571
|
-
if (
|
|
581
|
+
if (C || !(e instanceof GPUTexture && !u)) {
|
|
572
582
|
const g = !this.#t || !this.#o || this.#o.width < c || this.#o.height < n || this.#o.mipLevelCount < l;
|
|
573
583
|
this.#t && this.#o && !g ? E = this.#o : (this.#t && this.#o && this.#o.destroy(), E = this.#e.createTexture({
|
|
574
584
|
size: [c, n, 1],
|
|
@@ -579,7 +589,7 @@ class me {
|
|
|
579
589
|
}), this.#t && (this.#o = E));
|
|
580
590
|
}
|
|
581
591
|
let P;
|
|
582
|
-
if (
|
|
592
|
+
if (C)
|
|
583
593
|
if (e instanceof GPUTexture)
|
|
584
594
|
this.#U(_, e, E, c, n, R, r.flipY);
|
|
585
595
|
else {
|
|
@@ -595,19 +605,19 @@ class me {
|
|
|
595
605
|
}
|
|
596
606
|
else
|
|
597
607
|
e instanceof GPUTexture ? u ? _.copyTextureToTexture({ texture: e }, { texture: E }, { width: c, height: n }) : E = e : this.#e.queue.copyExternalImageToTexture({ source: e }, { texture: E }, { width: c, height: n });
|
|
598
|
-
u && this.#L(_, E, l, c, n, T, r.mipsAlphaScale, r.mipmapFilter), _.popDebugGroup?.(), this.#
|
|
608
|
+
u && this.#L(_, E, l, c, n, T, r.mipsAlphaScale, r.mipmapFilter), _.popDebugGroup?.(), this.#S("create input texture #" + x);
|
|
599
609
|
const W = r.outputTexture && r.outputTexture.width == c && r.outputTexture.height == n && r.outputTexture.mipLevelCount == l && r.outputTexture.format == d ? r.outputTexture : this.#e.createTexture({
|
|
600
610
|
size: [c, n, 1],
|
|
601
611
|
mipLevelCount: l,
|
|
602
612
|
format: d,
|
|
603
613
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
|
|
604
614
|
});
|
|
605
|
-
let
|
|
606
|
-
this.#t && this.#m && this.#m.size >= h ?
|
|
615
|
+
let S;
|
|
616
|
+
this.#t && this.#m && this.#m.size >= h ? S = this.#m : (this.#t && this.#m && this.#m.destroy(), S = this.#e.createBuffer({
|
|
607
617
|
size: h,
|
|
608
618
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
|
|
609
|
-
}), this.#t && (this.#m =
|
|
610
|
-
const k = `dispatch compute shader '${N[
|
|
619
|
+
}), this.#t && (this.#m = S));
|
|
620
|
+
const k = `dispatch compute shader '${N[i]}' #${x}`;
|
|
611
621
|
this.#G(k), _.pushDebugGroup?.("spark encode texture");
|
|
612
622
|
let b = {};
|
|
613
623
|
this.#n && typeof _.writeTimestamp != "function" && (b = {
|
|
@@ -637,7 +647,7 @@ class me {
|
|
|
637
647
|
{
|
|
638
648
|
binding: 2,
|
|
639
649
|
resource: {
|
|
640
|
-
buffer:
|
|
650
|
+
buffer: S,
|
|
641
651
|
offset: m[g].offset,
|
|
642
652
|
size: m[g].size
|
|
643
653
|
}
|
|
@@ -650,7 +660,7 @@ class me {
|
|
|
650
660
|
for (let g = 0; g < l; g++)
|
|
651
661
|
_.copyBufferToTexture(
|
|
652
662
|
{
|
|
653
|
-
buffer:
|
|
663
|
+
buffer: S,
|
|
654
664
|
offset: m[g].offset,
|
|
655
665
|
bytesPerRow: m[g].bytesPerRow,
|
|
656
666
|
rowsPerImage: m[g].bh
|
|
@@ -665,7 +675,7 @@ class me {
|
|
|
665
675
|
depthOrArrayLayers: 1
|
|
666
676
|
}
|
|
667
677
|
);
|
|
668
|
-
return this.#n && typeof _.writeTimestamp == "function" && _.writeTimestamp(this.#n, 1), _.popDebugGroup?.(), this.#e.queue.submit([_.finish()]), this.#
|
|
678
|
+
return this.#n && typeof _.writeTimestamp == "function" && _.writeTimestamp(this.#n, 1), _.popDebugGroup?.(), this.#e.queue.submit([_.finish()]), this.#S(k), this.#t || (P?.destroy(), E != e && E?.destroy(), S?.destroy()), W;
|
|
669
679
|
}
|
|
670
680
|
/**
|
|
671
681
|
* Returns the time (in milliseconds) it took to perform the most recent `encodeTexture()` call.
|
|
@@ -691,11 +701,11 @@ class me {
|
|
|
691
701
|
return 0;
|
|
692
702
|
const t = this.#e.createCommandEncoder();
|
|
693
703
|
t.resolveQuerySet(this.#n, 0, 2, this.#_, 0), t.copyBufferToBuffer(this.#_, 0, this.#u, 0, 16), this.#e.queue.submit([t.finish()]), await this.#e.queue.onSubmittedWorkDone(), await this.#u.mapAsync(GPUMapMode.READ);
|
|
694
|
-
const r = this.#u.getMappedRange(), e = new BigUint64Array(r),
|
|
695
|
-
return this.#u.unmap(), Number(o -
|
|
704
|
+
const r = this.#u.getMappedRange(), e = new BigUint64Array(r), i = e[0], o = e[1];
|
|
705
|
+
return this.#u.unmap(), Number(o - i) / 1e6;
|
|
696
706
|
}
|
|
697
|
-
async #w(t, r, e,
|
|
698
|
-
if (O(t, "device is required"), O(
|
|
707
|
+
async #w(t, r, e, i, o) {
|
|
708
|
+
if (O(t, "device is required"), O(Ce(t), "device is not a WebGPU device"), this.#e = t, this.#b = i, this.#t = o, this.#l = Ae(this.#e), this.#s = this.#e.createSampler({
|
|
699
709
|
magFilter: "linear",
|
|
700
710
|
minFilter: "linear"
|
|
701
711
|
}), this.#i = this.#e.createBuffer({
|
|
@@ -720,7 +730,7 @@ class me {
|
|
|
720
730
|
let c;
|
|
721
731
|
Array.isArray(r) ? c = r.map((n) => this.#P(n, !1)) : c = this.#l;
|
|
722
732
|
for (const n of c)
|
|
723
|
-
n !== void 0 && !this.#f[n] && this.#
|
|
733
|
+
n !== void 0 && !this.#f[n] && this.#C(n).catch((f) => {
|
|
724
734
|
console.error(`Failed to preload pipeline for format ${n}:`, f);
|
|
725
735
|
});
|
|
726
736
|
}
|
|
@@ -732,19 +742,19 @@ class me {
|
|
|
732
742
|
});
|
|
733
743
|
if (typeof t.compilationInfo == "function") {
|
|
734
744
|
const e = await t.compilationInfo();
|
|
735
|
-
if (e.messages.some((
|
|
745
|
+
if (e.messages.some((i) => i.type == "error")) {
|
|
736
746
|
console.error("WGSL compilation errors:");
|
|
737
|
-
for (const
|
|
738
|
-
console.error(
|
|
747
|
+
for (const i of e.messages)
|
|
748
|
+
console.error(i);
|
|
739
749
|
throw new Error("Shader compilation failed");
|
|
740
750
|
}
|
|
741
751
|
}
|
|
742
752
|
if (J() && (this.#h = !0), this.#h) {
|
|
743
753
|
this.#g = {}, this.#p = {}, this.#a = {};
|
|
744
754
|
const e = ["rgba8unorm-srgb", "rgba8unorm"];
|
|
745
|
-
for (const
|
|
746
|
-
this.#g[
|
|
747
|
-
label: `mipmap-pipeline-${
|
|
755
|
+
for (const i of e)
|
|
756
|
+
this.#g[i] = this.#e.createRenderPipeline({
|
|
757
|
+
label: `mipmap-pipeline-${i}`,
|
|
748
758
|
layout: "auto",
|
|
749
759
|
vertex: {
|
|
750
760
|
module: t,
|
|
@@ -753,14 +763,14 @@ class me {
|
|
|
753
763
|
fragment: {
|
|
754
764
|
module: t,
|
|
755
765
|
entryPoint: "mipmap_fs",
|
|
756
|
-
targets: [{ format:
|
|
766
|
+
targets: [{ format: i }]
|
|
757
767
|
},
|
|
758
768
|
primitive: {
|
|
759
769
|
topology: "triangle-strip",
|
|
760
770
|
stripIndexFormat: "uint32"
|
|
761
771
|
}
|
|
762
|
-
}), this.#p[
|
|
763
|
-
label: `resize-pipeline-${
|
|
772
|
+
}), this.#p[i] = this.#e.createRenderPipeline({
|
|
773
|
+
label: `resize-pipeline-${i}`,
|
|
764
774
|
layout: "auto",
|
|
765
775
|
vertex: {
|
|
766
776
|
module: t,
|
|
@@ -769,14 +779,14 @@ class me {
|
|
|
769
779
|
fragment: {
|
|
770
780
|
module: t,
|
|
771
781
|
entryPoint: "resize_fs",
|
|
772
|
-
targets: [{ format:
|
|
782
|
+
targets: [{ format: i }]
|
|
773
783
|
},
|
|
774
784
|
primitive: {
|
|
775
785
|
topology: "triangle-strip",
|
|
776
786
|
stripIndexFormat: "uint32"
|
|
777
787
|
}
|
|
778
|
-
}), this.#a[
|
|
779
|
-
label: `flip-y-pipeline-${
|
|
788
|
+
}), this.#a[i] = this.#e.createRenderPipeline({
|
|
789
|
+
label: `flip-y-pipeline-${i}`,
|
|
780
790
|
layout: "auto",
|
|
781
791
|
vertex: {
|
|
782
792
|
module: t,
|
|
@@ -785,7 +795,7 @@ class me {
|
|
|
785
795
|
fragment: {
|
|
786
796
|
module: t,
|
|
787
797
|
entryPoint: "flipy_fs",
|
|
788
|
-
targets: [{ format:
|
|
798
|
+
targets: [{ format: i }]
|
|
789
799
|
},
|
|
790
800
|
primitive: {
|
|
791
801
|
topology: "triangle-strip",
|
|
@@ -826,17 +836,17 @@ class me {
|
|
|
826
836
|
}
|
|
827
837
|
});
|
|
828
838
|
}
|
|
829
|
-
#
|
|
839
|
+
#C(t) {
|
|
830
840
|
if (this.#f[t])
|
|
831
841
|
return this.#f[t];
|
|
832
842
|
const r = (async () => {
|
|
833
843
|
this.#G("loadPipeline " + N[t]);
|
|
834
|
-
const e =
|
|
844
|
+
const e = be[t];
|
|
835
845
|
O(e, `No shader available for format ${N[t]}`);
|
|
836
|
-
let
|
|
837
|
-
this.#T || (
|
|
846
|
+
let i = await Z[e]();
|
|
847
|
+
this.#T || (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"));
|
|
838
848
|
const o = this.#e.createShaderModule({
|
|
839
|
-
code:
|
|
849
|
+
code: i,
|
|
840
850
|
label: N[t]
|
|
841
851
|
});
|
|
842
852
|
if (typeof o.getCompilationInfo == "function") {
|
|
@@ -855,7 +865,7 @@ class me {
|
|
|
855
865
|
entryPoint: "main"
|
|
856
866
|
}
|
|
857
867
|
});
|
|
858
|
-
return this.#
|
|
868
|
+
return this.#S("loadPipeline " + N[t]), c;
|
|
859
869
|
})();
|
|
860
870
|
return this.#f[t] = r, r;
|
|
861
871
|
}
|
|
@@ -866,8 +876,8 @@ class me {
|
|
|
866
876
|
const e = z[t];
|
|
867
877
|
if (e != null && this.#r(e))
|
|
868
878
|
return e;
|
|
869
|
-
const
|
|
870
|
-
for (const o of
|
|
879
|
+
const i = r ? ["bc4-r", "eac-r", "bc5-rg", "eac-rg", "bc1-rgb", "etc2-rgb", "bc7-rgb", "astc-rgb", "astc-4x4-rgb", "bc7-rgba", "astc-rgba", "astc-4x4-rgba"] : ["bc4-r", "eac-r", "bc5-rg", "eac-rg", "bc7-rgb", "astc-rgb", "astc-4x4-rgb", "bc1-rgb", "etc2-rgb", "bc7-rgba", "astc-rgba", "astc-4x4-rgba"];
|
|
880
|
+
for (const o of i)
|
|
871
881
|
if (o.includes(t) && this.#r(z[o]))
|
|
872
882
|
return z[o];
|
|
873
883
|
}
|
|
@@ -884,25 +894,25 @@ class me {
|
|
|
884
894
|
if (this.#r(a.BC5_RG)) return a.BC5_RG;
|
|
885
895
|
if (this.#r(a.EAC_RG)) return a.EAC_RG;
|
|
886
896
|
} else {
|
|
887
|
-
let
|
|
897
|
+
let i;
|
|
888
898
|
if (r instanceof GPUTexture)
|
|
889
|
-
r.format == "r8unorm" || r.format == "r16unorm" ?
|
|
899
|
+
r.format == "r8unorm" || r.format == "r16unorm" ? i = 1 : r.format == "rg8unorm" || r.format == "rg16unorm" ? i = 2 : i = await this.#F(r);
|
|
890
900
|
else {
|
|
891
|
-
const o =
|
|
892
|
-
|
|
901
|
+
const o = Ue(r);
|
|
902
|
+
i = this.#I(o);
|
|
893
903
|
}
|
|
894
|
-
if (
|
|
904
|
+
if (i == 4) {
|
|
895
905
|
if (this.#r(a.BC7_RGBA)) return a.BC7_RGBA;
|
|
896
906
|
if (this.#r(a.ASTC_4x4_RGBA)) return a.ASTC_4x4_RGBA;
|
|
897
|
-
} else if (
|
|
907
|
+
} else if (i == 3) {
|
|
898
908
|
if (this.#r(a.BC7_RGB)) return a.BC7_RGB;
|
|
899
909
|
if (this.#r(a.ASTC_4x4_RGB)) return a.ASTC_4x4_RGB;
|
|
900
910
|
if (this.#r(a.BC1_RGB)) return a.BC1_RGB;
|
|
901
911
|
if (this.#r(a.ETC2_RGB)) return a.ETC2_RGB;
|
|
902
|
-
} else if (
|
|
912
|
+
} else if (i == 2) {
|
|
903
913
|
if (this.#r(a.BC5_RG)) return a.BC5_RG;
|
|
904
914
|
if (this.#r(a.EAC_RG)) return a.EAC_RG;
|
|
905
|
-
} else if (
|
|
915
|
+
} else if (i == 1) {
|
|
906
916
|
if (this.#r(a.BC4_R)) return a.BC4_R;
|
|
907
917
|
if (this.#r(a.EAC_R)) return a.EAC_R;
|
|
908
918
|
}
|
|
@@ -915,21 +925,21 @@ class me {
|
|
|
915
925
|
return e;
|
|
916
926
|
}
|
|
917
927
|
#I(t) {
|
|
918
|
-
let r = !0, e = !0,
|
|
928
|
+
let r = !0, e = !0, i = 0;
|
|
919
929
|
const o = Math.min(1024 * 128, t.length);
|
|
920
930
|
for (let c = 0; c < o; c += 4) {
|
|
921
931
|
const n = t[c] / 255, f = t[c + 1] / 255, u = t[c + 2] / 255;
|
|
922
932
|
t[c + 3] < 255 && (r = !1), (n != f || f != u) && (e = !1);
|
|
923
933
|
const h = 2 * n - 1, m = 2 * f - 1, R = 2 * u - 1, T = h * h + m * m + R * R, d = Math.sqrt(T);
|
|
924
|
-
(Math.abs(d - 1) > 0.2 || R < -0.1) && (
|
|
934
|
+
(Math.abs(d - 1) > 0.2 || R < -0.1) && (i += 1);
|
|
925
935
|
}
|
|
926
|
-
return r ? e ? 1 : 16 *
|
|
936
|
+
return r ? e ? 1 : 16 * i < o ? 2 : 3 : 4;
|
|
927
937
|
}
|
|
928
938
|
async #F(t) {
|
|
929
939
|
const e = this.#e.createBuffer({
|
|
930
940
|
size: 12,
|
|
931
941
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
|
|
932
|
-
}),
|
|
942
|
+
}), i = this.#e.createBuffer({
|
|
933
943
|
size: 12,
|
|
934
944
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
935
945
|
}), o = this.#e.createBindGroup({
|
|
@@ -941,18 +951,18 @@ class me {
|
|
|
941
951
|
}), c = this.#e.createCommandEncoder(), n = c.beginComputePass();
|
|
942
952
|
n.setPipeline(this.#E), n.setBindGroup(0, o);
|
|
943
953
|
const { width: f, height: u } = t, l = Math.ceil(f / 8), h = Math.ceil(u / 8);
|
|
944
|
-
n.dispatchWorkgroups(l, h), n.end(), c.copyBufferToBuffer(e, 0,
|
|
945
|
-
const m = new Uint32Array(
|
|
946
|
-
return
|
|
954
|
+
n.dispatchWorkgroups(l, h), n.end(), c.copyBufferToBuffer(e, 0, i, 0, 12), this.#e.queue.submit([c.finish()]), await this.#e.queue.onSubmittedWorkDone(), await i.mapAsync(GPUMapMode.READ);
|
|
955
|
+
const m = new Uint32Array(i.getMappedRange()), R = m[0] == 0, T = m[1] == 0, d = m[2];
|
|
956
|
+
return i.unmap(), i.destroy(), e.destroy(), R ? T ? 1 : 4 * d < f * u ? 2 : 3 : 4;
|
|
947
957
|
}
|
|
948
958
|
#R(t, r, e) {
|
|
949
|
-
const
|
|
950
|
-
c.setUint32(0, t, !0), c.setFloat32(4,
|
|
959
|
+
const i = r && r.length > 0 ? e < r.length ? r[e] : r[r.length - 1] : 1, o = new ArrayBuffer(8), c = new DataView(o);
|
|
960
|
+
c.setUint32(0, t, !0), c.setFloat32(4, i, !0), this.#e.queue.writeBuffer(this.#i, 0, o);
|
|
951
961
|
}
|
|
952
962
|
// Apply scaling and flipY transform.
|
|
953
|
-
#U(t, r, e,
|
|
963
|
+
#U(t, r, e, i, o, c, n) {
|
|
954
964
|
if (this.#h) {
|
|
955
|
-
this.#M(t, r, e,
|
|
965
|
+
this.#M(t, r, e, i, o, c, n);
|
|
956
966
|
return;
|
|
957
967
|
}
|
|
958
968
|
this.#R(c);
|
|
@@ -990,10 +1000,10 @@ class me {
|
|
|
990
1000
|
}
|
|
991
1001
|
]
|
|
992
1002
|
});
|
|
993
|
-
f.setBindGroup(0, l), f.dispatchWorkgroups(Math.ceil(
|
|
1003
|
+
f.setBindGroup(0, l), f.dispatchWorkgroups(Math.ceil(i / 8), Math.ceil(o / 8)), f.end();
|
|
994
1004
|
}
|
|
995
1005
|
// Apply scaling and flipY transform.
|
|
996
|
-
#M(t, r, e,
|
|
1006
|
+
#M(t, r, e, i, o, c, n) {
|
|
997
1007
|
const f = (c & A.sRGB) != 0 ? "rgba8unorm-srgb" : "rgba8unorm", u = e.createView({
|
|
998
1008
|
baseMipLevel: 0,
|
|
999
1009
|
mipLevelCount: 1,
|
|
@@ -1037,9 +1047,9 @@ class me {
|
|
|
1037
1047
|
});
|
|
1038
1048
|
l.setBindGroup(0, m), l.draw(4, 1, 0, 0), l.end();
|
|
1039
1049
|
}
|
|
1040
|
-
async #L(t, r, e,
|
|
1050
|
+
async #L(t, r, e, i, o, c, n, f) {
|
|
1041
1051
|
n == null && this.#R(c);
|
|
1042
|
-
let u =
|
|
1052
|
+
let u = i, l = o;
|
|
1043
1053
|
if (this.#h)
|
|
1044
1054
|
for (let h = 0; h < e - 1; h++)
|
|
1045
1055
|
n != null && this.#R(c, n, h), u = Math.max(1, Math.floor(u / 2)), l = Math.max(1, Math.floor(l / 2)), this.#N(t, r, h, h + 1, u, l, c);
|
|
@@ -1051,14 +1061,14 @@ class me {
|
|
|
1051
1061
|
h.end();
|
|
1052
1062
|
}
|
|
1053
1063
|
}
|
|
1054
|
-
#D(t, r, e,
|
|
1064
|
+
#D(t, r, e, i, o, c, n, f) {
|
|
1055
1065
|
const u = this.#e.createBindGroup({
|
|
1056
1066
|
layout: r,
|
|
1057
1067
|
entries: [
|
|
1058
1068
|
{
|
|
1059
1069
|
binding: 0,
|
|
1060
1070
|
resource: e.createView({
|
|
1061
|
-
baseMipLevel:
|
|
1071
|
+
baseMipLevel: i,
|
|
1062
1072
|
mipLevelCount: 1,
|
|
1063
1073
|
format: (f & A.sRGB) != 0 ? "rgba8unorm-srgb" : "rgba8unorm",
|
|
1064
1074
|
usage: GPUTextureUsage.TEXTURE_BINDING
|
|
@@ -1086,9 +1096,9 @@ class me {
|
|
|
1086
1096
|
});
|
|
1087
1097
|
t.setBindGroup(0, u), t.dispatchWorkgroups(Math.ceil(c / 8), Math.ceil(n / 8));
|
|
1088
1098
|
}
|
|
1089
|
-
#N(t, r, e,
|
|
1099
|
+
#N(t, r, e, i, o, c, n) {
|
|
1090
1100
|
const f = (n & A.sRGB) != 0 ? "rgba8unorm-srgb" : "rgba8unorm", u = r.createView({
|
|
1091
|
-
baseMipLevel:
|
|
1101
|
+
baseMipLevel: i,
|
|
1092
1102
|
mipLevelCount: 1,
|
|
1093
1103
|
dimension: "2d",
|
|
1094
1104
|
format: f,
|
|
@@ -1127,9 +1137,18 @@ class me {
|
|
|
1127
1137
|
l.setPipeline(this.#g[f]), l.setBindGroup(0, h), l.draw(4, 1, 0, 0), l.end();
|
|
1128
1138
|
}
|
|
1129
1139
|
}
|
|
1130
|
-
const
|
|
1131
|
-
|
|
1132
|
-
)
|
|
1140
|
+
const ye = {
|
|
1141
|
+
"spark_astc_rgb.glsl": () => import("./spark_astc_rgb-CTmg8wnA.js").then((s) => s.default),
|
|
1142
|
+
"spark_astc_rgba.glsl": () => import("./spark_astc_rgba-CDMNWH6H.js").then((s) => s.default),
|
|
1143
|
+
"spark_bc1_rgb.glsl": () => import("./spark_bc1_rgb-C0pbbQPO.js").then((s) => s.default),
|
|
1144
|
+
"spark_bc4_r.glsl": () => import("./spark_bc4_r-YQ6i79H9.js").then((s) => s.default),
|
|
1145
|
+
"spark_bc5_rg.glsl": () => import("./spark_bc5_rg-BqzKgeMU.js").then((s) => s.default),
|
|
1146
|
+
"spark_bc7_rgb.glsl": () => import("./spark_bc7_rgb-DBkDHCzE.js").then((s) => s.default),
|
|
1147
|
+
"spark_bc7_rgba.glsl": () => import("./spark_bc7_rgba-DQUk89UX.js").then((s) => s.default),
|
|
1148
|
+
"spark_eac_r.glsl": () => import("./spark_eac_r-Dvy2qUxf.js").then((s) => s.default),
|
|
1149
|
+
"spark_eac_rg.glsl": () => import("./spark_eac_rg-DKLVL9co.js").then((s) => s.default),
|
|
1150
|
+
"spark_etc2_rgb.glsl": () => import("./spark_etc2_rgb-BpnNweVx.js").then((s) => s.default)
|
|
1151
|
+
}, p = {
|
|
1133
1152
|
ASTC_4x4_RGB: 0,
|
|
1134
1153
|
ASTC_4x4_RGBA: 1,
|
|
1135
1154
|
EAC_R: 4,
|
|
@@ -1177,7 +1196,7 @@ const Ie = /* @__PURE__ */ Object.assign({ "./spark_astc_rgb.glsl": () => import
|
|
|
1177
1196
|
"bc7-rgb",
|
|
1178
1197
|
/* 17 */
|
|
1179
1198
|
"bc7-rgba"
|
|
1180
|
-
],
|
|
1199
|
+
], Ie = [
|
|
1181
1200
|
/* 0 */
|
|
1182
1201
|
"spark_astc_rgb.glsl",
|
|
1183
1202
|
/* 1 */
|
|
@@ -1214,7 +1233,7 @@ const Ie = /* @__PURE__ */ Object.assign({ "./spark_astc_rgb.glsl": () => import
|
|
|
1214
1233
|
"spark_bc7_rgb.glsl",
|
|
1215
1234
|
/* 17 */
|
|
1216
1235
|
"spark_bc7_rgba.glsl"
|
|
1217
|
-
],
|
|
1236
|
+
], Fe = [
|
|
1218
1237
|
/* 0 */
|
|
1219
1238
|
16,
|
|
1220
1239
|
/* 1 */
|
|
@@ -1251,7 +1270,7 @@ const Ie = /* @__PURE__ */ Object.assign({ "./spark_astc_rgb.glsl": () => import
|
|
|
1251
1270
|
16,
|
|
1252
1271
|
/* 17 */
|
|
1253
1272
|
16
|
|
1254
|
-
],
|
|
1273
|
+
], Me = [
|
|
1255
1274
|
/* 0 */
|
|
1256
1275
|
!0,
|
|
1257
1276
|
// ASTC_4x4_RGB
|
|
@@ -1298,7 +1317,7 @@ const Ie = /* @__PURE__ */ Object.assign({ "./spark_astc_rgb.glsl": () => import
|
|
|
1298
1317
|
/* 17 */
|
|
1299
1318
|
!0
|
|
1300
1319
|
// BC7_RGBA
|
|
1301
|
-
], se = 37808, ie = 37840, ae = 36492, ne = 36493,
|
|
1320
|
+
], se = 37808, ie = 37840, ae = 36492, ne = 36493, Le = 33776, De = 35916, oe = 36283, ce = 36285, Ne = 37492, ve = 37493, ue = 37488, le = 37490, X = 36208, $ = 36214, Xe = [
|
|
1302
1321
|
/* 0 */
|
|
1303
1322
|
[se, ie],
|
|
1304
1323
|
/* 1 */
|
|
@@ -1312,13 +1331,13 @@ const Ie = /* @__PURE__ */ Object.assign({ "./spark_astc_rgb.glsl": () => import
|
|
|
1312
1331
|
/* 5 */
|
|
1313
1332
|
[le, le],
|
|
1314
1333
|
/* 6 */
|
|
1315
|
-
[
|
|
1334
|
+
[Ne, ve],
|
|
1316
1335
|
/* 7 */
|
|
1317
1336
|
null,
|
|
1318
1337
|
/* 8 */
|
|
1319
1338
|
null,
|
|
1320
1339
|
/* 9 */
|
|
1321
|
-
[
|
|
1340
|
+
[Le, De],
|
|
1322
1341
|
/* 10 */
|
|
1323
1342
|
null,
|
|
1324
1343
|
/* 11 */
|
|
@@ -1335,7 +1354,7 @@ const Ie = /* @__PURE__ */ Object.assign({ "./spark_astc_rgb.glsl": () => import
|
|
|
1335
1354
|
[ae, ne],
|
|
1336
1355
|
/* 17 */
|
|
1337
1356
|
[ae, ne]
|
|
1338
|
-
],
|
|
1357
|
+
], ke = [
|
|
1339
1358
|
/* 0 */
|
|
1340
1359
|
X,
|
|
1341
1360
|
/* 1 */
|
|
@@ -1386,17 +1405,17 @@ const Ie = /* @__PURE__ */ Object.assign({ "./spark_astc_rgb.glsl": () => import
|
|
|
1386
1405
|
"astc-rgb": p.ASTC_4x4_RGB,
|
|
1387
1406
|
"astc-rgba": p.ASTC_4x4_RGBA
|
|
1388
1407
|
});
|
|
1389
|
-
function
|
|
1408
|
+
function Oe(s, t = !1) {
|
|
1390
1409
|
const r = /* @__PURE__ */ new Set();
|
|
1391
1410
|
if (t) {
|
|
1392
|
-
const u =
|
|
1411
|
+
const u = s.getSupportedExtensions();
|
|
1393
1412
|
console.log("Available WebGL extensions:"), u && u.sort().forEach((l) => {
|
|
1394
1413
|
console.log(` ${l}`);
|
|
1395
1414
|
}), console.log(`Total: ${u ? u.length : 0} extensions`), console.log("");
|
|
1396
1415
|
}
|
|
1397
|
-
(
|
|
1398
|
-
const
|
|
1399
|
-
if ((
|
|
1416
|
+
(s.getExtension("EXT_texture_compression_bptc") || s.getExtension("WEBGL_texture_compression_bptc")) && (r.add(p.BC7_RGB), r.add(p.BC7_RGBA));
|
|
1417
|
+
const i = s.getExtension("WEBGL_compressed_texture_s3tc"), o = s.getExtension("WEBGL_compressed_texture_s3tc_srgb");
|
|
1418
|
+
if ((i || o) && r.add(p.BC1_RGB), s.getExtension("EXT_texture_compression_rgtc") && (r.add(p.BC4_R), r.add(p.BC5_RG)), s.getExtension("WEBGL_compressed_texture_etc") && (r.add(p.ETC2_RGB), r.add(p.EAC_R), r.add(p.EAC_RG)), s.getExtension("WEBGL_compressed_texture_astc") && (r.add(p.ASTC_4x4_RGB), r.add(p.ASTC_4x4_RGBA)), t) {
|
|
1400
1419
|
console.log("Supported compression formats:");
|
|
1401
1420
|
const u = Array.from(r).map((l) => v[l]).filter(Boolean);
|
|
1402
1421
|
u.forEach((l) => {
|
|
@@ -1405,10 +1424,10 @@ function We(i, t = !1) {
|
|
|
1405
1424
|
}
|
|
1406
1425
|
return r;
|
|
1407
1426
|
}
|
|
1408
|
-
async function
|
|
1409
|
-
const t =
|
|
1427
|
+
async function Ve(s) {
|
|
1428
|
+
const t = ye[s];
|
|
1410
1429
|
if (!t)
|
|
1411
|
-
throw new Error(`Shader not found: ${
|
|
1430
|
+
throw new Error(`Shader not found: ${s}`);
|
|
1412
1431
|
let r = await t();
|
|
1413
1432
|
return r = `#version 300 es
|
|
1414
1433
|
precision highp float;
|
|
@@ -1421,21 +1440,21 @@ void main() {
|
|
|
1421
1440
|
gl_Position = vec4(uv * 2.0 - 1.0, 0.0, 1.0);
|
|
1422
1441
|
}
|
|
1423
1442
|
`;
|
|
1424
|
-
function q(
|
|
1425
|
-
const
|
|
1426
|
-
if (
|
|
1427
|
-
const o =
|
|
1428
|
-
throw
|
|
1443
|
+
function q(s, t, r, e) {
|
|
1444
|
+
const i = s.createShader(t);
|
|
1445
|
+
if (s.shaderSource(i, r), s.compileShader(i), e && !s.getShaderParameter(i, s.COMPILE_STATUS)) {
|
|
1446
|
+
const o = s.getShaderInfoLog(i);
|
|
1447
|
+
throw s.deleteShader(i), new Error(`Shader compilation failed: ${o}`);
|
|
1429
1448
|
}
|
|
1430
|
-
return
|
|
1449
|
+
return i;
|
|
1431
1450
|
}
|
|
1432
|
-
function fe(
|
|
1433
|
-
const
|
|
1434
|
-
if (
|
|
1435
|
-
const o =
|
|
1436
|
-
throw
|
|
1451
|
+
function fe(s, t, r, e) {
|
|
1452
|
+
const i = s.createProgram();
|
|
1453
|
+
if (s.attachShader(i, t), s.attachShader(i, r), s.linkProgram(i), e && !s.getProgramParameter(i, s.LINK_STATUS)) {
|
|
1454
|
+
const o = s.getProgramInfoLog(i);
|
|
1455
|
+
throw s.deleteProgram(i), new Error(`Program linking failed: ${o}`);
|
|
1437
1456
|
}
|
|
1438
|
-
return
|
|
1457
|
+
return i;
|
|
1439
1458
|
}
|
|
1440
1459
|
class de {
|
|
1441
1460
|
#e;
|
|
@@ -1459,7 +1478,7 @@ class de {
|
|
|
1459
1478
|
constructor(t, r = {}) {
|
|
1460
1479
|
if (!t)
|
|
1461
1480
|
throw new Error("WebGL2 context is required");
|
|
1462
|
-
this.#e = t, this.#h = r.verbose ?? !1, this.#f = r.validateShaders ?? !1, this.#p = r.cacheTempResources ?? !1, this.#l =
|
|
1481
|
+
this.#e = t, this.#h = r.verbose ?? !1, this.#f = r.validateShaders ?? !1, this.#p = r.cacheTempResources ?? !1, this.#l = Oe(t, this.#h), r.preload && this.#m(r.preload);
|
|
1463
1482
|
}
|
|
1464
1483
|
dispose() {
|
|
1465
1484
|
const t = this.#e;
|
|
@@ -1493,8 +1512,8 @@ class de {
|
|
|
1493
1512
|
let r;
|
|
1494
1513
|
Array.isArray(t) ? r = t.map((e) => this.#b(e, !1)) : r = this.#l;
|
|
1495
1514
|
for (const e of r)
|
|
1496
|
-
e !== void 0 && !this.#T[e] && this.#x(e).catch((
|
|
1497
|
-
console.error(`Failed to preload program for format ${v[e]}:`,
|
|
1515
|
+
e !== void 0 && !this.#T[e] && this.#x(e).catch((i) => {
|
|
1516
|
+
console.error(`Failed to preload program for format ${v[e]}:`, i);
|
|
1498
1517
|
});
|
|
1499
1518
|
}
|
|
1500
1519
|
getSupportedFormats() {
|
|
@@ -1519,8 +1538,8 @@ class de {
|
|
|
1519
1538
|
const e = H[t];
|
|
1520
1539
|
if (e != null && this.#B(e))
|
|
1521
1540
|
return e;
|
|
1522
|
-
const
|
|
1523
|
-
for (const o of
|
|
1541
|
+
const i = r ? ["bc4-r", "eac-r", "bc5-rg", "eac-rg", "bc1-rgb", "etc2-rgb", "bc7-rgb", "astc-rgb", "astc-4x4-rgb", "bc7-rgba", "astc-rgba", "astc-4x4-rgba"] : ["bc4-r", "eac-r", "bc5-rg", "eac-rg", "bc7-rgb", "astc-rgb", "astc-4x4-rgb", "bc1-rgb", "etc2-rgb", "bc7-rgba", "astc-rgba", "astc-4x4-rgba"];
|
|
1542
|
+
for (const o of i)
|
|
1524
1543
|
if (o.includes(t) && this.#B(H[o]))
|
|
1525
1544
|
return H[o];
|
|
1526
1545
|
}
|
|
@@ -1530,18 +1549,18 @@ class de {
|
|
|
1530
1549
|
const r = (async () => {
|
|
1531
1550
|
const e = "Loading program for format: " + v[t];
|
|
1532
1551
|
this.#o(e);
|
|
1533
|
-
const
|
|
1534
|
-
this.#d || (this.#d = q(
|
|
1535
|
-
const c = await
|
|
1536
|
-
return
|
|
1552
|
+
const i = this.#e, o = Ie[t];
|
|
1553
|
+
this.#d || (this.#d = q(i, i.VERTEX_SHADER, Y, this.#f));
|
|
1554
|
+
const c = await Ve(o), n = q(i, i.FRAGMENT_SHADER, c, this.#f), f = fe(i, this.#d, n, this.#f);
|
|
1555
|
+
return i.deleteShader(n), this.#c(e), f;
|
|
1537
1556
|
})();
|
|
1538
1557
|
return this.#T[t] = r, r;
|
|
1539
1558
|
}
|
|
1540
1559
|
async encodeTexture(t, r = {}) {
|
|
1541
1560
|
const e = this.#e;
|
|
1542
|
-
typeof t == "string" && (t = await
|
|
1543
|
-
const
|
|
1544
|
-
O(
|
|
1561
|
+
typeof t == "string" && (t = await Q(t)), this.#t(`Image type: ${t.constructor.name}`);
|
|
1562
|
+
const i = t.width || t.videoWidth, o = t.height || t.videoHeight;
|
|
1563
|
+
O(i && o);
|
|
1545
1564
|
const c = r.format ?? "rgb";
|
|
1546
1565
|
let n;
|
|
1547
1566
|
if (typeof c == "string") {
|
|
@@ -1551,7 +1570,7 @@ class de {
|
|
|
1551
1570
|
throw new Error(`Format not supported: ${v[n]}`);
|
|
1552
1571
|
const f = await this.#x(n);
|
|
1553
1572
|
this.#t(`Selected format: ${v[n]}`);
|
|
1554
|
-
const u =
|
|
1573
|
+
const u = Fe[n], l = (r.srgb || r.format?.endsWith("srgb")) && Me[n], h = Xe[n], m = h ? l ? h[1] : h[0] : null, R = ke[n];
|
|
1555
1574
|
this.#t(`Using ${l ? "sRGB" : "linear"} color space`);
|
|
1556
1575
|
const T = `encodeTexture #${++this.#g}`;
|
|
1557
1576
|
this.#o(T);
|
|
@@ -1588,19 +1607,19 @@ class de {
|
|
|
1588
1607
|
break;
|
|
1589
1608
|
}
|
|
1590
1609
|
const y = r.generateMipmaps || r.mips;
|
|
1591
|
-
let
|
|
1610
|
+
let C = 1;
|
|
1592
1611
|
if (y) {
|
|
1593
|
-
let w =
|
|
1612
|
+
let w = i, I = o;
|
|
1594
1613
|
for (; w > 4 || I > 4; )
|
|
1595
|
-
|
|
1614
|
+
C++, w = Math.max(1, Math.floor(w / 2)), I = Math.max(1, Math.floor(I / 2));
|
|
1596
1615
|
}
|
|
1597
1616
|
const _ = e.createTexture();
|
|
1598
|
-
e.bindTexture(e.TEXTURE_2D, _), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, x), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, x), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAX_LEVEL, 0), e.texImage2D(e.TEXTURE_2D, 0, e.RGBA8,
|
|
1617
|
+
e.bindTexture(e.TEXTURE_2D, _), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, x), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, x), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAX_LEVEL, 0), e.texImage2D(e.TEXTURE_2D, 0, e.RGBA8, i, o, 0, e.RGBA, e.UNSIGNED_BYTE, t);
|
|
1599
1618
|
let E = _;
|
|
1600
1619
|
if (r.flipY) {
|
|
1601
1620
|
this.#t("Flipping texture vertically");
|
|
1602
1621
|
const G = e.createTexture();
|
|
1603
|
-
e.bindTexture(e.TEXTURE_2D, G), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, x), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, x), e.texStorage2D(e.TEXTURE_2D, 1, e.RGBA8,
|
|
1622
|
+
e.bindTexture(e.TEXTURE_2D, G), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, x), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, x), e.texStorage2D(e.TEXTURE_2D, 1, e.RGBA8, i, o);
|
|
1604
1623
|
const w = e.createFramebuffer();
|
|
1605
1624
|
e.bindFramebuffer(e.FRAMEBUFFER, w), e.framebufferTexture2D(e.FRAMEBUFFER, e.COLOR_ATTACHMENT0, e.TEXTURE_2D, G, 0), this.#d || (this.#d = q(e, e.VERTEX_SHADER, Y, this.#f));
|
|
1606
1625
|
const I = `#version 300 es
|
|
@@ -1618,34 +1637,34 @@ class de {
|
|
|
1618
1637
|
const M = e.createShader(e.FRAGMENT_SHADER);
|
|
1619
1638
|
e.shaderSource(M, I), e.compileShader(M);
|
|
1620
1639
|
const D = fe(e, F, M, this.#f);
|
|
1621
|
-
e.useProgram(D), e.viewport(0, 0,
|
|
1640
|
+
e.useProgram(D), e.viewport(0, 0, i, o), e.activeTexture(e.TEXTURE0), e.bindTexture(e.TEXTURE_2D, _), e.uniform1i(e.getUniformLocation(D, "uTexture"), 0), e.uniform2i(e.getUniformLocation(D, "uTextureSize"), i, o), e.drawArrays(e.TRIANGLES, 0, 3), e.deleteShader(F), e.deleteShader(M), e.deleteProgram(D), e.deleteFramebuffer(w), e.deleteTexture(_), E = G;
|
|
1622
1641
|
}
|
|
1623
|
-
y && (e.bindTexture(e.TEXTURE_2D, E), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.LINEAR_MIPMAP_LINEAR), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAX_LEVEL,
|
|
1642
|
+
y && (e.bindTexture(e.TEXTURE_2D, E), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.LINEAR_MIPMAP_LINEAR), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAX_LEVEL, C - 1), e.generateMipmap(e.TEXTURE_2D), this.#t(`Generated ${C} mipmap levels`));
|
|
1624
1643
|
const P = e.createTexture();
|
|
1625
|
-
e.bindTexture(e.TEXTURE_2D, P), e.texStorage2D(e.TEXTURE_2D,
|
|
1626
|
-
const
|
|
1627
|
-
let k =
|
|
1644
|
+
e.bindTexture(e.TEXTURE_2D, P), e.texStorage2D(e.TEXTURE_2D, C, m, i, o), y ? (e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.LINEAR_MIPMAP_LINEAR), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.LINEAR)) : (e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.LINEAR), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.LINEAR)), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, x), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, x);
|
|
1645
|
+
const j = Math.ceil(i / 4), W = Math.ceil(o / 4), S = u * j * W;
|
|
1646
|
+
let k = S;
|
|
1628
1647
|
const b = this.#p;
|
|
1629
1648
|
let U;
|
|
1630
|
-
b && this.#a && this.#E >=
|
|
1649
|
+
b && this.#a && this.#E >= S ? U = this.#a : (b && this.#a && e.deleteBuffer(this.#a), U = e.createBuffer(), b && (this.#a = U, this.#E = S)), e.bindBuffer(e.PIXEL_PACK_BUFFER, U), e.bindBuffer(e.PIXEL_UNPACK_BUFFER, U), e.bufferData(e.PIXEL_PACK_BUFFER, S, e.STREAM_COPY);
|
|
1631
1650
|
let B;
|
|
1632
|
-
const g = !b || this.#n <
|
|
1633
|
-
u === 8 ? b && this.#s && !g ? B = this.#s : (b && this.#s && e.deleteTexture(this.#s), B = e.createTexture(), e.bindTexture(e.TEXTURE_2D, B), e.texStorage2D(e.TEXTURE_2D, 1, R,
|
|
1651
|
+
const g = !b || this.#n < i || this.#_ < o;
|
|
1652
|
+
u === 8 ? b && this.#s && !g ? B = this.#s : (b && this.#s && e.deleteTexture(this.#s), B = e.createTexture(), e.bindTexture(e.TEXTURE_2D, B), e.texStorage2D(e.TEXTURE_2D, 1, R, i, o), b && (this.#s = B, this.#n = i, this.#_ = o)) : b && this.#i && !g ? B = this.#i : (b && this.#i && e.deleteTexture(this.#i), B = e.createTexture(), e.bindTexture(e.TEXTURE_2D, B), e.texStorage2D(e.TEXTURE_2D, 1, R, i, o), b && (this.#i = B, this.#n = i, this.#_ = o));
|
|
1634
1653
|
let L;
|
|
1635
1654
|
b && this.#u ? L = this.#u : (L = e.createFramebuffer(), b && (this.#u = L)), e.bindFramebuffer(e.FRAMEBUFFER, L), e.readBuffer(e.COLOR_ATTACHMENT0), e.framebufferTexture2D(e.FRAMEBUFFER, e.COLOR_ATTACHMENT0, e.TEXTURE_2D, B, 0), e.useProgram(f);
|
|
1636
|
-
for (let G = 0; G <
|
|
1637
|
-
const w = Math.max(1, Math.floor(
|
|
1655
|
+
for (let G = 0; G < C; G++) {
|
|
1656
|
+
const w = Math.max(1, Math.floor(i >> G)), I = Math.max(1, Math.floor(o >> G)), F = Math.ceil(w / 4), M = Math.ceil(I / 4), D = u * F * M;
|
|
1638
1657
|
k += D, e.bindTexture(e.TEXTURE_2D, E), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_BASE_LEVEL, G), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAX_LEVEL, G), e.viewport(0, 0, F, M), e.drawArrays(e.TRIANGLES, 0, 3), e.readPixels(0, 0, F, M, e.RGBA_INTEGER, u === 16 ? e.UNSIGNED_INT : e.UNSIGNED_SHORT, 0), e.bindTexture(e.TEXTURE_2D, P), e.compressedTexSubImage2D(e.TEXTURE_2D, G, 0, 0, w, I, m, D, 0);
|
|
1639
1658
|
}
|
|
1640
1659
|
return b || (e.deleteTexture(B), e.deleteBuffer(U), e.deleteFramebuffer(L)), e.deleteTexture(E), e.bindFramebuffer(e.FRAMEBUFFER, d.framebuffer), e.bindFramebuffer(e.READ_FRAMEBUFFER, d.readFramebuffer), e.bindTexture(e.TEXTURE_2D, d.textureBinding), e.useProgram(d.program), e.activeTexture(d.activeTexture), e.viewport(d.viewport[0], d.viewport[1], d.viewport[2], d.viewport[3]), e.bindBuffer(e.PIXEL_PACK_BUFFER, d.pixelPackBuffer), e.bindBuffer(e.PIXEL_UNPACK_BUFFER, d.pixelUnpackBuffer), d.blend ? e.enable(e.BLEND) : e.disable(e.BLEND), d.depthTest ? e.enable(e.DEPTH_TEST) : e.disable(e.DEPTH_TEST), d.stencilTest ? e.enable(e.STENCIL_TEST) : e.disable(e.STENCIL_TEST), d.cullFace ? e.enable(e.CULL_FACE) : e.disable(e.CULL_FACE), d.scissorTest ? e.enable(e.SCISSOR_TEST) : e.disable(e.SCISSOR_TEST), e.bindBuffer(e.ARRAY_BUFFER, d.arrayBuffer), e.bindVertexArray(d.vertexArray), this.#c(T), {
|
|
1641
1660
|
texture: P,
|
|
1642
|
-
width:
|
|
1661
|
+
width: i,
|
|
1643
1662
|
height: o,
|
|
1644
1663
|
format: m,
|
|
1645
1664
|
sparkFormat: n,
|
|
1646
1665
|
sparkFormatName: v[n],
|
|
1647
1666
|
srgb: l,
|
|
1648
|
-
mipmapCount:
|
|
1667
|
+
mipmapCount: C,
|
|
1649
1668
|
byteLength: k
|
|
1650
1669
|
};
|
|
1651
1670
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ludicon/spark.js",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Real-Time GPU Texture Codecs for the Web",
|
|
5
5
|
"main": "dist/spark.esm.js",
|
|
6
6
|
"module": "dist/spark.esm.js",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"types": "src/index.d.ts",
|
|
8
9
|
"exports": {
|
|
9
10
|
".": {
|
|
10
|
-
"import": "./dist/spark.esm.js"
|
|
11
|
+
"import": "./dist/spark.esm.js",
|
|
12
|
+
"types": "./src/index.d.ts"
|
|
11
13
|
},
|
|
12
|
-
"./three-gltf":
|
|
14
|
+
"./three-gltf": {
|
|
15
|
+
"import": "./src/three-gltf.js",
|
|
16
|
+
"types": "./src/three-gltf.d.ts"
|
|
17
|
+
}
|
|
13
18
|
},
|
|
14
19
|
"files": [
|
|
15
20
|
"dist",
|
|
16
21
|
"src/three-gltf.js",
|
|
22
|
+
"src/three-gltf.d.ts",
|
|
23
|
+
"src/index.d.ts",
|
|
17
24
|
"README.md",
|
|
18
25
|
"LICENSE"
|
|
19
26
|
],
|
|
@@ -28,6 +35,7 @@
|
|
|
28
35
|
"lint:fix": "eslint . --fix",
|
|
29
36
|
"format": "prettier --write \"src/**/*.{js,jsx}\"",
|
|
30
37
|
"validate-shaders": "for shader in src/shaders/*.wgsl; do echo \"Validating $shader...\"; tint \"$shader\" --validate > /dev/null || exit 1; done && echo \"All shaders validated successfully!\"",
|
|
38
|
+
"analyze-bundle": "node scripts/analyze-bundle-size.js",
|
|
31
39
|
"prepublishOnly": "npm run validate-shaders && npm run build",
|
|
32
40
|
"clean": "rimraf dist"
|
|
33
41
|
},
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// Type definitions for @ludicon/spark.js
|
|
2
|
+
// Project: https://github.com/ludicon/spark.js
|
|
3
|
+
// Definitions by: Ludicon LLC
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for initializing Spark (WebGPU) encoder
|
|
7
|
+
*/
|
|
8
|
+
export interface SparkCreateOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Whether to preload all encoder pipelines or an array of format names to preload.
|
|
11
|
+
* Pipelines that are not preloaded are compiled on-demand when first used.
|
|
12
|
+
* @default false
|
|
13
|
+
*/
|
|
14
|
+
preload?: boolean | string[]
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Whether to cache temporary resources for reuse across encodeTexture calls.
|
|
18
|
+
* Improves performance when encoding multiple textures, but uses more GPU memory.
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
cacheTempResources?: boolean
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Enable verbose logging for debugging.
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
verbose?: boolean
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Enable GPU timestamp queries for performance profiling.
|
|
31
|
+
* Requires `timestamp-query` feature and enabling unsafe WebGPU features in the browser.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
useTimestampQueries?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Options for encoding textures with Spark
|
|
39
|
+
*/
|
|
40
|
+
export interface SparkEncodeOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Desired block compression format. Can be specified in several ways:
|
|
43
|
+
* - Channel mask: "r", "rg", "rgb", "rgba" - Auto-selects the best format based on device capabilities
|
|
44
|
+
* - Explicit format: "bc1-rgb", "bc7-rgba", "astc-4x4-rgb", "etc2-rgb", "eac-r", etc.
|
|
45
|
+
* - Substring: "bc1", "bc7", "astc", "etc2" - Chooses the first matching format
|
|
46
|
+
* - Auto-detect: "auto" - Analyzes image to determine channel count (WebGPU only, has overhead)
|
|
47
|
+
* @default "rgb"
|
|
48
|
+
*/
|
|
49
|
+
format?: string
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Hint for the automatic format selector. When the input format is "rgb" it chooses
|
|
53
|
+
* 8 bit per block formats like "bc1" or "etc2" instead of "bc7" or "astc".
|
|
54
|
+
* @default false
|
|
55
|
+
*/
|
|
56
|
+
preferLowQuality?: boolean
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Whether to generate mipmaps.
|
|
60
|
+
* @default false
|
|
61
|
+
*/
|
|
62
|
+
mips?: boolean
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Alias for mips. Whether to generate mipmaps.
|
|
66
|
+
* @default false
|
|
67
|
+
*/
|
|
68
|
+
generateMipmaps?: boolean
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The filter to use for mipmap generation:
|
|
72
|
+
* - "box" - Simple 2x2 box filter
|
|
73
|
+
* - "magic" - Higher quality 4x4 filter with sharpening properties
|
|
74
|
+
* @default "magic"
|
|
75
|
+
*/
|
|
76
|
+
mipmapFilter?: "box" | "magic"
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Optional array of alpha scale values to apply to each generated mipmap level.
|
|
80
|
+
* The array should contain one value per mipmap level (starting with mip level 1).
|
|
81
|
+
* Each value multiplies the alpha channel of the corresponding mipmap level.
|
|
82
|
+
* Values greater than 1.0 increase opacity, while values less than 1.0 increase transparency.
|
|
83
|
+
* If the array is shorter than the number of mipmap levels, the last value is used for remaining levels.
|
|
84
|
+
* Only applies when mips is true.
|
|
85
|
+
*/
|
|
86
|
+
mipsAlphaScale?: number[]
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Whether to encode the image using an sRGB format.
|
|
90
|
+
* This also affects mipmap generation. The srgb mode can also be inferred from the format.
|
|
91
|
+
* @default false
|
|
92
|
+
*/
|
|
93
|
+
srgb?: boolean
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Whether to interpret the image as a normal map.
|
|
97
|
+
* This affects automatic format selection favoring the use of "bc5" and "eac-rg" formats.
|
|
98
|
+
* @default false
|
|
99
|
+
*/
|
|
100
|
+
normal?: boolean
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Whether to vertically flip the image before encoding.
|
|
104
|
+
* @default false
|
|
105
|
+
*/
|
|
106
|
+
flipY?: boolean
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* WebGPU-based texture encoder
|
|
111
|
+
*/
|
|
112
|
+
export class Spark {
|
|
113
|
+
/**
|
|
114
|
+
* Creates a new Spark instance for WebGPU.
|
|
115
|
+
* @param device - WebGPU device with required features enabled
|
|
116
|
+
* @param options - Configuration options
|
|
117
|
+
* @returns Initialized Spark instance
|
|
118
|
+
*/
|
|
119
|
+
static create(device: GPUDevice, options?: SparkCreateOptions): Promise<Spark>
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Determines the set of WebGPU features to request when initializing the device.
|
|
123
|
+
* This function inspects the given adapter to see which texture compression and shader
|
|
124
|
+
* features are available, and returns a list of those that are both supported and safe to enable.
|
|
125
|
+
* @param adapter - The WebGPU adapter returned from navigator.gpu.requestAdapter()
|
|
126
|
+
* @returns Array of WebGPU feature names to request during adapter.requestDevice()
|
|
127
|
+
*/
|
|
128
|
+
static getRequiredFeatures(adapter: GPUAdapter): GPUFeatureName[]
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Destroys the Spark instance and all associated GPU resources.
|
|
132
|
+
*/
|
|
133
|
+
dispose(): void
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Load an image and encode it to a compressed GPU texture.
|
|
137
|
+
* @param source - The image to encode. Can be a URL string, DOM image element, ImageBitmap, or GPUTexture
|
|
138
|
+
* @param options - Optional configuration for encoding
|
|
139
|
+
* @returns Promise resolving to the encoded GPU texture
|
|
140
|
+
*/
|
|
141
|
+
encodeTexture(source: string | HTMLImageElement | ImageBitmap | GPUTexture, options?: SparkEncodeOptions): Promise<GPUTexture>
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Returns list of compression formats supported on the current device.
|
|
145
|
+
* @returns Array of format name strings (e.g., "bc7-rgba", "bc1-rgb", "etc2-rgb")
|
|
146
|
+
*/
|
|
147
|
+
getSupportedFormats(): string[]
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Checks if a specific format is supported.
|
|
151
|
+
* @param format - Format name or format constant
|
|
152
|
+
* @returns True if format is supported
|
|
153
|
+
*/
|
|
154
|
+
isFormatSupported(format: string | number): boolean
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Frees cached temporary GPU resources when cacheTempResources option is enabled.
|
|
158
|
+
* Call this when you're done encoding textures to free up GPU memory.
|
|
159
|
+
*/
|
|
160
|
+
freeTempResources(): void
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Try to determine the best compression options automatically.
|
|
164
|
+
* Do not use this in production, this is for convenience only.
|
|
165
|
+
* @param source - Image input
|
|
166
|
+
* @param options - Encoding options
|
|
167
|
+
* @returns Recommended encoding options with an explicit encoding format
|
|
168
|
+
*/
|
|
169
|
+
selectPreferredOptions(source: string | HTMLImageElement | ImageBitmap | GPUTexture, options?: SparkEncodeOptions): Promise<SparkEncodeOptions>
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get elapsed time for the last encoding operation (requires useTimestampQueries option).
|
|
173
|
+
* @returns Promise resolving to elapsed time in milliseconds
|
|
174
|
+
*/
|
|
175
|
+
getTimeElapsed(): Promise<number>
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Options for initializing SparkGL (WebGL2) encoder
|
|
180
|
+
*/
|
|
181
|
+
export interface SparkGLCreateOptions {
|
|
182
|
+
/**
|
|
183
|
+
* Whether to preload shader programs. Can be:
|
|
184
|
+
* - false: Load shaders on-demand
|
|
185
|
+
* - true: Preload all supported formats
|
|
186
|
+
* - string[]: Array of format names to preload (e.g., ["bc7", "astc"])
|
|
187
|
+
* @default false
|
|
188
|
+
*/
|
|
189
|
+
preload?: boolean | string[]
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Whether to cache temporary resources for reuse across encodeTexture calls.
|
|
193
|
+
* @default false
|
|
194
|
+
*/
|
|
195
|
+
cacheTempResources?: boolean
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Enable verbose logging for debugging.
|
|
199
|
+
* @default false
|
|
200
|
+
*/
|
|
201
|
+
verbose?: boolean
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Enable WebGL shader validation. Only enable this for debugging,
|
|
205
|
+
* as it disables async shader compilation.
|
|
206
|
+
* @default false
|
|
207
|
+
*/
|
|
208
|
+
validateShaders?: boolean
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Result object returned by SparkGL.encodeTexture()
|
|
213
|
+
*/
|
|
214
|
+
export interface SparkGLTextureResult {
|
|
215
|
+
/**
|
|
216
|
+
* The compressed WebGL texture
|
|
217
|
+
*/
|
|
218
|
+
texture: WebGLTexture
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* WebGL internal format constant
|
|
222
|
+
*/
|
|
223
|
+
format: number
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Spark format name
|
|
227
|
+
*/
|
|
228
|
+
sparkFormat: number
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Human-readable Spark format name
|
|
232
|
+
*/
|
|
233
|
+
sparkFormatName: string
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Texture width in pixels
|
|
237
|
+
*/
|
|
238
|
+
width: number
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Texture height in pixels
|
|
242
|
+
*/
|
|
243
|
+
height: number
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Number of mipmap levels
|
|
247
|
+
*/
|
|
248
|
+
mipLevels: number
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Size of the texture data in bytes
|
|
252
|
+
*/
|
|
253
|
+
byteLength: number
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* WebGL2-based texture encoder
|
|
258
|
+
*/
|
|
259
|
+
export class SparkGL {
|
|
260
|
+
/**
|
|
261
|
+
* Creates a new SparkGL instance for WebGL2.
|
|
262
|
+
* @param gl - WebGL2 context. Required extensions are automatically enabled.
|
|
263
|
+
* @param options - Configuration options
|
|
264
|
+
* @returns Initialized SparkGL instance
|
|
265
|
+
*/
|
|
266
|
+
static create(gl: WebGLRenderingContext | WebGL2RenderingContext, options?: SparkGLCreateOptions): SparkGL
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Destroys the SparkGL instance and all associated GPU resources.
|
|
270
|
+
*/
|
|
271
|
+
dispose(): void
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Load an image and encode it to a compressed WebGL texture.
|
|
275
|
+
* @param source - The image to encode. Can be a URL string, DOM image element, ImageBitmap, or WebGLTexture
|
|
276
|
+
* @param options - Optional configuration for encoding
|
|
277
|
+
* @returns Promise resolving to an object containing the encoded texture and metadata
|
|
278
|
+
*/
|
|
279
|
+
encodeTexture(source: string | HTMLImageElement | ImageBitmap | WebGLTexture, options?: SparkEncodeOptions): Promise<SparkGLTextureResult>
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Returns list of compression formats supported on the current device.
|
|
283
|
+
* @returns Array of format name strings (e.g., "bc7-rgba", "bc1-rgb", "etc2-rgb")
|
|
284
|
+
*/
|
|
285
|
+
getSupportedFormats(): string[]
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Checks if a specific format is supported.
|
|
289
|
+
* @param format - Format name or format constant
|
|
290
|
+
* @returns True if format is supported
|
|
291
|
+
*/
|
|
292
|
+
isFormatSupported(format: string | number): boolean
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Frees cached temporary GPU resources when cacheTempResources option is enabled.
|
|
296
|
+
* Call this when you're done encoding textures to free up GPU memory.
|
|
297
|
+
*/
|
|
298
|
+
freeTempResources(): void
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export default Spark
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Type definitions for @ludicon/spark.js/three-gltf
|
|
2
|
+
// Project: https://github.com/ludicon/spark.js
|
|
3
|
+
|
|
4
|
+
import type { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
|
5
|
+
import type { Spark, SparkGL, SparkEncodeOptions } from '@ludicon/spark.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Options for the Spark GLTF loader plugin
|
|
9
|
+
*/
|
|
10
|
+
export interface SparkGLTFOptions extends Omit<SparkEncodeOptions, 'format'> {
|
|
11
|
+
/**
|
|
12
|
+
* Whether to generate mipmaps for textures.
|
|
13
|
+
* @default true
|
|
14
|
+
*/
|
|
15
|
+
mips?: boolean
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Alias for mips. Whether to generate mipmaps.
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
generateMipmaps?: boolean
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Hint for the automatic format selector. When true, chooses lower quality
|
|
25
|
+
* formats like "bc1" or "etc2" instead of "bc7" or "astc".
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
preferLowQuality?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Registers the Spark texture compression plugin with a Three.js GLTFLoader.
|
|
33
|
+
* This plugin automatically compresses textures during GLTF loading using Spark.
|
|
34
|
+
*
|
|
35
|
+
* The plugin analyzes material usage to determine optimal compression formats for each texture:
|
|
36
|
+
* - Base color textures use RGB or RGBA formats with sRGB encoding
|
|
37
|
+
* - Normal maps use RG formats
|
|
38
|
+
* - Metallic/roughness and other PBR textures use appropriate channel formats
|
|
39
|
+
*
|
|
40
|
+
* @param loader - The Three.js GLTFLoader instance to register the plugin with
|
|
41
|
+
* @param spark - A Spark or SparkGL instance to use for texture compression
|
|
42
|
+
* @param options - Optional encoding options to apply to all textures
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
47
|
+
* import { SparkGL } from '@ludicon/spark.js';
|
|
48
|
+
* import { registerSparkLoader } from '@ludicon/spark.js/three-gltf';
|
|
49
|
+
*
|
|
50
|
+
* const canvas = document.createElement('canvas');
|
|
51
|
+
* const gl = canvas.getContext('webgl2');
|
|
52
|
+
* const spark = SparkGL.create(gl, { cacheTempResources: true });
|
|
53
|
+
*
|
|
54
|
+
* const loader = new GLTFLoader();
|
|
55
|
+
* registerSparkLoader(loader, spark, { mips: true });
|
|
56
|
+
*
|
|
57
|
+
* loader.load('model.gltf', (gltf) => {
|
|
58
|
+
* scene.add(gltf.scene);
|
|
59
|
+
* });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function registerSparkLoader(loader: GLTFLoader, spark: Spark | SparkGL, options?: SparkGLTFOptions): void
|
package/src/three-gltf.js
CHANGED
|
@@ -185,7 +185,7 @@ class SparkLoader extends THREE.TextureLoader {
|
|
|
185
185
|
const texture = new THREE.ExternalTexture(gpuTexture)
|
|
186
186
|
if (textureObject.texture !== undefined) {
|
|
187
187
|
texture.format = textureObject.format
|
|
188
|
-
texture.byteLength = textureObject.byteLength
|
|
188
|
+
texture.userData.byteLength = textureObject.byteLength
|
|
189
189
|
}
|
|
190
190
|
if (this.format == "rg" && "NormalRGPacking" in THREE) {
|
|
191
191
|
// This is not understood by stock three.js
|