Bladeren bron

Rework the worker thread function

Popov72 5 jaren geleden
bovenliggende
commit
5c60d390ee
4 gewijzigde bestanden met toevoegingen van 951 en 186 verwijderingen
  1. 127 0
      src/Misc/KTX2/KTX2WorkerThread.ts
  2. 818 0
      src/Misc/KTX2/KTX2WorkerThreadJS.ts
  3. 1 6
      src/Misc/KTX2/index.ts
  4. 5 180
      src/Misc/KTX2/khronosTextureContainer2.ts

+ 127 - 0
src/Misc/KTX2/KTX2WorkerThread.ts

@@ -0,0 +1,127 @@
+import { TranscoderManager } from "./transcoderManager";
+import { sourceTextureFormat, transcodeTarget } from './transcoder';
+import { KTX2FileReader, supercompressionScheme, IKTX2_ImageDesc } from './KTX2FileReader';
+import { Nullable } from '../../types';
+import { LiteTranscoder_UASTC_ASTC } from './LiteTranscoder_UASTC_ASTC';
+import { LiteTranscoder_UASTC_BC7 } from './LiteTranscoder_UASTC_BC7';
+import { MSCTranscoder } from './mscTranscoder';
+
+declare function postMessage(message: any, transfer?: any[]): void;
+
+const COMPRESSED_RGBA_BPTC_UNORM_EXT = 36492;
+
+export interface IMipmap {
+    data: Nullable<Uint8Array>;
+    width: number;
+    height: number;
+    transcodedFormat: number;
+}
+
+export function workerFunc(): void {
+    TranscoderManager.registerTranscoder(LiteTranscoder_UASTC_ASTC);
+    TranscoderManager.registerTranscoder(LiteTranscoder_UASTC_BC7);
+    TranscoderManager.registerTranscoder(MSCTranscoder);
+
+    let transcoderMgr = new TranscoderManager();
+
+    onmessage = (event) => {
+        if (event.data.action === "init") {
+            postMessage({action: "init"});
+        } else if (event.data.action === "createMipmaps") {
+            _createMipmaps(event.data.kfr).then((mipmaps) => {
+                //if (!success) {
+                //    postMessage({action: "transcode", success: success, id: event.data.id});
+                //} else {
+                    postMessage({ action: "mipmapsCreated", success: true/*success*/, id: event.data.id, mipmaps: mipmaps.mipmaps }, mipmaps.mipmapsData);
+                //}
+            });
+        }
+    };
+
+    const _createMipmaps = (kfr: KTX2FileReader): Promise<{ mipmaps: Array<IMipmap>, mipmapsData: Array<ArrayBuffer> }> => {
+        /*await this.zstd.init();*/
+
+        //var mipmaps = [];
+        const width = kfr.header.pixelWidth;
+        const height = kfr.header.pixelHeight;
+        const srcTexFormat = kfr.textureFormat;
+
+        let targetFormat = transcodeTarget.BC7_M5_RGBA;
+        let transcodedFormat = COMPRESSED_RGBA_BPTC_UNORM_EXT;
+
+        const transcoder = transcoderMgr.findTranscoder(srcTexFormat, targetFormat);
+
+        if (transcoder === null) {
+            throw new Error(`KTX2 container - no transcoder found to transcode source texture format "${sourceTextureFormat[srcTexFormat]}" to format "${transcodeTarget[targetFormat]}"`);
+        }
+
+        const mipmaps: Array<IMipmap> = [];
+        const texturePromises: Array<Promise<Nullable<Uint8Array>>> = [];
+        const mipmapsData: Array<ArrayBuffer> = [];
+
+        let firstImageDescIndex = 0;
+
+        for (let level = 0; level < kfr.header.levelCount; level ++) {
+            if (level > 0) {
+                firstImageDescIndex += Math.max(kfr.header.layerCount, 1) * kfr.header.faceCount * Math.max(kfr.header.pixelDepth >> (level - 1), 1);
+            }
+
+            const levelWidth = width / Math.pow(2, level);
+            const levelHeight = height / Math.pow(2, level);
+
+            const numImagesInLevel = kfr.header.faceCount; // note that cubemap are not supported yet (see KTX2FileReader), so faceCount == 1
+            const levelImageByteLength = ((levelWidth + 3) >> 2) * ((levelHeight + 3) >> 2) * kfr.dfdBlock.bytesPlane[0];
+
+            const levelUncompressedByteLength = kfr.levels[level].uncompressedByteLength;
+
+            let levelDataBuffer = kfr.data.buffer;
+            let levelDataOffset = kfr.levels[level].byteOffset;
+            let imageOffsetInLevel = 0;
+
+            if (kfr.header.supercompressionScheme === supercompressionScheme.ZStandard) {
+                //levelDataBuffer = this.zstd.decode(new Uint8Array(levelDataBuffer, levelDataOffset, levelByteLength), levelUncompressedByteLength);
+                levelDataOffset = 0;
+            }
+
+            for (let imageIndex = 0; imageIndex < numImagesInLevel; imageIndex ++) {
+                let encodedData: Uint8Array;
+                let imageDesc: Nullable<IKTX2_ImageDesc> = null;
+
+                if (kfr.header.supercompressionScheme === supercompressionScheme.BasisLZ) {
+                    imageDesc = kfr.supercompressionGlobalData.imageDescs![firstImageDescIndex + imageIndex];
+
+                    encodedData = new Uint8Array(levelDataBuffer, levelDataOffset + imageDesc.rgbSliceByteOffset, imageDesc.rgbSliceByteLength + imageDesc.alphaSliceByteLength);
+                } else {
+                    encodedData = new Uint8Array(levelDataBuffer, levelDataOffset + imageOffsetInLevel, levelImageByteLength);
+
+                    imageOffsetInLevel += levelImageByteLength;
+                }
+
+                const mipmap: IMipmap = {
+                    data: null,
+                    width: levelWidth,
+                    height: levelHeight,
+                    transcodedFormat: transcodedFormat
+                };
+
+                const transcodedData = transcoder.transcode(srcTexFormat, targetFormat, level, levelWidth, levelHeight, levelUncompressedByteLength, kfr, imageDesc, encodedData).
+                        then((data) => {
+                            mipmap.data = data;
+                            if (data) {
+                                mipmapsData.push(data.buffer);
+                            }
+                            return data;
+                        }
+                      );
+
+                mipmaps.push(mipmap);
+
+                texturePromises.push(transcodedData);
+            }
+        }
+
+        return Promise.all(texturePromises).then(() => {
+            return { mipmaps, mipmapsData };
+        });
+    };
+}

+ 818 - 0
src/Misc/KTX2/KTX2WorkerThreadJS.ts

