gputex 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/testing.d.ts CHANGED
@@ -110,8 +110,10 @@ type ASTC4x4Block = Uint8Array;
110
110
  * grayscale + opaque → luminance, opaque → RGB, otherwise RGBA.
111
111
  *
112
112
  * Algorithm per class:
113
- * 1. Quantise input to 8-bit; classify.
114
- * 2. Farthest-pair over the class's channels → initial (e0, e1).
113
+ * 1. Quantise input to 8-bit; classify (opaque colour tries both CEM 8
114
+ * bit budgets and keeps the lower-error one).
115
+ * 2. Farthest-pair over the class's channels → initial (e0, e1),
116
+ * quantised to the class's endpoint range.
115
117
  * 3. Assign per-texel indices by nearest palette entry.
116
118
  * 4. One LSQ refit pass; accept only if total error strictly decreases.
117
119
  * 5. CEM 8/12: flip endpoints (and reflect weights) if
@@ -121,13 +123,14 @@ type ASTC4x4Block = Uint8Array;
121
123
  declare function encodeASTC4x4Block(pixels: ASTC4x4Pixels): ASTC4x4Block;
122
124
  /**
123
125
  * Decode an ASTC 4×4 block produced by this encoder (or by any other
124
- * encoder that respects our narrow subset: block modes 0x042/0x053/0x253,
125
- * single partition, CEM 0/8/12 with 8-bit endpoints). Handles the
126
+ * encoder that respects our narrow subset: block modes 0x042/0x053/0x242/
127
+ * 0x253, single partition, CEM 0/8/12 — QUANT_192 endpoints with 0x242,
128
+ * 8-bit endpoints otherwise). Handles the
126
129
  * blue-contraction branch even though our encoder doesn't produce it, so
127
130
  * externally-supplied blocks round-trip predictably.
128
131
  *
129
132
  * The CEM is validated against the block mode's expected pairing (we only
130
- * ever emit the three fixed combinations above).
133
+ * ever emit the four fixed combinations above).
131
134
  *
132
135
  * Output: 16 RGBA pixels as 64 floats in [0, 1].
133
136
  */
