@galacean/effects-plugin-ktx2 2.9.0-alpha.2 → 2.9.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/index.mjs CHANGED
@@ -3,11 +3,11 @@
3
3
  * Description: Galacean Effects player Khronos Texture 2.0 plugin
4
4
  * Author: Ant Group CO., Ltd.
5
5
  * Contributors: 澄弈
6
- * Version: v2.9.0-alpha.2
6
+ * Version: v2.9.0
7
7
  */
8
8
 
9
9
  import * as EFFECTS from '@galacean/effects';
10
- import { glContext, TextureSourceType, textureLoaderRegistry, loadBinary, logger } from '@galacean/effects';
10
+ import { glContext, TextureSourceType, Plugin, textureLoaderRegistry, loadBinary, registerPlugin, logger } from '@galacean/effects';
11
11
 
12
12
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
13
13
  try {
@@ -36,6 +36,96 @@ function _async_to_generator(fn) {
36
36
  };
37
37
  }
38
38
 
39
+ function _set_prototype_of(o, p) {
40
+ _set_prototype_of = Object.setPrototypeOf || function setPrototypeOf(o, p) {
41
+ o.__proto__ = p;
42
+ return o;
43
+ };
44
+ return _set_prototype_of(o, p);
45
+ }
46
+
47
+ function _inherits(subClass, superClass) {
48
+ if (typeof superClass !== "function" && superClass !== null) {
49
+ throw new TypeError("Super expression must either be null or a function");
50
+ }
51
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
52
+ constructor: {
53
+ value: subClass,
54
+ writable: true,
55
+ configurable: true
56
+ }
57
+ });
58
+ if (superClass) _set_prototype_of(subClass, superClass);
59
+ }
60
+
61
+ function _is_native_reflect_construct() {
62
+ // Since Reflect.construct can't be properly polyfilled, some
63
+ // implementations (e.g. core-js@2) don't set the correct internal slots.
64
+ // Those polyfills don't allow us to subclass built-ins, so we need to
65
+ // use our fallback implementation.
66
+ try {
67
+ // If the internal slots aren't set, this throws an error similar to
68
+ // TypeError: this is not a Boolean object.
69
+ var result = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
70
+ } catch (_) {}
71
+ return (_is_native_reflect_construct = function _is_native_reflect_construct() {
72
+ return !!result;
73
+ })();
74
+ }
75
+
76
+ function _construct(Parent, args, Class) {
77
+ if (_is_native_reflect_construct()) _construct = Reflect.construct;
78
+ else {
79
+ _construct = function construct(Parent, args, Class) {
80
+ var a = [
81
+ null
82
+ ];
83
+ a.push.apply(a, args);
84
+ var Constructor = Function.bind.apply(Parent, a);
85
+ var instance = new Constructor();
86
+ if (Class) _set_prototype_of(instance, Class.prototype);
87
+ return instance;
88
+ };
89
+ }
90
+ return _construct.apply(null, arguments);
91
+ }
92
+
93
+ function _get_prototype_of(o) {
94
+ _get_prototype_of = Object.setPrototypeOf ? Object.getPrototypeOf : function getPrototypeOf(o) {
95
+ return o.__proto__ || Object.getPrototypeOf(o);
96
+ };
97
+ return _get_prototype_of(o);
98
+ }
99
+
100
+ function _is_native_function(fn) {
101
+ return Function.toString.call(fn).indexOf("[native code]") !== -1;
102
+ }
103
+
104
+ function _wrap_native_super(Class) {
105
+ var _cache = typeof Map === "function" ? new Map() : undefined;
106
+ _wrap_native_super = function _wrap_native_super(Class) {
107
+ if (Class === null || !_is_native_function(Class)) return Class;
108
+ if (typeof Class !== "function") throw new TypeError("Super expression must either be null or a function");
109
+ if (typeof _cache !== "undefined") {
110
+ if (_cache.has(Class)) return _cache.get(Class);
111
+ _cache.set(Class, Wrapper);
112
+ }
113
+ function Wrapper() {
114
+ return _construct(Class, arguments, _get_prototype_of(this).constructor);
115
+ }
116
+ Wrapper.prototype = Object.create(Class.prototype, {
117
+ constructor: {
118
+ value: Wrapper,
119
+ enumerable: false,
120
+ writable: true,
121
+ configurable: true
122
+ }
123
+ });
124
+ return _set_prototype_of(Wrapper, Class);
125
+ };
126
+ return _wrap_native_super(Class);
127
+ }
128
+
39
129
  function __generator(thisArg, body) {
40
130
  var _ = {
41
131
  label: 0,
@@ -134,7 +224,6 @@ typeof SuppressedError === "function" ? SuppressedError : function _SuppressedEr
134
224
 
135
225
  /**
136
226
  * KTX2 transcode target format.
137
- * if you modify this file, please also modify KTX2TargetFormat in binomial-workercode.ts
138
227
  */ var KTX2TargetFormat;
139
228
  (function(KTX2TargetFormat) {
140
229
  /** RGB(A) compressed format, 128 bits per 4x4 pixel block. */ KTX2TargetFormat[KTX2TargetFormat["ASTC"] = 0] = "ASTC";
@@ -161,6 +250,41 @@ function _create_class(Constructor, protoProps, staticProps) {
161
250
  return Constructor;
162
251
  }
163
252
 
253
+ function _array_like_to_array(arr, len) {
254
+ if (len == null || len > arr.length) len = arr.length;
255
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
256
+ return arr2;
257
+ }
258
+
259
+ function _unsupported_iterable_to_array(o, minLen) {
260
+ if (!o) return;
261
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
262
+ var n = Object.prototype.toString.call(o).slice(8, -1);
263
+ if (n === "Object" && o.constructor) n = o.constructor.name;
264
+ if (n === "Map" || n === "Set") return Array.from(n);
265
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
266
+ }
267
+
268
+ function _create_for_of_iterator_helper_loose(o, allowArrayLike) {
269
+ var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
270
+ if (it) return (it = it.call(o)).next.bind(it);
271
+ // Fallback for engines without symbol support
272
+ if (Array.isArray(o) || (it = _unsupported_iterable_to_array(o)) || allowArrayLike && o && typeof o.length === "number") {
273
+ if (it) o = it;
274
+ var i = 0;
275
+ return function() {
276
+ if (i >= o.length) return {
277
+ done: true
278
+ };
279
+ return {
280
+ done: false,
281
+ value: o[i++]
282
+ };
283
+ };
284
+ }
285
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
286
+ }
287
+
164
288
  /**
165
289
  * @internal
166
290
  * WorkerPool, T 为发送消息的类型,U 为返回值的类型。
@@ -181,7 +305,7 @@ function _create_class(Constructor, protoProps, staticProps) {
181
305
  var _proto = WorkerPool.prototype;
182
306
  _proto.prepareWorker = function prepareWorker() {
183
307
  var count = this.limitedCount;
184
- var promises = new Array(count);
308
+ var promises = [];
185
309
  for(var i = 0; i < count; i++){
186
310
  promises.push(this.ensureWorker(i));
187
311
  }
@@ -529,6 +653,16 @@ var KTX2Container = /*#__PURE__*/ function() {
529
653
  this.parse(buffer);
530
654
  }
531
655
  var _proto = KTX2Container.prototype;
656
+ /**
657
+ * 释放内部持有的原始二进制数据引用
658
+ */ _proto.clear = function clear() {
659
+ for(var _iterator = _create_for_of_iterator_helper_loose(this.levels), _step; !(_step = _iterator()).done;){
660
+ var level = _step.value;
661
+ level.levelData = null;
662
+ }
663
+ this.levels.length = 0;
664
+ this.globalData = null;
665
+ };
532
666
  _proto.parse = function parse(data) {
533
667
  var KTX2_IDENTIFIER = new Uint8Array([
534
668
  0xAB,
@@ -716,28 +850,6 @@ var KTX2Container = /*#__PURE__*/ function() {
716
850
  return KTX2Container;
717
851
  }();
718
852
 
719
- function _set_prototype_of(o, p) {
720
- _set_prototype_of = Object.setPrototypeOf || function setPrototypeOf(o, p) {
721
- o.__proto__ = p;
722
- return o;
723
- };
724
- return _set_prototype_of(o, p);
725
- }
726
-
727
- function _inherits(subClass, superClass) {
728
- if (typeof superClass !== "function" && superClass !== null) {
729
- throw new TypeError("Super expression must either be null or a function");
730
- }
731
- subClass.prototype = Object.create(superClass && superClass.prototype, {
732
- constructor: {
733
- value: subClass,
734
- writable: true,
735
- configurable: true
736
- }
737
- });
738
- if (superClass) _set_prototype_of(subClass, superClass);
739
- }
740
-
741
853
  /* eslint-disable compat/compat */ /* eslint-disable promise/no-nesting */ /**
742
854
  * 转码核心代码
743
855
  * 主线程调用时会返回 API 对象
@@ -747,27 +859,38 @@ function _inherits(subClass, superClass) {
747
859
  /**
748
860
  * ZSTD (Zstandard) decoder.
749
861
  */ var ZSTDDecoder = /*#__PURE__*/ function() {
750
- function ZSTDDecoder() {}
862
+ function ZSTDDecoder() {
863
+ var _this = this;
864
+ this.heap = new Uint8Array(0);
865
+ this.instance = null;
866
+ this.importObject = {
867
+ env: {
868
+ emscripten_notify_memory_growth: function() {
869
+ _this.heap = new Uint8Array(_this.instance.exports.memory.buffer);
870
+ }
871
+ }
872
+ };
873
+ }
751
874
  var _proto = ZSTDDecoder.prototype;
752
875
  _proto.init = function init(zstddecWasmModule) {
876
+ var _this = this;
753
877
  if (!this.initPromise) {
754
- this.initPromise = WebAssembly.instantiate(zstddecWasmModule, ZSTDDecoder.IMPORT_OBJECT).then(this.initInstance);
878
+ this.initPromise = WebAssembly.instantiate(zstddecWasmModule, this.importObject).then(function(result) {
879
+ _this.instance = result;
880
+ _this.importObject.env.emscripten_notify_memory_growth();
881
+ });
755
882
  }
756
883
  return this.initPromise;
757
884
  };
758
- _proto.initInstance = function initInstance(result) {
759
- ZSTDDecoder.instance = result;
760
- ZSTDDecoder.IMPORT_OBJECT.env.emscripten_notify_memory_growth();
761
- };
762
885
  _proto.decode = function decode(array, uncompressedSize) {
763
886
  if (uncompressedSize === void 0) uncompressedSize = 0;
764
- if (!ZSTDDecoder.instance) {
887
+ if (!this.instance) {
765
888
  throw new Error("ZSTDDecoder: Await .init() before decoding.");
766
889
  }
767
- var exports = ZSTDDecoder.instance.exports;
890
+ var exports = this.instance.exports;
768
891
  var compressedSize = array.byteLength;
769
892
  var compressedPtr = exports.malloc(compressedSize);
770
- ZSTDDecoder.heap.set(array, compressedPtr);
893
+ this.heap.set(array, compressedPtr);
771
894
  uncompressedSize = uncompressedSize || Number(exports.ZSTD_findDecompressedSize(compressedPtr, compressedSize));
772
895
  var uncompressedPtr = exports.malloc(uncompressedSize);
773
896
  var actualSize = exports.ZSTD_decompress(uncompressedPtr, uncompressedSize, compressedPtr, compressedSize);
@@ -777,20 +900,13 @@ function _inherits(subClass, superClass) {
777
900
  throw new Error("ZSTDDecoder: decompression failed.");
778
901
  }
779
902
  // Read decompressed data and free WASM memory
780
- var dec = ZSTDDecoder.heap.slice(uncompressedPtr, uncompressedPtr + actualSize);
903
+ var dec = this.heap.slice(uncompressedPtr, uncompressedPtr + actualSize);
781
904
  exports.free(compressedPtr);
782
905
  exports.free(uncompressedPtr);
783
906
  return dec;
784
907
  };
785
908
  return ZSTDDecoder;
786
909
  }();
787
- ZSTDDecoder.IMPORT_OBJECT = {
788
- env: {
789
- emscripten_notify_memory_growth: function emscripten_notify_memory_growth() {
790
- ZSTDDecoder.heap = new Uint8Array(ZSTDDecoder.instance.exports.memory.buffer);
791
- }
792
- }
793
- };
794
910
  function transcodeASTC(wasmTranscoder, compressedData, width, height) {
795
911
  var nBlocks = (width + 3 >> 2) * (height + 3 >> 2);
796
912
  var texMemoryPages = nBlocks * 16 + 65535 >> 16;
@@ -859,35 +975,45 @@ function _inherits(subClass, superClass) {
859
975
  return result;
860
976
  });
861
977
  }
862
- self.onmessage = function onmessage(event) {
863
- var message = event.data;
864
- switch(message.type){
865
- case "init":
866
- initTranscoder(message.transcoderWasm).then(function() {
867
- self.postMessage("init-completed");
868
- }).catch(function(error) {
869
- self.postMessage({
870
- error: error
871
- });
872
- });
873
- break;
874
- case "transcode":
875
- wasmPromise.then(function(transcoderWasmModule) {
876
- transcode(message.data, message.needZstd, transcoderWasmModule, message.zstddecWasmModule).then(function(decodedData) {
877
- self.postMessage(decodedData);
978
+ // 仅在 Worker 环境中注册消息处理,避免在主线程调用时污染 window.onmessage
979
+ // Worker 环境中没有 window 对象
980
+ if (typeof window === "undefined") {
981
+ self.onmessage = function onmessage(event) {
982
+ var message = event.data;
983
+ switch(message.type){
984
+ case "init":
985
+ initTranscoder(message.transcoderWasm).then(function() {
986
+ self.postMessage("init-completed");
878
987
  }).catch(function(error) {
879
- return self.postMessage({
988
+ self.postMessage({
880
989
  error: error
881
990
  });
882
991
  });
883
- }).catch(function(error) {
884
- self.postMessage({
885
- error: error
992
+ break;
993
+ case "transcode":
994
+ if (!wasmPromise) {
995
+ self.postMessage({
996
+ error: new Error("Transcoder not initialized.")
997
+ });
998
+ break;
999
+ }
1000
+ wasmPromise.then(function(transcoderWasmModule) {
1001
+ transcode(message.data, message.needZstd, transcoderWasmModule, message.zstddecWasmModule).then(function(decodedData) {
1002
+ self.postMessage(decodedData);
1003
+ }).catch(function(error) {
1004
+ return self.postMessage({
1005
+ error: error
1006
+ });
1007
+ });
1008
+ }).catch(function(error) {
1009
+ self.postMessage({
1010
+ error: error
1011
+ });
886
1012
  });
887
- });
888
- break;
889
- }
890
- };
1013
+ break;
1014
+ }
1015
+ };
1016
+ }
891
1017
  // 主线程使用
892
1018
  return {
893
1019
  ZSTDDecoder: ZSTDDecoder,
@@ -1032,7 +1158,7 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1032
1158
  _proto.initTranscodeWorkerPool = function initTranscodeWorkerPool() {
1033
1159
  var _this = this;
1034
1160
  return _async_to_generator(function() {
1035
- var transcoderWasm, funcCode, funcBody, returnIndex, workerCode, workerURL;
1161
+ var transcoderWasm, workerEntryCode, workerURL;
1036
1162
  return __generator(this, function(_state) {
1037
1163
  switch(_state.label){
1038
1164
  case 0:
@@ -1058,13 +1184,10 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1058
1184
  ];
1059
1185
  case 3:
1060
1186
  transcoderWasm = _state.sent();
1061
- funcCode = TranscodeWorkerCode.toString();
1062
- funcBody = funcCode.substring(funcCode.indexOf("{") + 1, funcCode.lastIndexOf("}"));
1063
- // 移除主线程的 return 语句,添加 Worker 消息处理
1064
- returnIndex = funcBody.lastIndexOf("return {");
1065
- workerCode = funcBody.substring(0, returnIndex);
1187
+ // TranscodeWorkerCode 整体序列化
1188
+ workerEntryCode = "\n (" + TranscodeWorkerCode.toString() + ")();\n ";
1066
1189
  workerURL = URL.createObjectURL(new Blob([
1067
- workerCode
1190
+ workerEntryCode
1068
1191
  ], {
1069
1192
  type: "application/javascript"
1070
1193
  }));
@@ -1077,17 +1200,21 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1077
1200
  });
1078
1201
  })();
1079
1202
  };
1080
- _proto.transcode = function transcode(ktx2Container) {
1203
+ _proto.transcode = function transcode(ktx2Container, neededLevelCount) {
1081
1204
  var _this = this;
1082
1205
  return _async_to_generator(function() {
1083
- var needZstd, levelCount, faceCount, encodedData, faceIndex, mipmapData, mipmapIndex, level, levelWidth, levelHeight, originBuffer, originOffset, originByteLength, zstddecWasmModule, _tmp, faces, postMessageData;
1206
+ var needZstd, availableLevelCount, levelCount, faceCount, encodedData, faceIndex, mipmapData, mipmapIndex, level, levelWidth, levelHeight, originBuffer, originOffset, originByteLength, zstddecWasmModule, _tmp, faces, postMessageData;
1084
1207
  return __generator(this, function(_state) {
1085
1208
  switch(_state.label){
1086
1209
  case 0:
1087
1210
  needZstd = ktx2Container.supercompressionScheme === SupercompressionScheme.Zstd;
1088
- levelCount = ktx2Container.levels.length;
1211
+ availableLevelCount = ktx2Container.levels.length;
1212
+ levelCount = neededLevelCount != null ? neededLevelCount : availableLevelCount;
1213
+ if (levelCount < 1 || levelCount > availableLevelCount) {
1214
+ throw new Error("neededLevelCount " + levelCount + " is out of range [1, " + availableLevelCount + "].");
1215
+ }
1089
1216
  faceCount = ktx2Container.faceCount;
1090
- // 准备编码数据
1217
+ // 准备编码数据,只处理需要的 level,避免转码后丢弃多余结果
1091
1218
  encodedData = new Array(faceCount);
1092
1219
  for(faceIndex = 0; faceIndex < faceCount; faceIndex++){
1093
1220
  mipmapData = new Array(levelCount);
@@ -1142,6 +1269,9 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1142
1269
  ];
1143
1270
  case 5:
1144
1271
  // WebWorker 模式
1272
+ if (!_this.transcodeWorkerPool) {
1273
+ throw new Error("KhronosTranscoder: transcodeWorkerPool is not initialized.");
1274
+ }
1145
1275
  postMessageData = {
1146
1276
  type: "transcode",
1147
1277
  format: 0,
@@ -1187,14 +1317,32 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1187
1317
  }(TextureTranscoder);
1188
1318
 
1189
1319
  /**
1190
- * KTX2 加载器 - 专用于 UASTC 转 ASTC
1191
- */ var KTX2Loader = /*#__PURE__*/ function() {
1320
+ * KTX2 加载器插件
1321
+ */ var KTX2Loader = /*#__PURE__*/ function(Plugin) {
1322
+ _inherits(KTX2Loader, Plugin);
1192
1323
  function KTX2Loader(workerCount) {
1193
1324
  if (workerCount === void 0) workerCount = 0;
1194
- this.workerCount = workerCount;
1195
- this.khronosTranscoder = null;
1325
+ var _this;
1326
+ _this = Plugin.call(this) || this;
1327
+ _this.workerCount = workerCount;
1328
+ _this.khronosTranscoder = null;
1329
+ return _this;
1196
1330
  }
1197
1331
  var _proto = KTX2Loader.prototype;
1332
+ _proto.onAssetsLoadStart = function onAssetsLoadStart(_scene, _options) {
1333
+ var _this = this;
1334
+ return _async_to_generator(function() {
1335
+ return __generator(this, function(_state) {
1336
+ // 仅在未手动注册时才自动注册,避免覆盖用户通过 registerKTX2Loader(workerCount) 的自定义配置
1337
+ if (!textureLoaderRegistry.has("ktx2")) {
1338
+ registerKTX2Loader(_this.workerCount);
1339
+ }
1340
+ return [
1341
+ 2
1342
+ ];
1343
+ });
1344
+ })();
1345
+ };
1198
1346
  /**
1199
1347
  * 初始化 Khronos Transcoder
1200
1348
  */ _proto.initKhronosTranscoder = function initKhronosTranscoder() {
@@ -1282,7 +1430,7 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1282
1430
  */ _proto.loadFromBuffer = function loadFromBuffer(arrBuffer) {
1283
1431
  var _this = this;
1284
1432
  return _async_to_generator(function() {
1285
- var buffer, _ref, ktx2Container, result;
1433
+ var buffer, _ref, ktx2Container, result, hasFullMipmapChain, textureOptions;
1286
1434
  return __generator(this, function(_state) {
1287
1435
  switch(_state.label){
1288
1436
  case 0:
@@ -1292,10 +1440,13 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1292
1440
  _this.parseBuffer(buffer)
1293
1441
  ];
1294
1442
  case 1:
1295
- _ref = _state.sent(), ktx2Container = _ref.ktx2Container, result = _ref.result;
1443
+ _ref = _state.sent(), ktx2Container = _ref.ktx2Container, result = _ref.result, hasFullMipmapChain = _ref.hasFullMipmapChain;
1444
+ textureOptions = _this.createTextureByBuffer(ktx2Container, result, hasFullMipmapChain);
1445
+ // 转码完成后释放原始 KTX2 数据
1446
+ ktx2Container.clear();
1296
1447
  return [
1297
1448
  2,
1298
- _this.createTextureByBuffer(ktx2Container, result)
1449
+ textureOptions
1299
1450
  ];
1300
1451
  }
1301
1452
  });
@@ -1306,7 +1457,7 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1306
1457
  */ _proto.loadFromURL = function loadFromURL(url) {
1307
1458
  var _this = this;
1308
1459
  return _async_to_generator(function() {
1309
- var buffer, _, _ref, ktx2Container, result;
1460
+ var buffer, _, _ref, ktx2Container, result, hasFullMipmapChain, textureOptions;
1310
1461
  return __generator(this, function(_state) {
1311
1462
  switch(_state.label){
1312
1463
  case 0:
@@ -1325,10 +1476,13 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1325
1476
  _this.parseBuffer(buffer)
1326
1477
  ];
1327
1478
  case 2:
1328
- _ref = _state.sent(), ktx2Container = _ref.ktx2Container, result = _ref.result;
1479
+ _ref = _state.sent(), ktx2Container = _ref.ktx2Container, result = _ref.result, hasFullMipmapChain = _ref.hasFullMipmapChain;
1480
+ textureOptions = _this.createTextureByBuffer(ktx2Container, result, hasFullMipmapChain);
1481
+ // 转码完成后释放原始 KTX2 数据
1482
+ ktx2Container.clear();
1329
1483
  return [
1330
1484
  2,
1331
- _this.createTextureByBuffer(ktx2Container, result)
1485
+ textureOptions
1332
1486
  ];
1333
1487
  }
1334
1488
  });
@@ -1336,11 +1490,11 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1336
1490
  };
1337
1491
  /**
1338
1492
  * @internal
1339
- * 解析并转码 KTX2 文件
1493
+ * 解析并转码 KTX2 文件 - 专用于 UASTC 转 ASTC
1340
1494
  */ _proto.parseBuffer = function parseBuffer(buffer) {
1341
1495
  var _this = this;
1342
1496
  return _async_to_generator(function() {
1343
- var ktx2Container, transcoder, result;
1497
+ var ktx2Container, pixelWidth, pixelHeight, maxDimension, availableLevelCount, fullChainCount, hasFullMipmapChain, neededLevelCount, transcoder, result;
1344
1498
  return __generator(this, function(_state) {
1345
1499
  switch(_state.label){
1346
1500
  case 0:
@@ -1349,6 +1503,13 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1349
1503
  if (!ktx2Container.isUASTC) {
1350
1504
  throw new Error("Unsupported KTX2: only UASTC format is supported");
1351
1505
  }
1506
+ // 提前判断需要转码的 level 数量,避免转码后丢弃多余结果
1507
+ pixelWidth = ktx2Container.pixelWidth, pixelHeight = ktx2Container.pixelHeight;
1508
+ maxDimension = Math.max(pixelWidth, pixelHeight);
1509
+ availableLevelCount = ktx2Container.levels.length;
1510
+ fullChainCount = maxDimension > 0 ? Math.floor(Math.log2(maxDimension)) + 1 : 1;
1511
+ hasFullMipmapChain = availableLevelCount > 1 && availableLevelCount >= fullChainCount;
1512
+ neededLevelCount = hasFullMipmapChain ? availableLevelCount : 1;
1352
1513
  return [
1353
1514
  4,
1354
1515
  _this.ensureKhronosTranscoder()
@@ -1357,7 +1518,7 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1357
1518
  transcoder = _state.sent();
1358
1519
  return [
1359
1520
  4,
1360
- transcoder.transcode(ktx2Container)
1521
+ transcoder.transcode(ktx2Container, neededLevelCount)
1361
1522
  ];
1362
1523
  case 2:
1363
1524
  result = _state.sent();
@@ -1365,7 +1526,8 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1365
1526
  2,
1366
1527
  {
1367
1528
  ktx2Container: ktx2Container,
1368
- result: result
1529
+ result: result,
1530
+ hasFullMipmapChain: hasFullMipmapChain
1369
1531
  }
1370
1532
  ];
1371
1533
  }
@@ -1375,21 +1537,19 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1375
1537
  /**
1376
1538
  * @internal
1377
1539
  * 根据转码结果创建引擎所需的压缩纹理源选项
1378
- */ _proto.createTextureByBuffer = function createTextureByBuffer(ktx2Container, transcodeResult) {
1540
+ * @param hasFullMipmapChain - 是否包含完整 mipmap 链(由 parseBuffer 提前判断)
1541
+ */ _proto.createTextureByBuffer = function createTextureByBuffer(ktx2Container, transcodeResult, hasFullMipmapChain) {
1379
1542
  var _faces_;
1380
1543
  var pixelWidth = ktx2Container.pixelWidth, pixelHeight = ktx2Container.pixelHeight, faceCount = ktx2Container.faceCount;
1381
1544
  var _this_getASTC4x4TextureDetail = this.getASTC4x4TextureDetail(), internalFormat = _this_getASTC4x4TextureDetail.internalFormat, format = _this_getASTC4x4TextureDetail.format, type = _this_getASTC4x4TextureDetail.type;
1545
+ if (Math.max(pixelWidth, pixelHeight) === 0) {
1546
+ throw new Error("Invalid KTX2 texture: both width and height are zero");
1547
+ }
1382
1548
  var target = faceCount === 6 ? glContext.TEXTURE_CUBE_MAP : glContext.TEXTURE_2D;
1383
1549
  var faces = transcodeResult.faces;
1384
1550
  var _faces__length;
1385
- var transLevels = (_faces__length = (_faces_ = faces[0]) == null ? void 0 : _faces_.length) != null ? _faces__length : 0;
1386
- var maxDimension = Math.max(pixelWidth, pixelHeight);
1387
- if (maxDimension === 0) {
1388
- throw new Error("Invalid KTX2 texture: both width and height are zero");
1389
- }
1390
- var fullChainCount = Math.floor(Math.log2(maxDimension)) + 1;
1391
- var useMipmaps = transLevels > 1 && transLevels >= fullChainCount;
1392
- var levelCount = useMipmaps ? transLevels : 1;
1551
+ // 转码时已按 neededLevelCount 截断,直接使用全部转码结果
1552
+ var levelCount = hasFullMipmapChain ? (_faces__length = (_faces_ = faces[0]) == null ? void 0 : _faces_.length) != null ? _faces__length : 0 : 1;
1393
1553
  var mipmaps = [];
1394
1554
  for(var level = 0; level < levelCount; level++){
1395
1555
  for(var face = 0; face < faceCount; face++){
@@ -1430,7 +1590,7 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1430
1590
  this.khronosInitPromise = undefined;
1431
1591
  };
1432
1592
  return KTX2Loader;
1433
- }();
1593
+ }(_wrap_native_super(Plugin));
1434
1594
  /**
1435
1595
  * 注册 KTX2 加载器到全局注册表
1436
1596
  * @param workerCount - 大于 0 时使用 WebWorker,默认使用主线程
@@ -1448,8 +1608,8 @@ function zstddecWasm(imports){return _loadWasmModule(0, null, 'AGFzbQEAAAABpQEVY
1448
1608
 
1449
1609
  /**
1450
1610
  * 插件版本号
1451
- */ var version = "2.9.0-alpha.2";
1452
- registerKTX2Loader();
1611
+ */ var version = "2.9.0";
1612
+ registerPlugin("ktx2", KTX2Loader);
1453
1613
  logger.info("Plugin ktx2 version: " + version + ".");
1454
1614
  if (version !== EFFECTS.version) {
1455
1615
  console.error("注意:请统一 KTX2 插件与 Player 版本,不统一的版本混用会有不可预知的后果!", "\nAttention: Please ensure the KTX2 plugin is synchronized with the Player version. Mixing and matching incompatible versions may result in unpredictable consequences!");