@@ -0,0 +1,818 @@
+// @ts-nocheck
+// tslint:disable
+export function workerFunc() {
+
+    var COMPRESSED_RGBA_BPTC_UNORM_EXT = 36492;
+
+    var __extends = (this && this.__extends) || (function () {
+        var extendStatics = function (d, b) {
+            extendStatics = Object.setPrototypeOf ||
+                ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+                function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+            return extendStatics(d, b);
+        };
+        return function (d, b) {
+            extendStatics(d, b);
+            function __() { this.constructor = d; }
+            d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+        };
+    })();
+
+    /**
+     * Transcoder
+     */
+    var sourceTextureFormat;
+    (function (sourceTextureFormat) {
+        sourceTextureFormat[sourceTextureFormat["ETC1S"] = 0] = "ETC1S";
+        sourceTextureFormat[sourceTextureFormat["UASTC4x4"] = 1] = "UASTC4x4";
+    })(sourceTextureFormat || (sourceTextureFormat = {}));
+
+    var transcodeTarget;
+    (function (transcodeTarget) {
+        transcodeTarget[transcodeTarget["ASTC_4x4_RGBA"] = 0] = "ASTC_4x4_RGBA";
+        transcodeTarget[transcodeTarget["BC7_M5_RGBA"] = 1] = "BC7_M5_RGBA";
+        transcodeTarget[transcodeTarget["BC3_RGBA"] = 2] = "BC3_RGBA";
+        transcodeTarget[transcodeTarget["BC1_RGB"] = 3] = "BC1_RGB";
+        transcodeTarget[transcodeTarget["PVRTC1_4_RGBA"] = 4] = "PVRTC1_4_RGBA";
+        transcodeTarget[transcodeTarget["PVRTC1_4_RGB"] = 5] = "PVRTC1_4_RGB";
+        transcodeTarget[transcodeTarget["ETC2_RGBA"] = 6] = "ETC2_RGBA";
+        transcodeTarget[transcodeTarget["ETC1_RGB"] = 7] = "ETC1_RGB";
+        transcodeTarget[transcodeTarget["RGBA32"] = 8] = "RGBA32";
+    })(transcodeTarget || (transcodeTarget = {}));
+
+    var Transcoder = /** @class */ (function () {
+        function Transcoder() {
+        }
+        Transcoder.CanTranscode = function (src, dst) {
+            return false;
+        };
+        Transcoder.prototype.initialize = function () {
+        };
+        Transcoder.prototype.needMemoryManager = function () {
+            return false;
+        };
+        Transcoder.prototype.setMemoryManager = function (memoryMgr) {
+        };
+        Transcoder.prototype.transcode = function (src, dst, level, width, height, uncompressedByteLength, ktx2Reader, imageDesc, encodedData) {
+            return Promise.resolve(null);
+        };
+        return Transcoder;
+    }());
+
+    /**
+     * WASMMemoryManager
+     */
+    var WASMMemoryManager = /** @class */ (function () {
+        function WASMMemoryManager(initialMemoryPages) {
+            if (initialMemoryPages === void 0) { initialMemoryPages = (1 * 1024 * 1024) >> 16; }
+            this._numPages = initialMemoryPages;
+            this._memory = new WebAssembly.Memory({ initial: this._numPages });
+            this._memoryViewByteLength = this._numPages << 16;
+            this._memoryViewOffset = 0;
+            this._memoryView = new Uint8Array(this._memory.buffer, this._memoryViewOffset, this._memoryViewByteLength);
+        }
+        Object.defineProperty(WASMMemoryManager.prototype, "wasmMemory", {
+            get: function () {
+                return this._memory;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        WASMMemoryManager.prototype.getMemoryView = function (numPages, offset, byteLength) {
+            if (offset === void 0) { offset = 0; }
+            byteLength = byteLength !== null && byteLength !== void 0 ? byteLength : numPages << 16;
+            if (this._numPages < numPages) {
+                console.log("grow memory", this._numPages, numPages);
+                this._memory.grow(numPages - this._numPages);
+                this._numPages = numPages;
+                this._memoryView = new Uint8Array(this._memory.buffer, offset, byteLength);
+                this._memoryViewByteLength = byteLength;
+                this._memoryViewOffset = offset;
+            }
+            else if (this._memoryViewByteLength < byteLength || this._memoryViewOffset !== offset) {
+                console.log("recreate view", this._memoryViewByteLength, byteLength, this._memoryViewOffset, offset);
+                this._memoryView = new Uint8Array(this._memory.buffer, offset, byteLength);
+                this._memoryViewByteLength = byteLength;
+                this._memoryViewOffset = offset;
+            }
+            return this._memoryView;
+        };
+        return WASMMemoryManager;
+    }());
+
+    /**
+     * TranscoderManager
+     */
+    var TranscoderManager = /** @class */ (function () {
+        function TranscoderManager() {
+        }
+        TranscoderManager.registerTranscoder = function (transcoder) {
+            TranscoderManager._Transcoders.push(transcoder);
+        };
+        TranscoderManager.prototype.findTranscoder = function (src, dst) {
+            var transcoder = null;
+            for (var i = 0; i < TranscoderManager._Transcoders.length; ++i) {
+                if (TranscoderManager._Transcoders[i].CanTranscode(src, dst)) {
+                    var key = sourceTextureFormat[src] + "_" + transcodeTarget[dst];
+                    transcoder = TranscoderManager._transcoderInstances[key];
+                    if (!transcoder) {
+                        transcoder = new TranscoderManager._Transcoders[i]();
+                        transcoder.initialize();
+                        if (transcoder.needMemoryManager()) {
+                            if (!this._wasmMemoryManager) {
+                                this._wasmMemoryManager = new WASMMemoryManager();
+                            }
+                            transcoder.setMemoryManager(this._wasmMemoryManager);
+                        }
+                        TranscoderManager._transcoderInstances[key] = transcoder;
+                    }
+                    break;
+                }
+            }
+            return transcoder;
+        };
+        TranscoderManager._Transcoders = [];
+        TranscoderManager._transcoderInstances = {};
+        return TranscoderManager;
+    }());
+
+    /**
+     * LiteTranscoder
+     */
+    var LiteTranscoder = /** @class */ (function (_super) {
+        __extends(LiteTranscoder, _super);
+        function LiteTranscoder() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        LiteTranscoder.prototype._loadModule = function () {
+            var _this = this;
+            if (this._modulePromise) {
+                return this._modulePromise;
+            }
+            this._modulePromise = new Promise(function (resolve) {
+                fetch/*Tools.LoadFileAsync*/("http://localhost:1338" + _this._modulePath).
+                then(function (response) {
+                    return response.arrayBuffer();
+                }).then(function (wasmBinary) {
+                    WebAssembly.instantiate(wasmBinary, { env: { memory: _this._memoryManager.wasmMemory } }).then(function (moduleWrapper) {
+                        resolve({ module: moduleWrapper.instance.exports });
+                    });
+                });
+            });
+            return this._modulePromise;
+        };
+        Object.defineProperty(LiteTranscoder.prototype, "memoryManager", {
+            get: function () {
+                return this._memoryManager;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        LiteTranscoder.prototype.setModulePath = function (modulePath) {
+            this._modulePath = modulePath;
+        };
+        LiteTranscoder.prototype.needMemoryManager = function () {
+            return true;
+        };
+        LiteTranscoder.prototype.setMemoryManager = function (memoryMgr) {
+            this._memoryManager = memoryMgr;
+        };
+        LiteTranscoder.prototype.transcode = function (src, dst, level, width, height, uncompressedByteLength, ktx2Reader, imageDesc, encodedData) {
+            var _this = this;
+            return this._loadModule().then(function (moduleWrapper) {
+                var transcoder = moduleWrapper.module;
+                var nBlocks = ((width + 3) >> 2) * ((height + 3) >> 2);
+                var texMemoryPages = ((nBlocks * 16 + 65535) >> 16) + 1;
+                var textureView = _this.memoryManager.getMemoryView(texMemoryPages, 65536, nBlocks * 16);
+                textureView.set(encodedData);
+                return transcoder.transcode(nBlocks) === 0 ? textureView.slice() : null;
+            });
+        };
+        return LiteTranscoder;
+    }(Transcoder));
+
+    /**
+     * LiteTranscoder_UASTC_BC7
+     */
+    var LiteTranscoder_UASTC_BC7 = /** @class */ (function (_super) {
+        __extends(LiteTranscoder_UASTC_BC7, _super);
+        function LiteTranscoder_UASTC_BC7() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        LiteTranscoder_UASTC_BC7.CanTranscode = function (src, dst) {
+            return src === sourceTextureFormat.UASTC4x4 && dst === transcodeTarget.BC7_M5_RGBA;
+        };
+        LiteTranscoder_UASTC_BC7.prototype.initialize = function () {
+            this.setModulePath("/dist/preview release/basisTranscoder/uastc_bc7.wasm");
+        };
+        return LiteTranscoder_UASTC_BC7;
+    }(LiteTranscoder));
+
+    /**
+     * LiteTranscoder_UASTC_ASTC
+     */
+    var LiteTranscoder_UASTC_ASTC = /** @class */ (function (_super) {
+        __extends(LiteTranscoder_UASTC_ASTC, _super);
+        function LiteTranscoder_UASTC_ASTC() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        LiteTranscoder_UASTC_ASTC.CanTranscode = function (src, dst) {
+            return src === sourceTextureFormat.UASTC4x4 && dst === transcodeTarget.ASTC_4x4_RGBA;
+        };
+        LiteTranscoder_UASTC_ASTC.prototype.initialize = function () {
+            this.setModulePath("/dist/preview release/basisTranscoder/uastc_astc.wasm");
+        };
+        return LiteTranscoder_UASTC_ASTC;
+    }(LiteTranscoder));
+
+    /**
+     * LiteTranscoder_UASTC_ASTC
+     */
+    var MSCTranscoder = /** @class */ (function (_super) {
+        __extends(MSCTranscoder, _super);
+        function MSCTranscoder() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        MSCTranscoder.prototype._getMSCBasisTranscoder = function () {
+            var _this = this;
+            if (this._mscBasisTranscoderPromise) {
+                return this._mscBasisTranscoderPromise;
+            }
+            this._mscBasisTranscoderPromise = new Promise(function (resolve) {
+                importScripts("https://popov72.github.io/BabylonDev/resources/lib/msc_basis_transcoder.jss");
+                MSC_TRANSCODER().then(function (basisModule) {
+                    basisModule.initTranscoders();
+                    _this._mscBasisModule = basisModule;
+                    resolve();
+                });
+            });
+            return this._mscBasisTranscoderPromise;
+        };
+        MSCTranscoder.CanTranscode = function (src, dst) {
+            return true;
+        };
+        MSCTranscoder.prototype.transcode = function (src, dst, level, width, height, uncompressedByteLength, ktx2Reader, imageDesc, encodedData) {
+            var _this = this;
+            var isVideo = false;
+            return this._getMSCBasisTranscoder().then(function () {
+                var basisModule = _this._mscBasisModule;
+                var TranscodeTarget = basisModule.TranscodeTarget;
+                var TextureFormat = basisModule.TextureFormat;
+                var ImageInfo = basisModule.ImageInfo;
+                var transcoder = src === sourceTextureFormat.UASTC4x4 ? new basisModule.UastcImageTranscoder() : new basisModule.BasisLzEtc1sImageTranscoder();
+                var texFormat = src === sourceTextureFormat.UASTC4x4 ? TextureFormat.UASTC4x4 : TextureFormat.ETC1S;
+                var imageInfo = new ImageInfo(texFormat, width, height, level);
+                var targetFormat = TranscodeTarget[transcodeTarget[dst]]; // works because the labels of the sourceTextureFormat enum are the same than the property names used in TranscodeTarget!
+                if (!basisModule.isFormatSupported(targetFormat, texFormat)) {
+                    throw new Error("MSCTranscoder: Transcoding from \"" + sourceTextureFormat[src] + "\" to \"" + transcodeTarget[dst] + "\" not supported by current transcoder build.");
+                }
+                var result;
+                if (src === sourceTextureFormat.ETC1S) {
+                    var sgd = ktx2Reader.supercompressionGlobalData;
+                    transcoder.decodePalettes(sgd.endpointCount, sgd.endpointsData, sgd.selectorCount, sgd.selectorsData);
+                    transcoder.decodeTables(sgd.tablesData);
+                    imageInfo.flags = imageDesc.imageFlags;
+                    imageInfo.rgbByteOffset = 0;
+                    imageInfo.rgbByteLength = imageDesc.rgbSliceByteLength;
+                    imageInfo.alphaByteOffset = imageDesc.alphaSliceByteOffset > 0 ? imageDesc.rgbSliceByteLength : 0;
+                    imageInfo.alphaByteLength = imageDesc.alphaSliceByteLength;
+                    result = transcoder.transcodeImage(targetFormat, encodedData, imageInfo, 0, isVideo);
+                }
+                else {
+                    imageInfo.flags = 0;
+                    imageInfo.rgbByteOffset = 0;
+                    imageInfo.rgbByteLength = uncompressedByteLength;
+                    imageInfo.alphaByteOffset = 0;
+                    imageInfo.alphaByteLength = 0;
+                    result = transcoder.transcodeImage(targetFormat, encodedData, imageInfo, 0, ktx2Reader.hasAlpha, isVideo);
+                }
+                if (result && result.transcodedImage !== undefined) {
+                    var textureData = result.transcodedImage.get_typed_memory_view().slice();
+                    result.transcodedImage.delete();
+                    return textureData;
+                }
+                return null;
+            });
+        };
+        return MSCTranscoder;
+    }(Transcoder));
+    
+    TranscoderManager.registerTranscoder(LiteTranscoder_UASTC_ASTC);
+    TranscoderManager.registerTranscoder(LiteTranscoder_UASTC_BC7);
+    TranscoderManager.registerTranscoder(MSCTranscoder);
+
+    /**
+     * DataReader
+     */
+    var DataReader = /** @class */ (function () {
+        /**
+         * Constructor
+         * @param buffer The buffer to read
+         */
+        function DataReader(buffer) {
+            /**
+             * Indicates the endianness of the data in the buffer
+             */
+            this.littleEndian = true;
+            this.buffer = buffer;
+        }
+        Object.defineProperty(DataReader.prototype, "byteOffset", {
+            /**
+             * The current byte offset from the beginning of the data buffer.
+             */
+            get: function () {
+                return this._dataByteOffset;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        /**
+         * Loads the given byte length.
+         * @param byteLength The byte length to load
+         * @returns A promise that resolves when the load is complete
+         */
+        DataReader.prototype.loadAsync = function (byteLength) {
+            var _this = this;
+            delete this._dataView;
+            delete this._dataByteOffset;
+            if (!this.buffer) {
+                return Promise.resolve();
+            }
+            else {
+                return this.buffer.readAsync(this.byteOffset, byteLength).then(function (data) {
+                    _this._dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);
+                    _this._dataByteOffset = 0;
+                });
+            }
+        };
+        /**
+         * Sets the given buffer
+         * @param buffer The buffer to set
+         * @param byteOffset The starting offset in the buffer
+         * @param byteLength The byte length of the buffer
+         * @returns This instance
+         */
+        DataReader.prototype.setBuffer = function (buffer, byteOffset, byteLength) {
+            if (buffer.buffer) {
+                this._dataView = new DataView(buffer.buffer, byteOffset !== null && byteOffset !== void 0 ? byteOffset : buffer.byteOffset, byteLength !== null && byteLength !== void 0 ? byteLength : buffer.byteLength);
+            }
+            else {
+                this._dataView = new DataView(buffer, byteOffset !== null && byteOffset !== void 0 ? byteOffset : 0, byteLength !== null && byteLength !== void 0 ? byteLength : buffer.byteLength);
+            }
+            this._dataByteOffset = 0;
+            return this;
+        };
+        /**
+         * Read a unsigned 8-bit integer from the currently loaded data range.
+         * @returns The 8-bit integer read
+         */
+        DataReader.prototype.readUint8 = function () {
+            var value = this._dataView.getUint8(this._dataByteOffset);
+            this._dataByteOffset += 1;
+            return value;
+        };
+        /**
+         * Read a signed 8-bit integer from the currently loaded data range.
+         * @returns The 8-bit integer read
+         */
+        DataReader.prototype.readInt8 = function () {
+            var value = this._dataView.getInt8(this._dataByteOffset);
+            this._dataByteOffset += 1;
+            return value;
+        };
+        /**
+         * Read a unsigned 16-bit integer from the currently loaded data range.
+         * @returns The 16-bit integer read
+         */
+        DataReader.prototype.readUint16 = function () {
+            var value = this._dataView.getUint16(this._dataByteOffset, this.littleEndian);
+            this._dataByteOffset += 2;
+            return value;
+        };
+        /**
+         * Read a signed 16-bit integer from the currently loaded data range.
+         * @returns The 16-bit integer read
+         */
+        DataReader.prototype.readInt16 = function () {
+            var value = this._dataView.getInt16(this._dataByteOffset, this.littleEndian);
+            this._dataByteOffset += 2;
+            return value;
+        };
+        /**
+         * Read a unsigned 32-bit integer from the currently loaded data range.
+         * @returns The 32-bit integer read
+         */
+        DataReader.prototype.readUint32 = function () {
+            var value = this._dataView.getUint32(this._dataByteOffset, this.littleEndian);
+            this._dataByteOffset += 4;
+            return value;
+        };
+        /**
+         * Read a signed 32-bit integer from the currently loaded data range.
+         * @returns The 32-bit integer read
+         */
+        DataReader.prototype.readInt32 = function () {
+            var value = this._dataView.getInt32(this._dataByteOffset, this.littleEndian);
+            this._dataByteOffset += 4;
+            return value;
+        };
+        /**
+         * Read a unsigned 32-bit integer from the currently loaded data range.
+         * @returns The 32-bit integer read
+         */
+        DataReader.prototype.readUint64 = function () {
+            // split 64-bit number into two 32-bit (4-byte) parts
+            var left = this._dataView.getUint32(this._dataByteOffset, this.littleEndian);
+            var right = this._dataView.getUint32(this._dataByteOffset + 4, this.littleEndian);
+            // combine the two 32-bit values
+            var combined = this.littleEndian ? left + (Math.pow(2, 32) * right) : (Math.pow(2, 32) * left) + right;
+            /*if (!Number.isSafeInteger(combined)) {
+                console.warn('DataReader: ' + combined + ' exceeds MAX_SAFE_INTEGER. Precision may be lost.');
+            }*/
+            this._dataByteOffset += 8;
+            return combined;
+        };
+        /**
+         * Read a byte array from the currently loaded data range.
+         * @param byteLength The byte length to read
+         * @returns The byte array read
+         */
+        DataReader.prototype.readUint8Array = function (byteLength) {
+            var value = new Uint8Array(this._dataView.buffer, this._dataView.byteOffset + this._dataByteOffset, byteLength);
+            this._dataByteOffset += byteLength;
+            return value;
+        };
+        /**
+         * Read a string from the currently loaded data range.
+         * @param byteLength The byte length to read
+         * @returns The string read
+         */
+        DataReader.prototype.readString = function (byteLength) {
+            return StringTools.Decode(this.readUint8Array(byteLength));
+        };
+        /**
+         * Skips the given byte length the currently loaded data range.
+         * @param byteLength The byte length to skip
+         * @returns This instance
+         */
+        DataReader.prototype.skipBytes = function (byteLength) {
+            this._dataByteOffset += byteLength;
+            return this;
+        };
+        return DataReader;
+    }());
+
+    /**
+     * KTX2FileReader
+     */
+    var supercompressionScheme;
+    (function (supercompressionScheme) {
+        supercompressionScheme[supercompressionScheme["None"] = 0] = "None";
+        supercompressionScheme[supercompressionScheme["BasisLZ"] = 1] = "BasisLZ";
+        supercompressionScheme[supercompressionScheme["ZStandard"] = 2] = "ZStandard";
+        supercompressionScheme[supercompressionScheme["ZLib"] = 3] = "ZLib";
+    })(supercompressionScheme || (supercompressionScheme = {}));
+    var dfdModel;
+    (function (dfdModel) {
+        dfdModel[dfdModel["ETC1S"] = 163] = "ETC1S";
+        dfdModel[dfdModel["UASTC"] = 166] = "UASTC";
+    })(dfdModel || (dfdModel = {}));
+    var dfdChannel_ETC1S;
+    (function (dfdChannel_ETC1S) {
+        dfdChannel_ETC1S[dfdChannel_ETC1S["RGB"] = 0] = "RGB";
+        dfdChannel_ETC1S[dfdChannel_ETC1S["RRR"] = 3] = "RRR";
+        dfdChannel_ETC1S[dfdChannel_ETC1S["GGG"] = 4] = "GGG";
+        dfdChannel_ETC1S[dfdChannel_ETC1S["AAA"] = 15] = "AAA";
+    })(dfdChannel_ETC1S || (dfdChannel_ETC1S = {}));
+    var dfdChannel_UASTC;
+    (function (dfdChannel_UASTC) {
+        dfdChannel_UASTC[dfdChannel_UASTC["RGB"] = 0] = "RGB";
+        dfdChannel_UASTC[dfdChannel_UASTC["RGBA"] = 3] = "RGBA";
+        dfdChannel_UASTC[dfdChannel_UASTC["RRR"] = 4] = "RRR";
+        dfdChannel_UASTC[dfdChannel_UASTC["RRRG"] = 5] = "RRRG";
+    })(dfdChannel_UASTC || (dfdChannel_UASTC = {}));
+    /**
+     * Based on https://github.com/mrdoob/three.js/blob/dfb5c23ce126ec845e4aa240599915fef5375797/examples/jsm/loaders/KTX2Loader.js
+     * @hidden
+     */
+    var KTX2FileReader = /** @class */ (function () {
+        /**
+         * Will throw an exception if the file can't be parsed
+         */
+        function KTX2FileReader(data) {
+            this._data = data;
+            this._parseData();
+        }
+        Object.defineProperty(KTX2FileReader.prototype, "data", {
+            get: function () {
+                return this._data;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(KTX2FileReader.prototype, "header", {
+            get: function () {
+                return this._header;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(KTX2FileReader.prototype, "levels", {
+            get: function () {
+                return this._levels;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(KTX2FileReader.prototype, "dfdBlock", {
+            get: function () {
+                return this._dfdBlock;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(KTX2FileReader.prototype, "supercompressionGlobalData", {
+            get: function () {
+                return this._supercompressionGlobalData;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        KTX2FileReader.prototype._parseData = function () {
+            var offsetInFile = 12; // skip the header
+            /**
+             * Get the header
+             */
+            var hdrReader = new DataReader().setBuffer(this._data, offsetInFile, 17 * 4);
+            var header = this._header = {
+                vkFormat: hdrReader.readUint32(),
+                typeSize: hdrReader.readUint32(),
+                pixelWidth: hdrReader.readUint32(),
+                pixelHeight: hdrReader.readUint32(),
+                pixelDepth: hdrReader.readUint32(),
+                layerCount: hdrReader.readUint32(),
+                faceCount: hdrReader.readUint32(),
+                levelCount: hdrReader.readUint32(),
+                supercompressionScheme: hdrReader.readUint32(),
+                dfdByteOffset: hdrReader.readUint32(),
+                dfdByteLength: hdrReader.readUint32(),
+                kvdByteOffset: hdrReader.readUint32(),
+                kvdByteLength: hdrReader.readUint32(),
+                sgdByteOffset: hdrReader.readUint64(),
+                sgdByteLength: hdrReader.readUint64(),
+            };
+            if (header.pixelDepth > 0) {
+                throw new Error("Failed to parse KTX2 file - Only 2D textures are currently supported.");
+            }
+            if (header.layerCount > 1) {
+                throw new Error("Failed to parse KTX2 file - Array textures are not currently supported.");
+            }
+            if (header.faceCount > 1) {
+                //throw new Error(`Failed to parse KTX2 file - Cube textures are not currently supported.`);
+            }
+            console.log(header);
+            offsetInFile += hdrReader.byteOffset;
+            /**
+             * Get the levels
+             */
+            var levelCount = Math.max(1, header.levelCount);
+            var levelReader = new DataReader().setBuffer(this._data, offsetInFile, levelCount * 3 * (2 * 4));
+            var levels = this._levels = [];
+            while (levelCount--) {
+                levels.push({
+                    byteOffset: levelReader.readUint64(),
+                    byteLength: levelReader.readUint64(),
+                    uncompressedByteLength: levelReader.readUint64(),
+                });
+            }
+            offsetInFile += levelReader.byteOffset;
+            console.log(levels);
+            /**
+             * Get the data format descriptor (DFD) blocks
+             */
+            var dfdReader = new DataReader().setBuffer(this._data, header.dfdByteOffset, header.dfdByteLength);
+            var dfdBlock = this._dfdBlock = {
+                vendorId: dfdReader.skipBytes(4 /* skip totalSize */).readUint16(),
+                descriptorType: dfdReader.readUint16(),
+                versionNumber: dfdReader.readUint16(),
+                descriptorBlockSize: dfdReader.readUint16(),
+                colorModel: dfdReader.readUint8(),
+                colorPrimaries: dfdReader.readUint8(),
+                transferFunction: dfdReader.readUint8(),
+                flags: dfdReader.readUint8(),
+                texelBlockDimension: {
+                    x: dfdReader.readUint8() + 1,
+                    y: dfdReader.readUint8() + 1,
+                    z: dfdReader.readUint8() + 1,
+                    w: dfdReader.readUint8() + 1,
+                },
+                bytesPlane: [
+                    dfdReader.readUint8(),
+                    dfdReader.readUint8(),
+                    dfdReader.readUint8(),
+                    dfdReader.readUint8(),
+                    dfdReader.readUint8(),
+                    dfdReader.readUint8(),
+                    dfdReader.readUint8(),
+                    dfdReader.readUint8(),
+                ],
+                numSamples: 0,
+                samples: new Array(),
+            };
+            dfdBlock.numSamples = (dfdBlock.descriptorBlockSize - 24) / 16;
+            for (var i = 0; i < dfdBlock.numSamples; i++) {
+                var sample = {
+                    bitOffset: dfdReader.readUint16(),
+                    bitLength: dfdReader.readUint8() + 1,
+                    channelType: dfdReader.readUint8(),
+                    channelFlags: 0,
+                    samplePosition: [
+                        dfdReader.readUint8(),
+                        dfdReader.readUint8(),
+                        dfdReader.readUint8(),
+                        dfdReader.readUint8(),
+                    ],
+                    sampleLower: dfdReader.readUint32(),
+                    sampleUpper: dfdReader.readUint32(),
+                };
+                sample.channelFlags = (sample.channelType & 0xF0) >> 4;
+                sample.channelType = sample.channelType & 0x0F;
+                dfdBlock.samples.push(sample);
+            }
+            console.log(dfdBlock);
+            /*if (header.vkFormat !== KhronosTextureContainer2.VK_FORMAT_UNDEFINED &&
+                 !(header.supercompressionScheme === supercompressionScheme.BasisLZ ||
+                    dfdBlock.colorModel === dfdModel.UASTC)) {
+                throw new Error(`Failed to upload - Only Basis Universal supercompression is currently supported.`);
+            }*/
+            /**
+             * Get the Supercompression Global Data (sgd)
+             */
+            var sgd = this._supercompressionGlobalData = {};
+            if (header.sgdByteLength > 0) {
+                var sgdReader = new DataReader().setBuffer(this._data, header.sgdByteOffset, header.sgdByteLength);
+                sgd.endpointCount = sgdReader.readUint16();
+                sgd.selectorCount = sgdReader.readUint16();
+                sgd.endpointsByteLength = sgdReader.readUint32();
+                sgd.selectorsByteLength = sgdReader.readUint32();
+                sgd.tablesByteLength = sgdReader.readUint32();
+                sgd.extendedByteLength = sgdReader.readUint32();
+                sgd.imageDescs = [];
+                var imageCount = this._getImageCount();
+                for (var i = 0; i < imageCount; i++) {
+                    sgd.imageDescs.push({
+                        imageFlags: sgdReader.readUint32(),
+                        rgbSliceByteOffset: sgdReader.readUint32(),
+                        rgbSliceByteLength: sgdReader.readUint32(),
+                        alphaSliceByteOffset: sgdReader.readUint32(),
+                        alphaSliceByteLength: sgdReader.readUint32(),
+                    });
+                }
+                var endpointsByteOffset = header.sgdByteOffset + sgdReader.byteOffset;
+                var selectorsByteOffset = endpointsByteOffset + sgd.endpointsByteLength;
+                var tablesByteOffset = selectorsByteOffset + sgd.selectorsByteLength;
+                var extendedByteOffset = tablesByteOffset + sgd.tablesByteLength;
+                sgd.endpointsData = new Uint8Array(this._data.buffer, endpointsByteOffset, sgd.endpointsByteLength);
+                sgd.selectorsData = new Uint8Array(this._data.buffer, selectorsByteOffset, sgd.selectorsByteLength);
+                sgd.tablesData = new Uint8Array(this._data.buffer, tablesByteOffset, sgd.tablesByteLength);
+                sgd.extendedData = new Uint8Array(this._data.buffer, extendedByteOffset, sgd.extendedByteLength);
+            }
+            console.log("sgd", sgd);
+        };
+        KTX2FileReader.prototype._getImageCount = function () {
+            var layerPixelDepth = Math.max(this._header.pixelDepth, 1);
+            for (var i = 1; i < this._header.levelCount; i++) {
+                layerPixelDepth += Math.max(this._header.pixelDepth >> i, 1);
+            }
+            return Math.max(this._header.layerCount, 1) * this._header.faceCount * layerPixelDepth;
+        };
+        Object.defineProperty(KTX2FileReader.prototype, "textureFormat", {
+            get: function () {
+                return this._dfdBlock.colorModel === dfdModel.UASTC ? sourceTextureFormat.UASTC4x4 : sourceTextureFormat.ETC1S;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        Object.defineProperty(KTX2FileReader.prototype, "hasAlpha", {
+            get: function () {
+                var tformat = this.textureFormat;
+                switch (tformat) {
+                    case sourceTextureFormat.ETC1S:
+                        return this._dfdBlock.numSamples === 2 && (this._dfdBlock.samples[0].channelType === dfdChannel_ETC1S.AAA || this._dfdBlock.samples[1].channelType === dfdChannel_ETC1S.AAA);
+                    case sourceTextureFormat.UASTC4x4:
+                        return this._dfdBlock.samples[0].channelType === dfdChannel_UASTC.RGBA;
+                }
+                return false;
+            },
+            enumerable: false,
+            configurable: true
+        });
+        KTX2FileReader.prototype.getHasAlpha = function () {
+            var tformat = this.textureFormat;
+            switch (tformat) {
+                case sourceTextureFormat.ETC1S:
+                    return this._dfdBlock.numSamples === 2 && (this._dfdBlock.samples[0].channelType === dfdChannel_ETC1S.AAA || this._dfdBlock.samples[1].channelType === dfdChannel_ETC1S.AAA);
+                case sourceTextureFormat.UASTC4x4:
+                    return this._dfdBlock.samples[0].channelType === dfdChannel_UASTC.RGBA;
+            }
+            return false;
+        };
+        KTX2FileReader.IsValid = function (data) {
+            if (data.byteLength >= 12) {
+                // '«', 'K', 'T', 'X', ' ', '2', '0', '»', '\r', '\n', '\x1A', '\n'
+                var identifier = new Uint8Array(data.buffer, data.byteOffset, 12);
+                if (identifier[0] === 0xAB && identifier[1] === 0x4B && identifier[2] === 0x54 && identifier[3] === 0x58 && identifier[4] === 0x20 && identifier[5] === 0x32 &&
+                    identifier[6] === 0x30 && identifier[7] === 0xBB && identifier[8] === 0x0D && identifier[9] === 0x0A && identifier[10] === 0x1A && identifier[11] === 0x0A) {
+                    return true;
+                }
+            }
+            return false;
+        };
+        return KTX2FileReader;
+    }());
+
+    var transcoderMgr = new TranscoderManager();
+    onmessage = function (event) {
+        if (event.data.action === "init") {
+            postMessage({ action: "init" });
+        }
+        else if (event.data.action === "createMipmaps") {
+            var kfr = new KTX2FileReader(event.data.data);
+            _createMipmaps(kfr).then(function (mipmaps) {
+                //if (!success) {
+                //    postMessage({action: "transcode", success: success, id: event.data.id});
+                //} else {
+                postMessage({ action: "mipmapsCreated", success: true /*success*/, id: event.data.id, mipmaps: mipmaps.mipmaps }, mipmaps.mipmapsData);
+                //}
+            });
+        }
+    };
+    var _createMipmaps = function (kfr) {
+        /*await this.zstd.init();*/
+        //var mipmaps = [];
+        var width = kfr.header.pixelWidth;
+        var height = kfr.header.pixelHeight;
+        var srcTexFormat = kfr.textureFormat;
+        var targetFormat = transcodeTarget.BC7_M5_RGBA;
+        var transcodedFormat = COMPRESSED_RGBA_BPTC_UNORM_EXT;
+        var transcoder = transcoderMgr.findTranscoder(srcTexFormat, targetFormat);
+        if (transcoder === null) {
+            throw new Error("KTX2 container - no transcoder found to transcode source texture format \"" + sourceTextureFormat[srcTexFormat] + "\" to format \"" + transcodeTarget[targetFormat] + "\"");
+        }
+        var mipmaps = [];
+        var texturePromises = [];
+        var mipmapsData = [];
+        var firstImageDescIndex = 0;
+        for (var level = 0; level < kfr.header.levelCount; level++) {
+            if (level > 0) {
+                firstImageDescIndex += Math.max(kfr.header.layerCount, 1) * kfr.header.faceCount * Math.max(kfr.header.pixelDepth >> (level - 1), 1);
+            }
+            var levelWidth = width / Math.pow(2, level);
+            var levelHeight = height / Math.pow(2, level);
+            var numImagesInLevel = kfr.header.faceCount; // note that cubemap are not supported yet (see KTX2FileReader), so faceCount == 1
+            var levelImageByteLength = ((levelWidth + 3) >> 2) * ((levelHeight + 3) >> 2) * kfr.dfdBlock.bytesPlane[0];
+            var levelUncompressedByteLength = kfr.levels[level].uncompressedByteLength;
+            var levelDataBuffer = kfr.data.buffer;
+            var levelDataOffset = kfr.levels[level].byteOffset;
+            var imageOffsetInLevel = 0;
+            if (kfr.header.supercompressionScheme === supercompressionScheme.ZStandard) {
+                //levelDataBuffer = this.zstd.decode(new Uint8Array(levelDataBuffer, levelDataOffset, levelByteLength), levelUncompressedByteLength);
+                levelDataOffset = 0;
+            }
+            var _loop_1 = function (imageIndex) {
+                var encodedData = void 0;
+                var imageDesc = null;
+                if (kfr.header.supercompressionScheme === supercompressionScheme.BasisLZ) {
+                    imageDesc = kfr.supercompressionGlobalData.imageDescs[firstImageDescIndex + imageIndex];
+                    encodedData = new Uint8Array(levelDataBuffer, levelDataOffset + imageDesc.rgbSliceByteOffset, imageDesc.rgbSliceByteLength + imageDesc.alphaSliceByteLength);
+                }
+                else {
+                    encodedData = new Uint8Array(levelDataBuffer, levelDataOffset + imageOffsetInLevel, levelImageByteLength);
+                    imageOffsetInLevel += levelImageByteLength;
+                }
+                var mipmap = {
+                    data: null,
+                    width: levelWidth,
+                    height: levelHeight,
+                    transcodedFormat: transcodedFormat
+                };
+                var transcodedData = transcoder.transcode(srcTexFormat, targetFormat, level, levelWidth, levelHeight, levelUncompressedByteLength, kfr, imageDesc, encodedData).
+                    then(function (data) {
+                    mipmap.data = data;
+                    if (data) {
+                        mipmapsData.push(data.buffer);
+                    }
+                    return data;
+                });
+                mipmaps.push(mipmap);
+                texturePromises.push(transcodedData);
+            };
+            for (var imageIndex = 0; imageIndex < numImagesInLevel; imageIndex++) {
+                _loop_1(imageIndex);
+            }
+        }
+        return Promise.all(texturePromises).then(function () {
+            return { mipmaps: mipmaps, mipmapsData: mipmapsData };
+        });
+    };
+}

+ 1 - 6
src/Misc/KTX2/index.ts

@@ -1,8 +1,3 @@
 export * from "./khronosTextureContainer2";
 export * from "./KTX2FileReader";
-export * from "./LiteTranscoder";
-export * from "./LiteTranscoder_UASTC_BC7";
-export * from "./LiteTranscoder_UASTC_ASTC";
-export * from "./mscTranscoder";
-export * from "./transcoder";
-export * from "./wasmMemoryManager";
+export * from "./KTX2WorkerThreadJS";

+ 5 - 180
src/Misc/KTX2/khronosTextureContainer2.ts

@@ -3,39 +3,20 @@ import { ThinEngine } from "../../Engines/thinEngine";
 //import { EngineCapabilities } from '../../Engines/engineCapabilities';
 //import { Tools } from '../tools';
 import { Nullable } from '../../types';
-import { KTX2FileReader, supercompressionScheme, IKTX2_ImageDesc } from './KTX2FileReader';
-import { sourceTextureFormat, Transcoder, transcodeTarget } from './transcoder';
-import { WASMMemoryManager } from './wasmMemoryManager';
 
-import { LiteTranscoder_UASTC_BC7 } from "./LiteTranscoder_UASTC_BC7";
-import { LiteTranscoder_UASTC_ASTC } from "./LiteTranscoder_UASTC_ASTC";
-import { MSCTranscoder } from "./mscTranscoder";
+import { IMipmap } from "./KTX2WorkerThread";
+import { workerFunc } from "./KTX2WorkerThreadJS";
+import { KTX2FileReader } from './KTX2FileReader';
 
 //const RGB_S3TC_DXT1_Format = 33776;
 //const RGBA_S3TC_DXT5_Format = 33779;
 
-const COMPRESSED_RGBA_BPTC_UNORM_EXT = 36492;
-
-interface IMipmap {
-    data: Nullable<Uint8Array>;
-    width: number;
-    height: number;
-    transcodedFormat: number;
-}
-
 /**
  * Class for loading KTX2 files
  * @hidden
  */
 export class KhronosTextureContainer2 {
 
-    /** @hidden */
-    public static _Transcoders: Array<typeof Transcoder> = [];
-
-    public static registerTranscoder(transcoder: typeof Transcoder) {
-        KhronosTextureContainer2._Transcoders.push(transcoder);
-    }
-
     private _engine: ThinEngine;
 
     private static _WorkerPromise: Nullable<Promise<Worker>> = null;
@@ -67,12 +48,9 @@ export class KhronosTextureContainer2 {
 
     public constructor(engine: ThinEngine) {
         this._engine = engine;
-        //this._transcoderInstances = {};
     }
 
     public uploadAsync(data: ArrayBufferView, internalTexture: InternalTexture): Promise<void> {
-        const kfr = new KTX2FileReader(data);
-
         return new Promise((res, rej) => {
             KhronosTextureContainer2._CreateWorkerAsync().then(() => {
                 const actionId = KhronosTextureContainer2._actionId++;
@@ -92,19 +70,10 @@ export class KhronosTextureContainer2 {
                 KhronosTextureContainer2._Worker!.postMessage({
                     action: "createMipmaps",
                     id: actionId,
-                    kfr: {
-                        header: kfr.header,
-                        textureFormat: kfr.textureFormat,
-                        dfdBlock: kfr.dfdBlock,
-                        levels: kfr.levels,
-                        bufferData: kfr.data.buffer,
-                        supercompressionGlobalData: kfr.supercompressionGlobalData,
-                    },
-                }, [kfr.data.buffer]);
+                    data: data,
+                }, [data.buffer]);
             });
         });
-
-        //return this._createMipmaps(kfr, internalTexture);
     }
 
     protected _createTexture(mipmaps: Array<IMipmap>, internalTexture: InternalTexture) {
@@ -139,147 +108,3 @@ export class KhronosTextureContainer2 {
         return KTX2FileReader.IsValid(data);
     }
 }
-
-// Put in the order you want the transcoders to be used in priority
-KhronosTextureContainer2.registerTranscoder(LiteTranscoder_UASTC_ASTC);
-KhronosTextureContainer2.registerTranscoder(LiteTranscoder_UASTC_BC7);
-KhronosTextureContainer2.registerTranscoder(MSCTranscoder);
-
-/**
- *
- * Worker thread
- *
- */
-
-declare function postMessage(message: any, transfer?: any[]): void;
-function workerFunc(): void {
-
-    let _wasmMemoryManager: WASMMemoryManager;
-    let _transcoderInstances: { [key: string]: Transcoder } = {};
-
-    onmessage = (event) => {
-        if (event.data.action === "init") {
-            postMessage({action: "init"});
-        } else if (event.data.action === "createMipmaps") {
-            _createMipmaps(event.data.kfr).then((mipmaps) => {
-                //if (!success) {
-                //    postMessage({action: "transcode", success: success, id: event.data.id});
-                //} else {
-                    postMessage({ action: "mipmapsCreated", success: true/*success*/, id: event.data.id, mipmaps: mipmaps.mipmaps }, mipmaps.mipmapsData);
-                //}
-            });
-        }
-    };
-
-    const _findTranscoder = (src: sourceTextureFormat, dst: transcodeTarget): Nullable<Transcoder> => {
-        let transcoder: Nullable<Transcoder> = null;
-
-        for (let i = 0; i < KhronosTextureContainer2._Transcoders.length; ++i) {
-            if (KhronosTextureContainer2._Transcoders[i].CanTranscode(src, dst)) {
-                const key = sourceTextureFormat[src] + "_" + transcodeTarget[dst];
-                transcoder = _transcoderInstances[key];
-                if (!transcoder) {
-                    transcoder = new KhronosTextureContainer2._Transcoders[i]();
-                    transcoder!.initialize();
-                    if (transcoder!.needMemoryManager()) {
-                        if (!_wasmMemoryManager) {
-                            _wasmMemoryManager = new WASMMemoryManager();
-                        }
-                        transcoder!.setMemoryManager(_wasmMemoryManager);
-                    }
-                    _transcoderInstances[key] = transcoder;
-                }
-                break;
-            }
-        }
-
-        return transcoder;
-    };
-
-    const _createMipmaps = (kfr: KTX2FileReader): Promise<{ mipmaps: Array<IMipmap>, mipmapsData: Array<ArrayBuffer> }> => {
-        /*await this.zstd.init();*/
-
-        //var mipmaps = [];
-        const width = kfr.header.pixelWidth;
-        const height = kfr.header.pixelHeight;
-        const srcTexFormat = kfr.textureFormat;
-
-        let targetFormat = 1/*transcodeTarget.BC7_M5_RGBA*/;
-        let transcodedFormat = 36492/*COMPRESSED_RGBA_BPTC_UNORM_EXT*/;
-
-        const transcoder = _findTranscoder(srcTexFormat, targetFormat);
-
-        if (transcoder === null) {
-            throw new Error(`KTX2 container - no transcoder found to transcode source texture format "${sourceTextureFormat[srcTexFormat]}" to format "${transcodeTarget[targetFormat]}"`);
-        }
-
-        const mipmaps: Array<IMipmap> = [];
-        const texturePromises: Array<Promise<Nullable<Uint8Array>>> = [];
-        const mipmapsData: Array<ArrayBuffer> = [];
-
-        let firstImageDescIndex = 0;
-
-        for (let level = 0; level < kfr.header.levelCount; level ++) {
-            if (level > 0) {
-                firstImageDescIndex += Math.max(kfr.header.layerCount, 1) * kfr.header.faceCount * Math.max(kfr.header.pixelDepth >> (level - 1), 1);
-            }
-
-            const levelWidth = width / Math.pow(2, level);
-            const levelHeight = height / Math.pow(2, level);
-
-            const numImagesInLevel = kfr.header.faceCount; // note that cubemap are not supported yet (see KTX2FileReader), so faceCount == 1
-            const levelImageByteLength = ((levelWidth + 3) >> 2) * ((levelHeight + 3) >> 2) * kfr.dfdBlock.bytesPlane[0];
-
-            const levelUncompressedByteLength = kfr.levels[level].uncompressedByteLength;
-
-            let levelDataBuffer = kfr.data.buffer;
-            let levelDataOffset = kfr.levels[level].byteOffset;
-            let imageOffsetInLevel = 0;
-
-            if (kfr.header.supercompressionScheme === 2/*supercompressionScheme.ZStandard*/) {
-                //levelDataBuffer = this.zstd.decode(new Uint8Array(levelDataBuffer, levelDataOffset, levelByteLength), levelUncompressedByteLength);
-                levelDataOffset = 0;
-            }
-
-            for (let imageIndex = 0; imageIndex < numImagesInLevel; imageIndex ++) {
-                let encodedData: Uint8Array;
-                let imageDesc: Nullable<IKTX2_ImageDesc> = null;
-
-                if (kfr.header.supercompressionScheme === 1/*supercompressionScheme.BasisLZ*/) {
-                    imageDesc = kfr.supercompressionGlobalData.imageDescs![firstImageDescIndex + imageIndex];
-
-                    encodedData = new Uint8Array(levelDataBuffer, levelDataOffset + imageDesc.rgbSliceByteOffset, imageDesc.rgbSliceByteLength + imageDesc.alphaSliceByteLength);
-                } else {
-                    encodedData = new Uint8Array(levelDataBuffer, levelDataOffset + imageOffsetInLevel, levelImageByteLength);
-
-                    imageOffsetInLevel += levelImageByteLength;
-                }
-
-                const mipmap: IMipmap = {
-                    data: null,
-                    width: levelWidth,
-                    height: levelHeight,
-                    transcodedFormat: transcodedFormat
-                };
-
-                const transcodedData = transcoder.transcode(srcTexFormat, targetFormat, level, levelWidth, levelHeight, levelUncompressedByteLength, kfr, imageDesc, encodedData).
-                        then((data) => {
-                            mipmap.data = data;
-                            if (data) {
-                                mipmapsData.push(data.buffer);
-                            }
-                            return data;
-                        }
-                      );
-
-                mipmaps.push(mipmap);
-
-                texturePromises.push(transcodedData);
-            }
-        }
-
-        return Promise.all(texturePromises).then(() => {
-            return { mipmaps, mipmapsData };
-        });
-    };
-}