package/dist/testing.js CHANGED
@@ -922,33 +922,49 @@ function decodeBC7Block(block) {
922
922
  // src/astc4x4_ref.ts
923
923
  var BLOCK_MODE_4x4_2BIT = 66;
924
924
  var BLOCK_MODE_4x4_3BIT = 83;
925
+ var BLOCK_MODE_4x4_4BIT = 578;
925
926
  var BLOCK_MODE_4x4_5BIT = 595;
926
927
  var CEM_LUM_DIRECT = 0;
927
928
  var CEM_RGB_DIRECT = 8;
928
929
  var CEM_RGBA_DIRECT = 12;
929
930
  var WEIGHT_UNQ_4 = [0, 21, 43, 64];
930
931
  var WEIGHT_UNQ_8 = [0, 9, 18, 27, 37, 46, 55, 64];
932
+ var WEIGHT_UNQ_16 = Array.from({ length: 16 }, (_, w) => {
933
+ const u = w << 2 | w >> 2;
934
+ return u > 32 ? u + 1 : u;
935
+ });
931
936
  var WEIGHT_UNQ_32 = Array.from({ length: 32 }, (_, w) => w <= 15 ? 2 * w : 2 * w + 2);
932
937
  var CLASS_LUM = {
933
938
  cem: CEM_LUM_DIRECT,
934
939
  blockMode: BLOCK_MODE_4x4_5BIT,
935
940
  channels: [0],
936
941
  weightBits: 5,
937
- unq: WEIGHT_UNQ_32
942
+ unq: WEIGHT_UNQ_32,
943
+ endpointRange: 256
938
944
  };
939
945
  var CLASS_RGB = {
940
946
  cem: CEM_RGB_DIRECT,
941
947
  blockMode: BLOCK_MODE_4x4_3BIT,
942
948
  channels: [0, 1, 2],
943
949
  weightBits: 3,
944
- unq: WEIGHT_UNQ_8
950
+ unq: WEIGHT_UNQ_8,
951
+ endpointRange: 256
952
+ };
953
+ var CLASS_RGB_Q192 = {
954
+ cem: CEM_RGB_DIRECT,
955
+ blockMode: BLOCK_MODE_4x4_4BIT,
956
+ channels: [0, 1, 2],
957
+ weightBits: 4,
958
+ unq: WEIGHT_UNQ_16,
959
+ endpointRange: 192
945
960
  };
946
961
  var CLASS_RGBA = {
947
962
  cem: CEM_RGBA_DIRECT,
948
963
  blockMode: BLOCK_MODE_4x4_2BIT,
949
964
  channels: [0, 1, 2, 3],
950
965
  weightBits: 2,
951
- unq: WEIGHT_UNQ_4
966
+ unq: WEIGHT_UNQ_4,
967
+ endpointRange: 256
952
968
  };
953
969
  function clamp2(v, lo, hi) {
954
970
  return v < lo ? lo : v > hi ? hi : v;
@@ -959,6 +975,84 @@ function to82(v) {
959
975
  function interp16(e0, e1, w) {
960
976
  return (64 - w) * e0 * 257 + w * e1 * 257 + 32 >> 6;
961
977
  }
978
+ function unq192(value) {
979
+ const trit = value >> 6;
980
+ const bits = value & 63;
981
+ const A = bits & 1 ? 511 : 0;
982
+ const f = bits >> 5 & 1;
983
+ const B = (bits >> 1 & 31) << 4 | f;
984
+ const T = (trit * 5 + B ^ A) & 511;
985
+ return (A & 128 | T >> 2) & 255;
986
+ }
987
+ var Q192_VALUE_OF = (() => {
988
+ const t = new Int16Array(256).fill(-1);
989
+ for (let v = 0; v < 192; v++) t[unq192(v)] = v;
990
+ return t;
991
+ })();
992
+ function nearest192(x) {
993
+ const v = clamp2(Math.round(x), 0, 255);
994
+ if (Q192_VALUE_OF[v] >= 0) return v;
995
+ const lo = v - 1;
996
+ const hi = v + 1;
997
+ const loOk = lo >= 0 && Q192_VALUE_OF[lo] >= 0;
998
+ const hiOk = hi <= 255 && Q192_VALUE_OF[hi] >= 0;
999
+ if (loOk && hiOk) return x - lo <= hi - x ? lo : hi;
1000
+ if (loOk) return x - lo <= 2 ? lo : v + 2;
1001
+ return hiOk ? hi : v - 2;
1002
+ }
1003
+ function decodeTrits(T) {
1004
+ const bit = (x, i) => x >> i & 1;
1005
+ let C;
1006
+ let t3;
1007
+ let t4;
1008
+ if ((T >> 2 & 7) === 7) {
1009
+ C = (T >> 5 & 7) << 2 | T & 3;
1010
+ t4 = 2;
1011
+ t3 = 2;
1012
+ } else {
1013
+ C = T & 31;
1014
+ if ((T >> 5 & 3) === 3) {
1015
+ t4 = 2;
1016
+ t3 = bit(T, 7);
1017
+ } else {
1018
+ t4 = bit(T, 7);
1019
+ t3 = T >> 5 & 3;
1020
+ }
1021
+ }
1022
+ let t0;
1023
+ let t1;
1024
+ let t2;
1025
+ if ((C & 3) === 3) {
1026
+ t2 = 2;
1027
+ t1 = bit(C, 4);
1028
+ t0 = bit(C, 3) << 1 | bit(C, 2) & ~bit(C, 3) & 1;
1029
+ } else if ((C >> 2 & 3) === 3) {
1030
+ t2 = 2;
1031
+ t1 = 2;
1032
+ t0 = C & 3;
1033
+ } else {
1034
+ t2 = bit(C, 4);
1035
+ t1 = C >> 2 & 3;
1036
+ t0 = bit(C, 1) << 1 | bit(C, 0) & ~bit(C, 1) & 1;
1037
+ }
1038
+ return [t0, t1, t2, t3, t4];
1039
+ }
1040
+ function encodeTrits(t0, t1, t2, t3, t4) {
1041
+ let C;
1042
+ if (t2 === 2 && t1 === 2) C = 12 | t0;
1043
+ else if (t2 === 2) C = t1 << 4 | t0 << 2 | 3;
1044
+ else C = t2 << 4 | t1 << 2 | t0;
1045
+ if (t3 === 2 && t4 === 2) return C >> 2 << 5 | 7 << 2 | C & 3;
1046
+ if (t4 === 2) return t3 << 7 | 3 << 5 | C;
1047
+ return t4 << 7 | t3 << 5 | C;
1048
+ }
1049
+ var TRIT_SLICES = [
1050
+ [0, 2],
1051
+ [2, 2],
1052
+ [4, 1],
1053
+ [5, 2],
1054
+ [7, 1]
1055
+ ];
962
1056
  var BitWriter1282 = class {
963
1057
  bits = 0n;
964
1058
  write(pos, nBits, value) {
@@ -1070,39 +1164,61 @@ function refitEndpoints2(vals, C, indices, unq) {
1070
1164
  }
1071
1165
  return { e0, e1 };
1072
1166
  }
1073
- function encodeASTC4x4Block(pixels) {
1074
- if (pixels.length !== 64) {
1075
- throw new Error(`encodeASTC4x4Block: expected 64 values (16 RGBA), got ${pixels.length}`);
1076
- }
1077
- const pixels8 = new Uint8Array(64);
1078
- for (let k = 0; k < 64; k++) pixels8[k] = to82(pixels[k]);
1079
- let gray = true;
1080
- let opaque = true;
1081
- for (let k = 0; k < 16; k++) {
1082
- const r = pixels8[k * 4];
1083
- if (pixels8[k * 4 + 1] !== r || pixels8[k * 4 + 2] !== r) gray = false;
1084
- if (pixels8[k * 4 + 3] !== 255) opaque = false;
1085
- }
1086
- const cls = opaque ? gray ? CLASS_LUM : CLASS_RGB : CLASS_RGBA;
1167
+ function quantEndpoint(cls, e) {
1168
+ return cls.endpointRange === 192 ? e.map(nearest192) : e.map((v) => clamp2(Math.round(v), 0, 255));
1169
+ }
1170
+ function fitClass(cls, pixels8) {
1087
1171
  const C = cls.channels.length;
1088
1172
  const vals = new Float64Array(16 * C);
1089
1173
  for (let k = 0; k < 16; k++) {
1090
1174
  for (let c = 0; c < C; c++) vals[k * C + c] = pixels8[k * 4 + cls.channels[c]];
1091
1175
  }
1092
1176
  const fp = farthestPair2(vals, C);
1093
- let e0 = Array.from({ length: C }, (_, c) => vals[fp.i0 * C + c]);
1094
- let e1 = Array.from({ length: C }, (_, c) => vals[fp.i1 * C + c]);
1177
+ let e0 = quantEndpoint(
1178
+ cls,
1179
+ Array.from({ length: C }, (_, c) => vals[fp.i0 * C + c])
1180
+ );
1181
+ let e1 = quantEndpoint(
1182
+ cls,
1183
+ Array.from({ length: C }, (_, c) => vals[fp.i1 * C + c])
1184
+ );
1095
1185
  let { indices, err } = totalSqError(vals, C, e0, e1, cls.unq);
1096
1186
  const refit = refitEndpoints2(vals, C, indices, cls.unq);
1097
1187
  if (refit) {
1098
- const second = totalSqError(vals, C, refit.e0, refit.e1, cls.unq);
1188
+ const r0 = quantEndpoint(cls, refit.e0);
1189
+ const r1 = quantEndpoint(cls, refit.e1);
1190
+ const second = totalSqError(vals, C, r0, r1, cls.unq);
1099
1191
  if (second.err < err) {
1100
- e0 = refit.e0;
1101
- e1 = refit.e1;
1192
+ e0 = r0;
1193
+ e1 = r1;
1102
1194
  indices = second.indices;
1103
1195
  err = second.err;
1104
1196
  }
1105
1197
  }
1198
+ return { e0, e1, indices, err };
1199
+ }
1200
+ function encodeASTC4x4Block(pixels) {
1201
+ if (pixels.length !== 64) {
1202
+ throw new Error(`encodeASTC4x4Block: expected 64 values (16 RGBA), got ${pixels.length}`);
1203
+ }
1204
+ const pixels8 = new Uint8Array(64);
1205
+ for (let k = 0; k < 64; k++) pixels8[k] = to82(pixels[k]);
1206
+ let gray = true;
1207
+ let opaque = true;
1208
+ for (let k = 0; k < 16; k++) {
1209
+ const r = pixels8[k * 4];
1210
+ if (pixels8[k * 4 + 1] !== r || pixels8[k * 4 + 2] !== r) gray = false;
1211
+ if (pixels8[k * 4 + 3] !== 255) opaque = false;
1212
+ }
1213
+ const classes = opaque ? gray ? [CLASS_LUM] : [CLASS_RGB, CLASS_RGB_Q192] : [CLASS_RGBA];
1214
+ let best = null;
1215
+ for (const cls2 of classes) {
1216
+ const fit = fitClass(cls2, pixels8);
1217
+ if (!best || fit.err < best.err) best = { cls: cls2, ...fit };
1218
+ }
1219
+ const { cls } = best;
1220
+ let { e0, e1, indices } = best;
1221
+ const C = cls.channels.length;
1106
1222
  {
1107
1223
  const nSum = Math.min(C, 3);
1108
1224
  let s0 = 0;
@@ -1128,9 +1244,29 @@ function packBlock3(cls, e0, e1, indices) {
1128
1244
  bw.write(0, 11, cls.blockMode);
1129
1245
  bw.write(11, 2, 0);
1130
1246
  bw.write(13, 4, cls.cem);
1131
- for (let c = 0; c < e0.length; c++) {
1132
- bw.write(17 + c * 16, 8, e0[c]);
1133
- bw.write(25 + c * 16, 8, e1[c]);
1247
+ const values = [];
1248
+ for (let c = 0; c < e0.length; c++) values.push(e0[c], e1[c]);
1249
+ if (cls.endpointRange === 192) {
1250
+ let pos = 17;
1251
+ for (let g = 0; g < values.length; g += 5) {
1252
+ const grp = values.slice(g, g + 5).map((v) => {
1253
+ const q = Q192_VALUE_OF[v];
1254
+ if (q < 0) throw new Error(`packBlock: ${v} is not a QUANT_192 level`);
1255
+ return q;
1256
+ });
1257
+ const t = [0, 0, 0, 0, 0];
1258
+ grp.forEach((q, i) => t[i] = q >> 6);
1259
+ const T = encodeTrits(t[0], t[1], t[2], t[3], t[4]);
1260
+ grp.forEach((q, i) => {
1261
+ bw.write(pos, 6, q & 63);
1262
+ pos += 6;
1263
+ const [start, count] = TRIT_SLICES[i];
1264
+ bw.write(pos, count, T >> start & (1 << count) - 1);
1265
+ pos += count;
1266
+ });
1267
+ }
1268
+ } else {
1269
+ values.forEach((v, i) => bw.write(17 + i * 8, 8, v));
1134
1270
  }
1135
1271
  for (let k = 0; k < 16; k++) {
1136
1272
  const w = indices[k];
@@ -1140,19 +1276,20 @@ function packBlock3(cls, e0, e1, indices) {
1140
1276
  }
1141
1277
  return bw.toBytes();
1142
1278
  }
1143
- var CLASS_BY_MODE = {
1144
- [BLOCK_MODE_4x4_2BIT]: CLASS_RGBA,
1145
- [BLOCK_MODE_4x4_3BIT]: CLASS_RGB,
1146
- [BLOCK_MODE_4x4_5BIT]: CLASS_LUM
1279
+ var CLASS_BY_MODE_CEM = {
1280
+ [`${BLOCK_MODE_4x4_2BIT}:${CEM_RGBA_DIRECT}`]: CLASS_RGBA,
1281
+ [`${BLOCK_MODE_4x4_3BIT}:${CEM_RGB_DIRECT}`]: CLASS_RGB,
1282
+ [`${BLOCK_MODE_4x4_4BIT}:${CEM_RGB_DIRECT}`]: CLASS_RGB_Q192,
1283
+ [`${BLOCK_MODE_4x4_5BIT}:${CEM_LUM_DIRECT}`]: CLASS_LUM
1147
1284
  };
1285
+ var SUPPORTED_MODES = /* @__PURE__ */ new Set([BLOCK_MODE_4x4_2BIT, BLOCK_MODE_4x4_3BIT, BLOCK_MODE_4x4_4BIT, BLOCK_MODE_4x4_5BIT]);
1148
1286
  function decodeASTC4x4Block(block) {
1149
1287
  if (block.length !== 16) {
1150
1288
  throw new Error(`decodeASTC4x4Block: expected 16 bytes, got ${block.length}`);
1151
1289
  }
1152
1290
  const br = new BitReader1282(block);
1153
1291
  const mode = br.read(0, 11);
1154
- const cls = CLASS_BY_MODE[mode];
1155
- if (!cls) {
1292
+ if (!SUPPORTED_MODES.has(mode)) {
1156
1293
  throw new Error(`decodeASTC4x4Block: unsupported block mode 0x${mode.toString(16)}`);
1157
1294
  }
1158
1295
  const partCount = br.read(11, 2);
@@ -1160,12 +1297,31 @@ function decodeASTC4x4Block(block) {
1160
1297
  throw new Error(`decodeASTC4x4Block: multi-partition blocks not supported (count=${partCount + 1})`);
1161
1298
  }
1162
1299
  const cem = br.read(13, 4);
1163
- if (cem !== cls.cem) {
1164
- throw new Error(`decodeASTC4x4Block: expected CEM ${cls.cem} with block mode 0x${mode.toString(16)}, got ${cem}`);
1300
+ const cls = CLASS_BY_MODE_CEM[`${mode}:${cem}`];
1301
+ if (!cls) {
1302
+ throw new Error(`decodeASTC4x4Block: unsupported CEM ${cem} with block mode 0x${mode.toString(16)}`);
1165
1303
  }
1166
1304
  const nVals = cls.channels.length * 2;
1167
1305
  const v = [];
1168
- for (let i = 0; i < nVals; i++) v.push(br.read(17 + i * 8, 8));
1306
+ if (cls.endpointRange === 192) {
1307
+ let pos = 17;
1308
+ for (let g = 0; g < nVals; g += 5) {
1309
+ const n = Math.min(5, nVals - g);
1310
+ const bits = [];
1311
+ let T = 0;
1312
+ for (let i = 0; i < n; i++) {
1313
+ bits.push(br.read(pos, 6));
1314
+ pos += 6;
1315
+ const [start, count] = TRIT_SLICES[i];
1316
+ T |= br.read(pos, count) << start;
1317
+ pos += count;
1318
+ }
1319
+ const t = decodeTrits(T);
1320
+ for (let i = 0; i < n; i++) v.push(unq192(t[i] << 6 | bits[i]));
1321
+ }
1322
+ } else {
1323
+ for (let i = 0; i < nVals; i++) v.push(br.read(17 + i * 8, 8));
1324
+ }
1169
1325
  let e0;
1170
1326
  let e1;
1171
1327
  if (cls.cem === CEM_LUM_DIRECT) {
package/dist/three.d.ts CHANGED
@@ -1,139 +1,18 @@
1
- import { TextureHint, PreferredFormat, FormatQuality, SvgRasterSize, TextureFormat, Encoder, EncoderImageSource } from './index.js';
2
- export { ASTC4x4Encoder, ASTC4x4WebGLEncoder, BC1Encoder, BC1WebGLEncoder, BC5Encoder, BC5WebGLEncoder, BC7Encoder, BC7EncoderOptions, BC7WebGLEncoder, Capabilities, ETC2Encoder, EncodeBytesResult, EncodeCallOptions, EncodeMipChainResult, EncodedLevelBytes, EncoderConstructor, EncoderOptions, ExtensionProvider, FeatureProvider, FormatSelection, FormatVariant, MipLevel, RasterizeSvgOptions, RawPixelSource, SelectFormatOptions, WebGLBlockEncoder, WebGLCapabilities, WebGLEncodeBytesResult, WebGLEncoderConstructor, WebGLEncoderImageSource, WebGLEncoderOptions, WebGLFormatSelection, WebGPUFeature, createWebGLContext, detectCapabilities, detectWebGLCapabilities, generateGpuMipChain, generateMipChain, getSharedWebGLContext, gpuMipLevelCount, isWebGLAvailable, padToBlockMultiple, rasterizeSvg, selectFormat, selectWebGLFormat } from './index.js';
1
+ import { CompressResult as CompressResult$1, CompressTextureSource, CompressOptions, TextureHint, PreferredFormat, FormatQuality, SvgRasterSize, TextureFormat, Encoder, EncoderImageSource } from './index.js';
2
+ export { ASTC4x4Encoder, ASTC4x4WebGLEncoder, BC1Encoder, BC1WebGLEncoder, BC5Encoder, BC5WebGLEncoder, BC7Encoder, BC7EncoderOptions, BC7WebGLEncoder, Capabilities, ETC2Encoder, EncodeBytesResult, EncodeCallOptions, EncodeMipChainResult, EncodedLevelBytes, EncoderConstructor, EncoderOptions, ExtensionProvider, FeatureProvider, FormatSelection, FormatVariant, MipLevel, RasterizeSvgOptions, RawPixelSource, SelectFormatOptions, WebGLBlockEncoder, WebGLCapabilities, WebGLEncodeBytesResult, WebGLEncoderConstructor, WebGLEncoderImageSource, WebGLEncoderOptions, WebGLFormatSelection, WebGPUFeature, clearTranscodeCache, compressTextureToBytes, createWebGLContext, detectCapabilities, detectWebGLCapabilities, generateGpuMipChain, generateMipChain, getSharedWebGLContext, gpuMipLevelCount, isWebGLAvailable, padToBlockMultiple, rasterizeSvg, releaseSharedGpuResources, selectFormat, selectWebGLFormat, setTranscodeCacheLimit } from './index.js';
3
3
  import { Texture, CompressedTexture, Loader, CompressedPixelFormat } from 'three';
4
4
 
5
- /**
6
- * Everything `compressTexture()` can take as an image source. A superset
7
- * of `EncoderImageSource` (see Encoder.ts) that also accepts URL strings
8
- * and Blob / File objects — the common cases in a web app.
9
- *
10
- * SVG works through all of these: a URL to an `.svg` file, a string of
11
- * inline SVG markup (detected by a leading `<`), an SVG Blob/File, or an
12
- * HTMLImageElement whose src is SVG. Vector sources are rasterised to RGBA
13
- * before encoding — see the `svgSize` option.
14
- */
15
- type CompressTextureSource = string | Blob | File | ImageBitmap | HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageData;
16
- interface CompressOptions {
17
- /** How the texture will be used. Drives format selection. Default 'color'. */
18
- hint?: TextureHint;
19
- /**
20
- * Prefer a specific format over the default choice when the device
21
- * supports it; falls back to the normal selection (BC7 → ASTC → ETC2 →
22
- * RGBA8) when it doesn't. Currently only 'bc1': half the memory of BC7
23
- * for opaque colour textures, at lower quality. Only honoured with
24
- * `hint: 'color'` — BC1 can't carry real alpha or normal maps.
25
- */
26
- preferredFormat?: PreferredFormat;
27
- /**
28
- * Memory/fidelity trade-off for opaque colour textures. Default 'high'
29
- * (BC7 / ASTC 4×4, 1 byte/pixel). 'low' picks the 4-bpp formats when the
30
- * device has one — BC1 on desktop-class GPUs, ETC2 RGB8 on mobile-class
31
- * ones — halving GPU memory at visibly lower quality on smooth content.
32
- * Ignored for `hint: 'colorWithAlpha'` and `hint: 'normal'` (the 4-bpp
33
- * formats can't carry them). On the WebGL fallback tier only BC1 is
34
- * available at 'low'.
35
- */
36
- quality?: FormatQuality;
37
- /** Pick the sRGB or linear variant of the chosen format. Default 'srgb'. */
38
- colorSpace?: 'srgb' | 'linear';
39
- /**
40
- * Rasterisation size for SVG sources. A number scales the SVG so its
41
- * longest side matches (aspect ratio preserved); `{ width, height }`
42
- * rasterises at exactly that size. Default: the SVG's intrinsic size
43
- * (absolute width/height attributes, else the viewBox dimensions).
44
- * Ignored for non-SVG sources.
45
- */
46
- svgSize?: SvgRasterSize;
47
- /** Flip the image vertically before encoding. Default true (matches Three.js convention). */
48
- flipY?: boolean;
49
- /** Generate a full mip chain down to 1×1 on the CPU, encode every level. */
50
- mipmaps?: boolean;
51
- /** Reuse an existing device (e.g. Three.js's renderer device) instead
52
- * of creating a new one. WebGPU path only. When provided, the encoder
53
- * never destroys it. */
54
- device?: GPUDevice;
55
- adapter?: GPUAdapter;
56
- /**
57
- * Keep the compressed bytes in a session-scoped in-memory LRU and reuse
58
- * them on repeat calls, skipping BOTH the image decode and the encode —
59
- * the dominant costs. Re-loading a texture later in the session (e.g.
60
- * two worlds sharing an atlas) becomes a few ms. Keyed by source
61
- * identity + selected format + encode options; capped at 256 MiB of
62
- * compressed bytes by default (`setTranscodeCacheLimit()` to tune) and
63
- * never touches persistent storage. Default false.
64
- *
65
- * URL and Blob/File sources get an identity automatically (URL string or
66
- * content hash). Pixel sources (ImageBitmap, canvas, ImageData) are only
67
- * cached when `cacheKey` is provided.
68
- */
69
- cache?: boolean;
70
- /**
71
- * Explicit cache identity for the source, overriding the derived one.
72
- * Use when you already know a stable name (e.g. an asset path) and want
73
- * to skip content hashing, or to make pixel sources cacheable.
74
- */
75
- cacheKey?: string;
76
- }
77
- interface CompressResult {
78
- /** CompressedTexture on a compressed path; Texture on RGBA8 fallback. */
5
+ /** A `compressTexture()` result: a ready-to-use texture plus the same encode
6
+ * metadata `compressTextureToBytes()` returns (minus the raw `levels`). */
7
+ interface CompressResult extends Omit<CompressResult$1, 'levels' | 'fallbackBitmap'> {
8
+ /** `CompressedTexture` on a compressed path; a plain `Texture` on the RGBA8 fallback. */
79
9
  texture: Texture | CompressedTexture;
80
- /** The compressed format selected, or null when we fell back to RGBA8. */
81
- format: TextureFormat | null;
82
- /** True iff we returned an uncompressed Texture because no encoder fit. */
83
- fallbackUncompressed: boolean;
84
- /**
85
- * Which backend produced the result. 'webgpu' = compute path, 'webgl' =
86
- * fragment-shader fallback, 'none' = uncompressed RGBA8.
87
- */
88
- backend: 'webgpu' | 'webgl' | 'none';
89
- /**
90
- * True iff the chosen format is ASTC and the hint was 'normal'. The
91
- * caller must apply the (R, W) → (x, y) swizzle in the material — ASTC
92
- * has no 2-channel mode, so normal maps ride the RGBA path.
93
- */
94
- astcNormalRemap: boolean;
95
- width: number;
96
- height: number;
97
- mipLevels: number;
98
- /** Wall-clock time of GPU encoding, summed across mip levels. */
99
- encodeMs: number;
100
- /**
101
- * Wall-clock time to turn the source into decoded RGBA pixels: fetch /
102
- * base64 decode, image decode, SVG rasterisation. Usually the dominant
103
- * cost for large images — when a load feels slower than `encodeMs`
104
- * suggests, this is where the time went.
105
- */
106
- decodeMs: number;
107
- /** Wall-clock time of the whole `compressTexture()` call: decode + CPU
108
- * mip generation + encode + texture assembly. */
109
- totalMs: number;
110
- /** True when the result came from the in-memory transcode cache (the
111
- * `cache` option) — no decode or encode ran; decodeMs/encodeMs are 0. */
112
- cacheHit: boolean;
113
- /** Dispose the texture and release GPU resources owned by this call.
114
- * On the default path (no `device`/`adapter` option) the encoder and
115
- * device are shared across `compressTexture()` calls and survive this —
116
- * release those with `releaseSharedGpuResources()`. */
10
+ /** Dispose the texture. The shared encoder/device survive — release those
11
+ * with `releaseSharedGpuResources()`. */
117
12
  destroy(): void;
118
13
  }
119
- /**
120
- * Destroy the WebGPU device and encoders that `compressTexture()` shares
121
- * across calls (created lazily when neither the `device` nor the `adapter`
122
- * option is passed). Safe to call at any time — in-flight encodes on the
123
- * shared device will fail, and the next `compressTexture()` call recreates
124
- * everything. No-op when nothing is cached.
125
- */
126
- declare function releaseSharedGpuResources(): void;
127
14
  declare function compressTexture(source: CompressTextureSource, options?: CompressOptions): Promise<CompressResult>;
128
15
 
129
- /**
130
- * Cap the cache's total compressed payload in bytes (default 256 MiB).
131
- * Lower it to evict immediately; 0 disables caching entirely.
132
- */
133
- declare function setTranscodeCacheLimit(bytes: number): void;
134
- /** Drop every cached transcode. Textures already built from entries are unaffected. */
135
- declare function clearTranscodeCache(): void;
136
-
137
16
  declare class GputexLoader extends Loader<Texture> {
138
17
  /** Format-selection hint. Default 'color'. */
139
18
  hint: TextureHint;
@@ -230,4 +109,4 @@ interface EncodeToTextureOptions {
230
109
  */
231
110
  declare function encodeToTexture(encoder: Encoder, source: EncoderImageSource, { colorSpace, flipY }?: EncodeToTextureOptions): Promise<EncodeResult>;
232
111
 
233
- export { type CompressOptions, type CompressResult, type CompressTextureSource, type EncodeResult, type EncodeToTextureOptions, Encoder, EncoderImageSource, FormatQuality, GputexLoader, PreferredFormat, SvgRasterSize, TextureFormat, TextureHint, buildCompressedTexture, clearTranscodeCache, compressTexture, encodeToTexture, releaseSharedGpuResources, setTranscodeCacheLimit, threeFormatFor };
112
+ export { CompressOptions, type CompressResult, CompressTextureSource, type EncodeResult, type EncodeToTextureOptions, Encoder, EncoderImageSource, FormatQuality, GputexLoader, PreferredFormat, SvgRasterSize, TextureFormat, TextureHint, buildCompressedTexture, compressTexture, encodeToTexture, threeFormatFor };