@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/dist/spark.esm.js CHANGED
@@ -1,12 +1,6 @@
1
- const modules = /* @__PURE__ */ Object.assign({ "./spark_astc_rgb.wgsl": () => import("./spark_astc_rgb-ylbf30mQ.js"), "./spark_astc_rgba.wgsl": () => import("./spark_astc_rgba-C4NuyfHw.js"), "./spark_bc1_rgb.wgsl": () => import("./spark_bc1_rgb-L0ZV40FW.js"), "./spark_bc4_r.wgsl": () => import("./spark_bc4_r-B4cqoXVq.js"), "./spark_bc5_rg.wgsl": () => import("./spark_bc5_rg-boGe-kBy.js"), "./spark_bc7_rgb.wgsl": () => import("./spark_bc7_rgb-CYdL55pE.js"), "./spark_bc7_rgba.wgsl": () => import("./spark_bc7_rgba-BFgOyqos.js"), "./spark_eac_r.wgsl": () => import("./spark_eac_r-BFwH430b.js"), "./spark_eac_rg.wgsl": () => import("./spark_eac_rg--Gm5Gzmk.js"), "./spark_etc2_rgb.wgsl": () => import("./spark_etc2_rgb-CWjBHhHQ.js"), "./utils.wgsl": () => import("./utils-CqE_M33R.js") });
2
- const shaders = Object.fromEntries(
3
- Object.entries(modules).map(([path, module]) => {
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
- const SparkFormatMap = Object.freeze({
222
- "astc-4x4-rgb": SparkFormat.ASTC_4x4_RGB,
223
- "astc-4x4-rgba": SparkFormat.ASTC_4x4_RGBA,
224
- "eac-r": SparkFormat.EAC_R,
225
- "eac-rg": SparkFormat.EAC_RG,
226
- "etc2-rgb": SparkFormat.ETC2_RGB,
227
- "bc1-rgb": SparkFormat.BC1_RGB,
228
- "bc4-r": SparkFormat.BC4_R,
229
- "bc5-rg": SparkFormat.BC5_RG,
230
- "bc7-rgb": SparkFormat.BC7_RGB,
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": SparkFormat.ASTC_4x4_RGB,
234
- "astc-rgba": SparkFormat.ASTC_4x4_RGBA,
222
+ "astc-rgb": s.ASTC_4x4_RGB,
223
+ "astc-rgba": s.ASTC_4x4_RGBA,
235
224
  // webgpu aliases:
236
- "bc1-rgba-unorm": SparkFormat.BC1_RGB,
237
- "bc1-rgba-unorm-srgb": SparkFormat.BC1_RGB,
238
- "bc4-r-unorm": SparkFormat.BC4_R,
239
- "bc5-rg-unorm": SparkFormat.BC5_RG,
240
- "bc7-rgba-unorm": SparkFormat.BC7_RGBA,
241
- "bc7-rgba-unorm-srgb": SparkFormat.BC7_RGBA,
242
- "etc2-rgb8unorm": SparkFormat.ETC2_RGB,
243
- "etc2-rgb8unorm-srgb": SparkFormat.ETC2_RGB,
244
- "eac-r11unorm": SparkFormat.EAC_R,
245
- "eac-rg11unorm": SparkFormat.EAC_RG,
246
- "astc-4x4-unorm": SparkFormat.ASTC_4x4_RGBA,
247
- "astc-4x4-unorm-srgb": SparkFormat.ASTC_4x4_RGBA
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
- true,
286
+ !0,
300
287
  // ASTC_4x4_RGB
301
288
  /* 1 */
302
- true,
289
+ !0,
303
290
  // ASTC_4x4_RGBA
304
291
  /* 2 */
305
292
  null,
306
293
  /* 3 */
307
294
  null,
308
295
  /* 4 */
309
- false,
296
+ !1,
310
297
  // EAC_R
311
298
  /* 5 */
312
- false,
299
+ !1,
313
300
  // EAC_RG
314
301
  /* 6 */
315
- true,
302
+ !0,
316
303
  // ETC2_RGB
317
304
  /* 7 */
318
305
  null,
319
306
  /* 8 */
320
307
  null,
321
308
  /* 9 */
322
- true,
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
- false,
318
+ !1,
332
319
  // BC4_R
333
320
  /* 14 */
334
- false,
321
+ !1,
335
322
  // BC5_RG
336
323
  /* 15 */
337
324
  null,
338
325
  /* 16 */
339
- true,
326
+ !0,
340
327
  // BC7_RGB
341
328
  /* 17 */
342
- true
329
+ !0
343
330
  // BC7_RGB
344
331
  ];
345
- function assert(condition, message) {
346
- if (!condition) {
347
- throw new Error(message);
348
- }
332
+ function w(n, e) {
333
+ if (!n)
334
+ throw new Error(e);
349
335
  }
350
- function isWebGPU(device) {
351
- return typeof GPUDevice != "undefined" && device instanceof GPUDevice;
336
+ function X(n) {
337
+ return typeof GPUDevice < "u" && n instanceof GPUDevice;
352
338
  }
353
- function isIOS() {
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 getSafariVersion() {
358
- const match = navigator.userAgent.match(/Safari\/(\d+(\.\d+)?)/);
359
- return match && parseFloat(match[1]);
343
+ function j() {
344
+ const n = navigator.userAgent.match(/Safari\/(\d+(\.\d+)?)/);
345
+ return n && parseFloat(n[1]);
360
346
  }
361
- function getFirefoxVersion() {
362
- const match = navigator.userAgent.match(/Firefox\/(\d+(\.\d+)?)/);
363
- return match && parseFloat(match[1]);
347
+ function Q() {
348
+ const n = navigator.userAgent.match(/Firefox\/(\d+(\.\d+)?)/);
349
+ return n && parseFloat(n[1]);
364
350
  }
365
- function detectWebGPUFormats(device) {
366
- const supportedFormats = /* @__PURE__ */ new Set();
367
- const formatMap = {
351
+ function H(n) {
352
+ const e = /* @__PURE__ */ new Set(), r = {
368
353
  "texture-compression-bc": [
369
- SparkFormat.BC1_RGB,
370
- SparkFormat.BC4_R,
371
- SparkFormat.BC5_RG,
372
- SparkFormat.BC7_RGB,
373
- SparkFormat.BC7_RGBA
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": [SparkFormat.ETC2_RGB, SparkFormat.EAC_R, SparkFormat.EAC_RG],
376
- "texture-compression-astc": [SparkFormat.ASTC_4x4_RGB, SparkFormat.ASTC_4x4_RGBA]
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 [feature, formats] of Object.entries(formatMap)) {
379
- if (device.features.has(feature)) {
380
- for (const format of formats) {
381
- supportedFormats.add(format);
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 imageToByteArray(image) {
388
- const canvas = document.createElement("canvas");
389
- canvas.width = image.width;
390
- canvas.height = image.height;
391
- const ctx = canvas.getContext("2d");
392
- ctx.drawImage(image, 0, 0);
393
- const imageData = ctx.getImageData(0, 0, image.width, image.height);
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 isSvgUrl(url) {
397
- return /\.svg(?:$|\?)/i.test(url) || /^data:image\/svg\+xml[,;]/i.test(url);
377
+ function J(n) {
378
+ return /\.svg(?:$|\?)/i.test(n) || /^data:image\/svg\+xml[,;]/i.test(n);
398
379
  }
399
- function loadImageElement(url) {
400
- return new Promise((resolve, reject) => {
401
- const img = new Image();
402
- img.crossOrigin = "anonymous";
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 loadImageBitmap(url, opts = {}) {
410
- const res = await fetch(url, { mode: "cors" });
411
- if (!res.ok) throw new Error(`HTTP ${res.status} for ${url}`);
412
- const blob = await res.blob();
413
- return createImageBitmap(blob, {
414
- imageOrientation: opts.flipY ? "flipY" : "none",
415
- colorSpaceConversion: opts.colorSpaceConversion ?? "none",
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
- function loadImage(url) {
420
- if (isSvgUrl(url)) {
421
- return loadImageElement(url);
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 BYTES_PER_ROW_ALIGNMENT = 256;
427
- const MIN_MIP_SIZE = 4;
428
- function computeMipmapLayout(w, h, blockSize, mipmaps) {
429
- let mipmapCount = 0;
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 bw = Math.ceil(w / 4);
434
- const bh = Math.ceil(h / 4);
435
- const bytesPerRow = Math.ceil(bw * blockSize / BYTES_PER_ROW_ALIGNMENT) * BYTES_PER_ROW_ALIGNMENT;
436
- const alignedSize = bh * bytesPerRow;
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 Spark {
446
- #device;
447
- #supportedFormats;
448
- #pipelines = [];
449
- #supportsFloat16;
450
- #mipmapPipeline;
451
- #resizePipeline;
452
- #flipYPipeline;
453
- #detectChannelCountPipeline;
454
- #defaultSampler;
455
- #uniformBuffer = new Array(3);
456
- #querySet;
457
- #queryBuffer;
458
- #queryReadbackBuffer;
459
- #encodeCounter = 0;
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(device, options = {}) {
468
- const instance = new Spark();
469
- await instance.#init(device, options.preload ?? false, options.useTimestampQueries ?? false);
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 formats = [
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 supported = [];
500
- for (const format of formats) {
501
- const sparkFormat = SparkFormatMap[format];
502
- if (this.#isFormatSupported(sparkFormat)) {
503
- const ratio = SparkFormatRatio[sparkFormat];
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 supported;
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(adapter) {
528
- const features = [];
529
- const IOS = isIOS();
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(source, options = {}) {
556
- if (options.format == void 0 || options.format == "auto") {
557
- const image = source instanceof Image || source instanceof ImageBitmap || source instanceof GPUTexture ? source : await loadImage(source);
558
- options.format = "auto";
559
- const format = await this.#getBestMatchingFormat(options, image);
560
- options.format = SparkFormatName[format];
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 options;
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(source, options = {}) {
610
- assert(this.#device, "Spark is not initialized");
611
- const image = source instanceof Image || source instanceof ImageBitmap || source instanceof GPUTexture ? source : await loadImage(source);
612
- console.log("Loaded image", image);
613
- const format = await this.#getBestMatchingFormat(options, image);
614
- const pipelinePromise = this.#loadPipeline(format);
615
- const width = Math.ceil(image.width / 4) * 4;
616
- const height = Math.ceil(image.height / 4) * 4;
617
- const blockSize = SparkBlockSize[format];
618
- const mipmaps = options.generateMipmaps || options.mips;
619
- const { mipmapCount, outputSize, bufferRanges } = computeMipmapLayout(width, height, blockSize, mipmaps);
620
- const srgb = (options.srgb || options.format?.endsWith("srgb")) && SparkFormatIsRGB[format];
621
- const colorMode = srgb ? 1 : options.normal ? 2 : 0;
622
- const webgpuFormat = SparkWebGPUFormats[format] + (srgb ? "-srgb" : "");
623
- const viewFormats = srgb ? ["rgba8unorm", "rgba8unorm-srgb"] : ["rgba8unorm"];
624
- const counter = this.#encodeCounter++;
625
- console.time("create input texture #" + counter);
626
- let inputUsage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.STORAGE_BINDING;
627
- const needsProcessing = options.flipY || width != image.width || height != image.height;
628
- if (!needsProcessing && !(image instanceof GPUTexture)) {
629
- inputUsage |= GPUTextureUsage.RENDER_ATTACHMENT;
630
- }
631
- const commandEncoder = this.#device.createCommandEncoder();
632
- commandEncoder.pushDebugGroup?.("spark process texture");
633
- if (this.#querySet && typeof commandEncoder.writeTimestamp === "function") {
634
- commandEncoder.writeTimestamp(this.#querySet, 0);
635
- }
636
- let inputTexture;
637
- if (needsProcessing || !(image instanceof GPUTexture && !mipmaps)) {
638
- inputTexture = this.#device.createTexture({
639
- size: [width, height, 1],
640
- mipLevelCount: mipmapCount,
641
- format: "rgba8unorm",
642
- usage: inputUsage,
643
- viewFormats
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
- const outputBuffer = this.#device.createBuffer({
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 label = `dispatch compute shader '${SparkFormatName[format]}' #${counter}`;
693
- console.time(label);
694
- commandEncoder.pushDebugGroup?.("spark encode texture");
695
- let args = {};
696
- if (this.#querySet && typeof commandEncoder.writeTimestamp !== "function") {
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: inputTexture.createView({
715
- baseMipLevel: m,
608
+ resource: b.createView({
609
+ baseMipLevel: p,
716
610
  mipLevelCount: 1
717
611
  })
718
612
  },
719
613
  {
720
614
  binding: 1,
721
- resource: this.#defaultSampler
615
+ resource: this.#a
722
616
  },
723
617
  {
724
618
  binding: 2,
725
619
  resource: {
726
- buffer: outputBuffer,
727
- offset: bufferRanges[m].offset,
728
- size: bufferRanges[m].size
620
+ buffer: y,
621
+ offset: m[p].offset,
622
+ size: m[p].size
729
623
  }
730
624
  }
731
625
  ]
732
626
  });
733
- pass.setBindGroup(0, bindGroup);
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
- pass.end();
737
- for (let m = 0; m < mipmapCount; m++) {
738
- commandEncoder.copyBufferToTexture(
629
+ R.end();
630
+ for (let p = 0; p < l; p++)
631
+ g.copyBufferToTexture(
739
632
  {
740
- buffer: outputBuffer,
741
- offset: bufferRanges[m].offset,
742
- bytesPerRow: bufferRanges[m].bytesPerRow,
743
- rowsPerImage: bufferRanges[m].bh
633
+ buffer: y,
634
+ offset: m[p].offset,
635
+ bytesPerRow: m[p].bytesPerRow,
636
+ rowsPerImage: m[p].bh
744
637
  },
745
638
  {
746
- texture: outputTexture,
747
- mipLevel: m
639
+ texture: U,
640
+ mipLevel: p
748
641
  },
749
642
  {
750
- width: bufferRanges[m].bw * 4,
751
- height: bufferRanges[m].bh * 4,
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.#querySet) {
670
+ if (!this.#r)
808
671
  return 0;
809
- }
810
- const commandEncoder = this.#device.createCommandEncoder();
811
- commandEncoder.resolveQuerySet(this.#querySet, 0, 2, this.#queryBuffer, 0);
812
- commandEncoder.copyBufferToBuffer(this.#queryBuffer, 0, this.#queryReadbackBuffer, 0, 16);
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 #init(device, preload, useTimestampQueries) {
826
- assert(device, "device is required");
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.#uniformBuffer[i] = this.#device.createBuffer({
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
- this.#device.queue.writeBuffer(this.#uniformBuffer[i], 0, new Uint32Array([i]));
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 (useTimestampQueries && this.#device.features.has("timestamp-query")) {
842
- const webkitVersion = getSafariVersion();
843
- const firefoxVersion = getFirefoxVersion();
844
- if ((!webkitVersion || webkitVersion >= 26) && !firefoxVersion) {
845
- this.#querySet = this.#device.createQuerySet({
846
- type: "timestamp",
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 #loadUtilPipelines() {
880
- const shaderModule = this.#device.createShaderModule({
881
- code: await shaders["utils.wgsl"](),
711
+ async #G() {
712
+ const e = this.#e.createShaderModule({
713
+ code: await k["utils.wgsl"](),
882
714
  label: "utils"
883
715
  });
884
- if (typeof shaderModule.compilationInfo == "function") {
885
- const info = await shaderModule.compilationInfo();
886
- if (info.messages.some((msg) => msg.type == "error")) {
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 msg of info.messages) {
889
- console.error(msg);
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.#mipmapPipeline = await this.#device.createComputePipelineAsync({
725
+ this.#c = await this.#e.createComputePipelineAsync({
895
726
  layout: "auto",
896
727
  compute: {
897
- module: shaderModule,
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: shaderModule,
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: shaderModule,
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: shaderModule,
746
+ module: e,
919
747
  entryPoint: "detect_channel_count"
920
748
  }
921
749
  });
922
750
  }
923
- async #loadPipeline(format) {
924
- if (this.#pipelines[format]) {
925
- return this.#pipelines[format];
926
- }
927
- const pipelinePromise = (async () => {
928
- console.time("loadPipeline " + SparkFormatName[format]);
929
- const shaderFile = SparkShaderFiles[format];
930
- assert(shaderFile, `No shader available for format ${SparkFormatName[format]}`);
931
- let shaderCode = await shaders[shaderFile]();
932
- if (!this.#supportsFloat16) {
933
- shaderCode = shaderCode.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");
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 shaderModule.getCompilationInfo == "function") {
940
- const info = await shaderModule.getCompilationInfo();
941
- if (info.messages.some((msg) => msg.type == "error")) {
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 msg of info.messages) {
944
- console.error(msg);
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 pipeline = await this.#device.createComputePipelineAsync({
773
+ const o = await this.#e.createComputePipelineAsync({
950
774
  layout: "auto",
951
775
  compute: {
952
- module: shaderModule,
776
+ module: a,
953
777
  entryPoint: "main"
954
778
  }
955
779
  });
956
- console.timeEnd("loadPipeline " + SparkFormatName[format]);
957
- return pipeline;
780
+ return console.timeEnd("loadPipeline " + B[e]), o;
958
781
  })();
959
- this.#pipelines[format] = pipelinePromise;
960
- return pipelinePromise;
782
+ return this.#i[e] = r, r;
961
783
  }
962
- #isFormatSupported(format) {
963
- return this.#supportedFormats.has(format);
784
+ #t(e) {
785
+ return this.#o.has(e);
964
786
  }
965
- #getPreferredFormat(format, preferLowQuality) {
966
- const explicitFormat = SparkFormatMap[format];
967
- if (explicitFormat != void 0 && this.#isFormatSupported(explicitFormat)) {
968
- return explicitFormat;
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 key of preferenceOrder) {
998
- if (key.includes(format) && this.#isFormatSupported(SparkFormatMap[key])) {
999
- return SparkFormatMap[key];
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 #getBestMatchingFormat(options, image) {
1004
- if (options.format == void 0) {
1005
- options.format = "rgb";
1006
- } else if (options.format == "auto") {
1007
- if (options.alpha) {
1008
- if (this.#isFormatSupported(SparkFormat.BC7_RGBA)) return SparkFormat.BC7_RGBA;
1009
- if (this.#isFormatSupported(SparkFormat.ASTC_4x4_RGBA)) return SparkFormat.ASTC_4x4_RGBA;
1010
- } else if (options.srgb) {
1011
- if (this.#isFormatSupported(SparkFormat.BC7_RGB)) return SparkFormat.BC7_RGB;
1012
- if (this.#isFormatSupported(SparkFormat.ASTC_4x4_RGB)) return SparkFormat.ASTC_4x4_RGB;
1013
- if (this.#isFormatSupported(SparkFormat.BC1_RGB)) return SparkFormat.BC1_RGB;
1014
- if (this.#isFormatSupported(SparkFormat.ETC2_RGB)) return SparkFormat.ETC2_RGB;
1015
- } else if (options.normal) {
1016
- if (this.#isFormatSupported(SparkFormat.BC5_RG)) return SparkFormat.BC5_RG;
1017
- if (this.#isFormatSupported(SparkFormat.EAC_RG)) return SparkFormat.EAC_RG;
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 channelCount;
1020
- if (image instanceof GPUTexture) {
1021
- if (image.format == "r8unorm" || image.format == "r16unorm") channelCount = 1;
1022
- else if (image.format == "rg8unorm" || image.format == "rg16unorm") channelCount = 2;
1023
- else {
1024
- channelCount = await this.#detectChannelCountGPU(image);
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 (channelCount == 4) {
1031
- if (this.#isFormatSupported(SparkFormat.BC7_RGBA)) return SparkFormat.BC7_RGBA;
1032
- if (this.#isFormatSupported(SparkFormat.ASTC_4x4_RGBA)) return SparkFormat.ASTC_4x4_RGBA;
1033
- } else if (channelCount == 3) {
1034
- if (this.#isFormatSupported(SparkFormat.BC7_RGB)) return SparkFormat.BC7_RGB;
1035
- if (this.#isFormatSupported(SparkFormat.ASTC_4x4_RGB)) return SparkFormat.ASTC_4x4_RGB;
1036
- if (this.#isFormatSupported(SparkFormat.BC1_RGB)) return SparkFormat.BC1_RGB;
1037
- if (this.#isFormatSupported(SparkFormat.ETC2_RGB)) return SparkFormat.ETC2_RGB;
1038
- } else if (channelCount == 2) {
1039
- if (this.#isFormatSupported(SparkFormat.BC5_RG)) return SparkFormat.BC5_RG;
1040
- if (this.#isFormatSupported(SparkFormat.EAC_RG)) return SparkFormat.EAC_RG;
1041
- } else if (channelCount == 1) {
1042
- if (this.#isFormatSupported(SparkFormat.BC4_R)) return SparkFormat.BC4_R;
1043
- if (this.#isFormatSupported(SparkFormat.EAC_R)) return SparkFormat.EAC_R;
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 format = this.#getPreferredFormat(options.format, options.preferLowQuality);
1049
- if (format === void 0) {
1050
- throw new Error(`Unsupported format: ${options.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
- #detectChannelCount(imageData) {
1055
- let opaque = true;
1056
- let grayscale = true;
1057
- let invalidNormalCount = 0;
1058
- const count = Math.min(1024 * 128, imageData.length);
1059
- for (let i = 0; i < count; i += 4) {
1060
- const r = imageData[i] / 255;
1061
- const g = imageData[i + 1] / 255;
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
- if (!opaque) return 4;
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 #detectChannelCountGPU(texture) {
1079
- const counterSize = 12;
1080
- const counterBuffer = this.#device.createBuffer({
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
- const readbackBuffer = this.#device.createBuffer({
1085
- size: counterSize,
883
+ }), i = this.#e.createBuffer({
884
+ size: 12,
1086
885
  usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
1087
- });
1088
- const bindGroup = this.#device.createBindGroup({
1089
- layout: this.#detectChannelCountPipeline.getBindGroupLayout(0),
886
+ }), a = this.#e.createBindGroup({
887
+ layout: this.#u.getBindGroupLayout(0),
1090
888
  entries: [
1091
- { binding: 0, resource: texture.createView() },
1092
- { binding: 1, resource: { buffer: counterBuffer } }
889
+ { binding: 0, resource: e.createView() },
890
+ { binding: 1, resource: { buffer: t } }
1093
891
  ]
1094
- });
1095
- const encoder = this.#device.createCommandEncoder();
1096
- const pass = encoder.beginComputePass();
1097
- pass.setPipeline(this.#detectChannelCountPipeline);
1098
- pass.setBindGroup(0, bindGroup);
1099
- const { width, height } = texture;
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 #processInputTexture(encoder, inputTexture, outputTexture, width, height, colorMode, flipY) {
1122
- const pass = encoder.beginComputePass();
1123
- const pipeline = flipY ? this.#flipYPipeline : this.#resizePipeline;
1124
- pass.setPipeline(pipeline);
1125
- const bindGroup = this.#device.createBindGroup({
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: inputTexture.createView({
908
+ resource: r.createView({
1131
909
  baseMipLevel: 0,
1132
910
  mipLevelCount: 1,
1133
- format: colorMode == 1 ? "rgba8unorm-srgb" : "rgba8unorm",
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: outputTexture.createView({
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.#defaultSampler
927
+ resource: this.#a
1150
928
  },
1151
929
  {
1152
930
  binding: 3,
1153
- resource: { buffer: this.#uniformBuffer[colorMode] }
931
+ resource: { buffer: this.#n[o] }
1154
932
  }
1155
933
  ]
1156
934
  });
1157
- pass.setBindGroup(0, bindGroup);
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 #generateMipmaps(encoder, texture, mipmapCount, width, height, colorMode) {
1162
- const pass = encoder.beginComputePass();
1163
- pass.setPipeline(this.#mipmapPipeline);
1164
- let w = width;
1165
- let h = height;
1166
- for (let i = 0; i < mipmapCount - 1; i++) {
1167
- w = Math.max(1, Math.floor(w / 2));
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
- #generateMipLevel(pass, texture, srcLevel, dstLevel, width, height, colorMode) {
1174
- const bindGroup = this.#device.createBindGroup({
1175
- layout: this.#mipmapPipeline.getBindGroupLayout(0),
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: texture.createView({
1180
- baseMipLevel: srcLevel,
951
+ resource: r.createView({
952
+ baseMipLevel: t,
1181
953
  mipLevelCount: 1,
1182
- format: colorMode == 1 ? "rgba8unorm-srgb" : "rgba8unorm",
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: texture.createView({
1189
- baseMipLevel: dstLevel,
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.#defaultSampler
970
+ resource: this.#a
1199
971
  },
1200
972
  {
1201
973
  binding: 3,
1202
- resource: { buffer: this.#uniformBuffer[colorMode] }
974
+ resource: { buffer: this.#n[c] }
1203
975
  }
1204
976
  ]
1205
977
  });
1206
- pass.setBindGroup(0, bindGroup);
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
  };