Przeglądaj źródła

Code reorganizing

Popov72 5 lat temu
rodzic
commit
23a319a6ed

+ 1 - 1
src/Materials/Textures/Loaders/ktxTextureLoader.ts

@@ -1,5 +1,5 @@
 import { KhronosTextureContainer } from "../../../Misc/khronosTextureContainer";
-import { KhronosTextureContainer2 } from "../../../Misc/khronosTextureContainer2";
+import { KhronosTextureContainer2 } from "../../../Misc/KTX2/khronosTextureContainer2";
 import { Nullable } from "../../../types";
 import { Engine } from "../../../Engines/engine";
 import { InternalTexture } from "../../../Materials/Textures/internalTexture";

+ 350 - 0
src/Misc/KTX2/KTX2FileReader.ts

@@ -0,0 +1,350 @@
+import { DataReader } from '../dataReader';
+import { sourceTextureFormat } from './transcoder';
+
+/** @hidden */
+export enum supercompressionScheme {
+    BasisLZ = 1,
+    ZStandard = 2
+}
+
+const enum dfdModel {
+    ETC1S = 163,
+    UASTC = 166
+}
+
+const enum dfdChannel_ETC1S {
+    RGB = 0,
+    RRR = 3,
+    GGG = 4,
+    AAA = 15
+
+}
+
+const enum dfdChannel_UASTC {
+    RGB  = 0,
+    RGBA = 3,
+    RRR  = 4,
+    RRRG = 5
+}
+
+/** @hidden */
+export interface IKTX2_Header {
+    vkFormat: number;
+    typeSize: number;
+    pixelWidth: number;
+    pixelHeight: number;
+    pixelDepth: number;
+    layerCount: number;
+    faceCount: number;
+    levelCount: number;
+    supercompressionScheme: number;
+    dfdByteOffset: number;
+    dfdByteLength: number;
+    kvdByteOffset: number;
+    kvdByteLength: number;
+    sgdByteOffset: number;
+    sgdByteLength: number;
+}
+
+/** @hidden */
+export interface IKTX2_Level {
+    byteOffset: number;
+    byteLength: number;
+    uncompressedByteLength: number;
+}
+
+interface IKTX2_Sample {
+    bitOffset: number;
+    bitLength: number;
+    channelType: number;
+    channelFlags: number;
+    samplePosition: number[];
+    sampleLower: number;
+    sampleUpper: number;
+}
+
+interface IKTX2_DFD {
+    vendorId: number;
+    descriptorType: number;
+    versionNumber: number;
+    descriptorBlockSize: number;
+    colorModel: number;
+    colorPrimaries: number;
+    transferFunction: number;
+    flags: number;
+    texelBlockDimension: {
+        x: number;
+        y: number;
+        z: number;
+        w: number;
+    };
+    bytesPlane: Array<number>;
+    numSamples: number;
+    samples: Array<IKTX2_Sample>;
+}
+
+/** @hidden */
+export interface IKTX2_ImageDesc {
+    imageFlags: number;
+    rgbSliceByteOffset: number;
+    rgbSliceByteLength: number;
+    alphaSliceByteOffset: number;
+    alphaSliceByteLength: number;
+}
+
+/** @hidden */
+export interface IKTX2_SupercompressionGlobalData {
+    endpointCount?: number;
+    selectorCount?: number;
+    endpointsByteLength?: number;
+    selectorsByteLength?: number;
+    tablesByteLength?: number;
+    extendedByteLength?: number;
+    imageDescs?: Array<IKTX2_ImageDesc>;
+    endpointsData?: Uint8Array;
+    selectorsData?: Uint8Array;
+    tablesData?: Uint8Array;
+    extendedData?: Uint8Array;
+}
+
+/**
+ * Based on https://github.com/mrdoob/three.js/blob/dfb5c23ce126ec845e4aa240599915fef5375797/examples/jsm/loaders/KTX2Loader.js
+ * @hidden
+ */
+export class KTX2FileReader {
+
+    //private static readonly VK_FORMAT_UNDEFINED = 0x00;
+
+    private _data: ArrayBufferView;
+    private _header: IKTX2_Header;
+    private _levels: Array<IKTX2_Level>;
+    private _dfdBlock: IKTX2_DFD;
+    private _supercompressionGlobalData: IKTX2_SupercompressionGlobalData;
+
+    /**
+     * Will throw an exception if the file can't be parsed
+     */
+    constructor(data: ArrayBufferView) {
+        this._data = data;
+
+        this._parseData();
+    }
+
+    public get data(): ArrayBufferView {
+        return this._data;
+    }
+
+    public get header(): IKTX2_Header {
+        return this._header;
+    }
+
+    public get levels(): Array<IKTX2_Level> {
+        return this._levels;
+    }
+
+    public get supercompressionGlobalData(): IKTX2_SupercompressionGlobalData {
+        return this._supercompressionGlobalData;
+    }
+
+    private _parseData() {
+        let offsetInFile = 12; // skip the header
+
+        /**
+         * Get the header
+         */
+        const hdrReader = new DataReader().setBuffer(this._data, offsetInFile, 17 * 4);
+
+        const 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
+         */
+        let levelCount = Math.max(1, header.levelCount);
+
+        const levelReader = new DataReader().setBuffer(this._data, offsetInFile, levelCount * 3 * (2 * 4));
+
+        const levels: Array<IKTX2_Level> = 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
+         */
+        const dfdReader = new DataReader().setBuffer(this._data, header.dfdByteOffset, header.dfdByteLength);
+
+        const 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(), /* bytesPlane0 */
+                dfdReader.readUint8(), /* bytesPlane1 */
+                dfdReader.readUint8(), /* bytesPlane2 */
+                dfdReader.readUint8(), /* bytesPlane3 */
+                dfdReader.readUint8(), /* bytesPlane4 */
+                dfdReader.readUint8(), /* bytesPlane5 */
+                dfdReader.readUint8(), /* bytesPlane6 */
+                dfdReader.readUint8(), /* bytesPlane7 */
+            ],
+            numSamples: 0,
+            samples: new Array<IKTX2_Sample>(),
+        };
+
+        dfdBlock.numSamples = (dfdBlock.descriptorBlockSize - 24) / 16;
+
+        for (let i = 0; i < dfdBlock.numSamples; i++) {
+            const sample = {
+                bitOffset: dfdReader.readUint16(),
+                bitLength: dfdReader.readUint8() + 1,
+                channelType: dfdReader.readUint8(),
+                channelFlags: 0,
+                samplePosition: [
+                    dfdReader.readUint8(), /* samplePosition0 */
+                    dfdReader.readUint8(), /* samplePosition1 */
+                    dfdReader.readUint8(), /* samplePosition2 */
+                    dfdReader.readUint8(), /* samplePosition3 */
+                ],
+                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)
+         */
+        const sgd: IKTX2_SupercompressionGlobalData = this._supercompressionGlobalData = {};
+
+        if (header.sgdByteLength > 0) {
+            const 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 = [];
+
+            for (let i = 0; i < header.levelCount; i ++) {
+                sgd.imageDescs.push({
+                    imageFlags: sgdReader.readUint32(),
+                    rgbSliceByteOffset: sgdReader.readUint32(),
+                    rgbSliceByteLength: sgdReader.readUint32(),
+                    alphaSliceByteOffset: sgdReader.readUint32(),
+                    alphaSliceByteLength: sgdReader.readUint32(),
+                });
+            }
+
+            const endpointsByteOffset = header.sgdByteOffset + sgdReader.byteOffset;
+            const selectorsByteOffset = endpointsByteOffset + sgd.endpointsByteLength;
+            const tablesByteOffset = selectorsByteOffset + sgd.selectorsByteLength;
+            const 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);
+
+    }
+
+    public get textureFormat(): sourceTextureFormat {
+        return this._dfdBlock.colorModel === dfdModel.UASTC ? sourceTextureFormat.UASTC4x4 : sourceTextureFormat.ETC1S;
+    }
+
+    public get hasAlpha(): boolean {
+        const 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;
+    }
+
+    public static IsValid(data: ArrayBufferView): boolean {
+        if (data.byteLength >= 12) {
+            // '«', 'K', 'T', 'X', ' ', '2', '0', '»', '\r', '\n', '\x1A', '\n'
+            const 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;
+    }
+}

+ 60 - 0
src/Misc/KTX2/LiteTranscoder.ts

@@ -0,0 +1,60 @@
+import { Nullable } from '../../types';
+import { Tools } from '../tools';
+import { Transcoder, sourceTextureFormat, transcodeTarget } from './transcoder';
+import { WASMMemoryManager } from './wasmMemoryManager';
+import { IKTX2_SupercompressionGlobalData, IKTX2_ImageDesc } from './KTX2FileReader';
+
+export class LiteTranscoder extends Transcoder {
+
+    private _modulePath: string;
+    private _modulePromise: Promise<{ module: any }>;
+    private _memoryManager: WASMMemoryManager;
+
+    protected async _loadModule(): Promise<{ module: any }> {
+        if (this._modulePromise) {
+            return this._modulePromise;
+        }
+
+        this._modulePromise = new Promise((resolve) => {
+            Tools.LoadFileAsync(this._modulePath).then((wasmBinary: string | ArrayBuffer) => {
+                WebAssembly.instantiate(wasmBinary as ArrayBuffer, { env: { memory: this._memoryManager.wasmMemory } }).then((moduleWrapper) => {
+                    resolve({ module: moduleWrapper.instance.exports });
+                });
+            });
+        });
+
+        return this._modulePromise;
+    }
+
+    protected get memoryManager(): WASMMemoryManager {
+        return this._memoryManager;
+    }
+
+    protected setModulePath(modulePath: string): void {
+        this._modulePath = modulePath;
+    }
+
+    public needMemoryManager(): boolean {
+        return true;
+    }
+
+    public setMemoryManager(memoryMgr: WASMMemoryManager): void {
+        this._memoryManager = memoryMgr;
+    }
+
+    public transcode(src: sourceTextureFormat, dst: transcodeTarget, level: number, width: number, height: number, levelUncompressedByteLength: number, hasAlpha: boolean, sgd: IKTX2_SupercompressionGlobalData, imageDesc: Nullable<IKTX2_ImageDesc>, encodedData: Uint8Array): Promise<Nullable<Uint8Array>> {
+        return this._loadModule().then((moduleWrapper: any) => {
+            const transcoder: any = moduleWrapper.module;
+
+            const nBlocks = ((width + 3) >> 2) * ((height + 3) >> 2);
+
+            const texMemoryPages = ((nBlocks * 16 + 65535) >> 16) + 1;
+
+            const textureView = this.memoryManager.getMemoryView(texMemoryPages, 65536, nBlocks * 16);
+
+            textureView.set(encodedData);
+
+            return transcoder.transcode(nBlocks) === 0 ? textureView.slice() : null;
+        });
+    }
+}

+ 16 - 0
src/Misc/KTX2/LiteTranscoder_UASTC_ASTC.ts

@@ -0,0 +1,16 @@
+import { sourceTextureFormat, transcodeTarget } from './transcoder';
+import { LiteTranscoder } from './LiteTranscoder';
+
+/**
+ * @hidden
+ */
+export class LiteTranscoder_UASTC_ASTC extends LiteTranscoder {
+
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget): boolean {
+        return src === sourceTextureFormat.UASTC4x4 && dst === transcodeTarget.ASTC_4x4_RGBA;
+    }
+
+    public initialize(): void {
+        this.setModulePath("/dist/preview release/basisTranscoder/uastc_astc.wasm");
+    }
+}

+ 16 - 0
src/Misc/KTX2/LiteTranscoder_UASTC_BC7.ts

@@ -0,0 +1,16 @@
+import { sourceTextureFormat, transcodeTarget } from './transcoder';
+import { LiteTranscoder } from './LiteTranscoder';
+
+/**
+ * @hidden
+ */
+export class LiteTranscoder_UASTC_BC7 extends LiteTranscoder {
+
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget): boolean {
+        return src === sourceTextureFormat.UASTC4x4 && dst === transcodeTarget.BC7_M5_RGBA;
+    }
+
+    public initialize(): void {
+        this.setModulePath("/dist/preview release/basisTranscoder/uastc_bc7.wasm");
+    }
+}

+ 8 - 0
src/Misc/KTX2/index.ts

@@ -0,0 +1,8 @@
+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";

+ 162 - 0
src/Misc/KTX2/khronosTextureContainer2.ts

@@ -0,0 +1,162 @@
+import { InternalTexture } from "../../Materials/Textures/internalTexture";
+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";
+
+//const RGB_S3TC_DXT1_Format = 33776;
+//const RGBA_S3TC_DXT5_Format = 33779;
+
+const COMPRESSED_RGBA_BPTC_UNORM_EXT = 36492;
+
+/**
+ * Class for loading KTX2 files
+ * @hidden
+ */
+export class KhronosTextureContainer2 {
+
+    private static _Transcoders: Array<typeof Transcoder> = [];
+
+    public static registerTranscoder(transcoder: typeof Transcoder) {
+        KhronosTextureContainer2._Transcoders.push(transcoder);
+    }
+
+    private _engine: ThinEngine;
+    private _wasmMemoryManager: WASMMemoryManager;
+    private _transcoderInstances: { [key: string]: Transcoder };
+
+    public constructor(engine: ThinEngine) {
+        this._engine = engine;
+        this._transcoderInstances = {};
+    }
+
+    private _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 = this._transcoderInstances[key];
+                if (!transcoder) {
+                    transcoder = new KhronosTextureContainer2._Transcoders[i]();
+                    transcoder!.initialize();
+                    if (transcoder!.needMemoryManager()) {
+                        if (!this._wasmMemoryManager) {
+                            this._wasmMemoryManager = new WASMMemoryManager();
+                        }
+                        transcoder!.setMemoryManager(this._wasmMemoryManager);
+                    }
+                    this._transcoderInstances[key] = transcoder;
+                }
+                break;
+            }
+        }
+
+        return transcoder;
+    }
+
+    private async _createTexture(kfr: KTX2FileReader, internalTexture: InternalTexture) {
+        /*await this.zstd.init();*/
+
+        //var mipmaps = [];
+        var width = kfr.header.pixelWidth;
+        var height = kfr.header.pixelHeight;
+        var srcTexFormat = kfr.textureFormat;
+        var hasAlpha = kfr.hasAlpha;
+
+        let targetFormat = transcodeTarget.BC7_M5_RGBA;
+        let transcodedFormat = COMPRESSED_RGBA_BPTC_UNORM_EXT;
+
+        const transcoder = this._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 texturePromises: Array<Promise<Nullable<Uint8Array>>> = [];
+
+        var imageDescIndex = 0;
+
+        for (var level = 0; level < kfr.header.levelCount; level ++) {
+            var levelWidth = width / Math.pow(2, level);
+            var levelHeight = height / Math.pow(2, level);
+
+            var numImagesInLevel = 1; // TODO(donmccurdy): Support cubemaps, arrays and 3D.
+            var imageOffsetInLevel = 0;
+            var levelByteLength = kfr.levels[level].byteLength;
+            var levelUncompressedByteLength = kfr.levels[level].uncompressedByteLength;
+
+            for (var imageIndex = 0; imageIndex < numImagesInLevel; imageIndex ++) {
+                let encodedData: Uint8Array;
+                let imageDesc: Nullable<IKTX2_ImageDesc> = null;
+
+                if (srcTexFormat === sourceTextureFormat.ETC1S) {
+                    imageDesc = kfr.supercompressionGlobalData.imageDescs![imageDescIndex++];
+
+                    encodedData = new Uint8Array(kfr.data.buffer, kfr.levels[level].byteOffset + imageDesc.rgbSliceByteOffset, imageDesc.rgbSliceByteLength + imageDesc.alphaSliceByteLength);
+                } else {
+                    encodedData = new Uint8Array(kfr.data.buffer, kfr.levels[level].byteOffset + imageOffsetInLevel, levelByteLength);
+
+                    if (kfr.header.supercompressionScheme === supercompressionScheme.ZStandard) {
+                        //encodedData = this.zstd.decode( encodedData, levelUncompressedByteLength );
+                    }
+                }
+
+                texturePromises.push(transcoder.transcode(srcTexFormat, targetFormat, level, levelWidth, levelHeight, levelUncompressedByteLength, hasAlpha, kfr.supercompressionGlobalData, imageDesc, encodedData));
+
+                imageOffsetInLevel += levelByteLength;
+            }
+        }
+
+        Promise.all(texturePromises).then((textures) => {
+            for (let t = 0; t < textures.length; ++t) {
+                const textureData = textures[t];
+
+                if (textureData === null) {
+                    throw new Error("KTX2 container - could not transcode one of the image");
+                }
+
+                internalTexture.width = internalTexture.baseWidth = width;
+                internalTexture.height = internalTexture.baseHeight = height;
+                internalTexture.generateMipMaps = false;
+                internalTexture.invertY = false;
+
+                this._engine._bindTextureDirectly(this._engine._gl.TEXTURE_2D, internalTexture);
+                this._engine._uploadCompressedDataToTextureDirectly(internalTexture, transcodedFormat, width, height, textureData, 0, 0);
+
+                internalTexture.isReady = true;
+                break;
+            }
+        });
+
+        //this.mipmaps = mipmaps;
+    }
+
+    public uploadAsync(data: ArrayBufferView, internalTexture: InternalTexture): Promise<void> {
+        const kfr = new KTX2FileReader(data);
+
+        return this._createTexture(kfr, internalTexture);
+
+    }
+
+    /**
+     * Checks if the given data starts with a KTX2 file identifier.
+     * @param data the data to check
+     * @returns true if the data is a KTX2 file or false otherwise
+     */
+    public static IsValid(data: ArrayBufferView): boolean {
+        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);

+ 93 - 0
src/Misc/KTX2/mscTranscoder.ts

@@ -0,0 +1,93 @@
+import { Nullable } from '../../types';
+import { Transcoder, sourceTextureFormat, transcodeTarget } from './transcoder';
+import { IKTX2_SupercompressionGlobalData, IKTX2_ImageDesc } from './KTX2FileReader';
+
+declare var MSC_TRANSCODER: any;
+
+/**
+ * @hidden
+ */
+export class MSCTranscoder extends Transcoder {
+
+    private _mscBasisTranscoderPromise: Promise<any>;
+    private _mscBasisModule: any;
+
+    private _getMSCBasisTranscoder() {
+        if (this._mscBasisTranscoderPromise) {
+            return this._mscBasisTranscoderPromise;
+        }
+
+        this._mscBasisTranscoderPromise = new Promise((resolve) => {
+            MSC_TRANSCODER().then((basisModule: any) => {
+                basisModule.initTranscoders();
+                this._mscBasisModule = basisModule;
+                resolve();
+            });
+        });
+
+        return this._mscBasisTranscoderPromise;
+    }
+
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget): boolean {
+        return true;
+    }
+
+    public transcode(src: sourceTextureFormat, dst: transcodeTarget, level: number, width: number, height: number, levelUncompressedByteLength: number, hasAlpha: boolean, sgd: IKTX2_SupercompressionGlobalData, imageDesc: Nullable<IKTX2_ImageDesc>, encodedData: Uint8Array): Promise<Nullable<Uint8Array>> {
+        const isVideo = false;
+        return this._getMSCBasisTranscoder().then(() => {
+            const basisModule = this._mscBasisModule;
+
+            const TranscodeTarget: any = basisModule.TranscodeTarget;
+            const TextureFormat: any = basisModule.TextureFormat;
+            const ImageInfo: any = 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);
+
+            const 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.`);
+            }
+
+            let result: any;
+
+            if (src === sourceTextureFormat.ETC1S) {
+                var numEndpoints = sgd.endpointCount;
+                var numSelectors = sgd.selectorCount;
+                var endpoints = sgd.endpointsData;
+                var selectors = sgd.selectorsData;
+                var tables = sgd.tablesData;
+
+                transcoder.decodePalettes(numEndpoints, endpoints, numSelectors, selectors);
+                transcoder.decodeTables(tables);
+
+                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 = levelUncompressedByteLength;
+                imageInfo.alphaByteOffset = 0;
+                imageInfo.alphaByteLength = 0;
+
+                result = transcoder.transcodeImage(targetFormat, encodedData, imageInfo, 0, hasAlpha, isVideo);
+            }
+
+            if (result && result.transcodedImage !== undefined) {
+                const textureData = result.transcodedImage.get_typed_memory_view().slice();
+                result.transcodedImage.delete();
+                return textureData;
+            }
+
+            return null;
+        });
+    }
+}

+ 50 - 0
src/Misc/KTX2/transcoder.ts

@@ -0,0 +1,50 @@
+import { Nullable } from '../../types';
+import { WASMMemoryManager } from './wasmMemoryManager';
+import { IKTX2_SupercompressionGlobalData, IKTX2_ImageDesc } from './KTX2FileReader';
+
+/**
+ * @hidden
+ */
+export enum sourceTextureFormat {
+    ETC1S,
+    UASTC4x4
+}
+
+/**
+ * @hidden
+ */
+export enum transcodeTarget {
+    ASTC_4x4_RGBA,
+    BC7_M5_RGBA,
+    BC3_RGBA,
+    BC1_RGB,
+    PVRTC1_4_RGBA,
+    PVRTC1_4_RGB,
+    ETC2_RGBA,
+    ETC1_RGB,
+    RGBA32
+}
+
+/**
+ * @hidden
+ */
+export class Transcoder {
+
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget): boolean {
+        return false;
+    }
+
+    public initialize(): void {
+    }
+
+    public needMemoryManager(): boolean {
+        return false;
+    }
+
+    public setMemoryManager(memoryMgr: WASMMemoryManager): void {
+    }
+
+    public transcode(src: sourceTextureFormat, dst: transcodeTarget, level: number, width: number, height: number, levelUncompressedByteLength: number, hasAlpha: boolean, sgd: IKTX2_SupercompressionGlobalData, imageDesc: Nullable<IKTX2_ImageDesc>, encodedData: Uint8Array): Promise<Nullable<Uint8Array>> {
+        return Promise.resolve(null);
+    }
+}

+ 44 - 0
src/Misc/KTX2/wasmMemoryManager.ts

@@ -0,0 +1,44 @@
+/**
+ * @hidden
+ */
+export class WASMMemoryManager {
+
+    private _memory: WebAssembly.Memory;
+    private _numPages: number;
+    private _memoryView: Uint8Array;
+    private _memoryViewByteLength: number;
+    private _memoryViewOffset: number;
+
+    constructor(initialMemoryPages: number = (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);
+    }
+
+    public get wasmMemory(): WebAssembly.Memory {
+        return this._memory;
+    }
+
+    public getMemoryView(numPages: number, offset: number = 0, byteLength?: number): Uint8Array {
+        byteLength = 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;
+    }
+}

+ 0 - 551
src/Misc/khronosTextureContainer2.ts

@@ -1,551 +0,0 @@
-import { InternalTexture } from "../Materials/Textures/internalTexture";
-import { ThinEngine } from "../Engines/thinEngine";
-import { EngineCapabilities } from '../Engines/engineCapabilities';
-import { Tools } from './tools';
-import { DataReader } from './dataReader';
-import { Constants } from '../Engines/constants';
-import { Nullable } from '../types';
-
-declare var MSC_TRANSCODER: any;
-
-const RGB_S3TC_DXT1_Format = 33776;
-const RGBA_S3TC_DXT5_Format = 33779;
-
-const COMPRESSED_RGBA_BPTC_UNORM_EXT = 36492;
-
-const enum supercompressionScheme {
-    BasisLZ = 1,
-    ZStandard = 2
-}
-
-const enum dfdModel {
-    ETC1S = 163, /* not supported yet */
-    UASTC = 166
-}
-
-const enum textureFormat {
-    ETC1S,
-    UASTC4x4
-}
-
-const enum dfdChannel_ETC1S {
-    RGB = 0,
-    RRR = 3,
-    GGG = 4,
-    AAA = 15
-
-}
-
-const enum dfdChannel_UASTC {
-    RGB  = 0,
-    RGBA = 3,
-    RRR  = 4,
-    RRRG = 5
-}
-
-interface IKTX2_Header {
-    vkFormat: number;
-    typeSize: number;
-    pixelWidth: number;
-    pixelHeight: number;
-    pixelDepth: number;
-    layerCount: number;
-    faceCount: number;
-    levelCount: number;
-    supercompressionScheme: number;
-    dfdByteOffset: number;
-    dfdByteLength: number;
-    kvdByteOffset: number;
-    kvdByteLength: number;
-    sgdByteOffset: number;
-    sgdByteLength: number;
-}
-
-interface IKTX2_Level {
-    byteOffset: number;
-    byteLength: number;
-    uncompressedByteLength: number;
-}
-
-interface IKTX2_Sample {
-    bitOffset: number;
-    bitLength: number;
-    channelType: number;
-    channelFlags: number;
-    samplePosition: number[];
-    sampleLower: number;
-    sampleUpper: number;
-}
-
-interface IKTX2_DFD {
-    vendorId: number;
-    descriptorType: number;
-    versionNumber: number;
-    descriptorBlockSize: number;
-    colorModel: number;
-    colorPrimaries: number;
-    transferFunction: number;
-    flags: number;
-    texelBlockDimension: {
-        x: number;
-        y: number;
-        z: number;
-        w: number;
-    };
-    bytesPlane: Array<number>;
-    numSamples: number;
-    samples: Array<IKTX2_Sample>;
-}
-
-interface IKTX2_ImageDesc {
-    imageFlags: number;
-    rgbSliceByteOffset: number;
-    rgbSliceByteLength: number;
-    alphaSliceByteOffset: number;
-    alphaSliceByteLength: number;
-}
-
-interface IKTX2_SupercompressionGlobalData {
-    endpointCount?: number;
-    selectorCount?: number;
-    endpointsByteLength?: number;
-    selectorsByteLength?: number;
-    tablesByteLength?: number;
-    extendedByteLength?: number;
-    imageDescs?: Array<IKTX2_ImageDesc>;
-    endpointsData?: Uint8Array;
-    selectorsData?: Uint8Array;
-    tablesData?: Uint8Array;
-    extendedData?: Uint8Array;
-}
-
-/**
- * Class for loading KTX2 files
- * !!! Experimental Extension Subject to Changes !!!
- * @hidden
- */
-export class KhronosTextureContainer2 {
-    public static WasmModules = {
-        "uastc_astc" : "/dist/preview release/basisTranscoder/uastc_astc.wasm",
-        "uastc_bc7" : "/dist/preview release/basisTranscoder/uastc_bc7.wasm",
-    };
-
-    private static _ModulePromise: Promise<{ module: any }>;
-    private static _TranscodeFormat: number;
-
-    private static readonly VK_FORMAT_UNDEFINED = 0x00;
-
-    private _engine: ThinEngine;
-
-    private _data: ArrayBufferView;
-    private _header: IKTX2_Header;
-    private _levels: Array<IKTX2_Level>;
-    private _dfdBlock: IKTX2_DFD;
-    private _sgd: IKTX2_SupercompressionGlobalData;
-
-    private _mscBasisTranscoder: Promise<any>;
-
-    public constructor(engine: ThinEngine) {
-        this._engine = engine;
-
-        if (!KhronosTextureContainer2._ModulePromise) {
-            KhronosTextureContainer2._ModulePromise = new Promise((resolve) => {
-                Tools.LoadFileAsync(KhronosTextureContainer2.WasmModules.uastc_bc7).then((wasmBinary) => {
-                    resolve({ module: wasmBinary });
-                });
-            });
-        }
-    }
-
-    private _mscBasisModule: any;
-
-    private _getMSCBasisTranscoder() {
-        if (this._mscBasisTranscoder) {
-            return this._mscBasisTranscoder;
-        }
-
-        this._mscBasisTranscoder = new Promise((resolve) => {
-            MSC_TRANSCODER().then((basisModule: any) => {
-                basisModule.initTranscoders();
-                this._mscBasisModule = basisModule;
-                resolve();
-            });
-        });
-
-        return this._mscBasisTranscoder;
-    }
-    /**
-     * Based on https://github.com/mrdoob/three.js/blob/dfb5c23ce126ec845e4aa240599915fef5375797/examples/jsm/loaders/KTX2Loader.js
-     */
-    private _parseData() {
-        let offsetInFile = 12; // skip the header
-
-        /**
-         * Get the header
-         */
-        const hdrReader = new DataReader().setBuffer(this._data, offsetInFile, 17 * 4);
-
-        const 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 upload - Only 2D textures are currently supported.`);
-        }
-
-        if (header.layerCount > 1) {
-            throw new Error(`Failed to upload - Array textures are not currently supported.`);
-        }
-
-        if (header.faceCount > 1) {
-            throw new Error(`Failed to upload - Cube textures are not currently supported.`);
-        }
-
-        console.log(header);
-
-        offsetInFile += hdrReader.byteOffset;
-
-        /**
-         * Get the levels
-         */
-        let levelCount = Math.max(1, header.levelCount);
-
-        const levelReader = new DataReader().setBuffer(this._data, offsetInFile, levelCount * 3 * (2 * 4));
-
-        const levels: Array<IKTX2_Level> = 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
-         */
-        const dfdReader = new DataReader().setBuffer(this._data, header.dfdByteOffset, header.dfdByteLength);
-
-        const 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(), /* bytesPlane0 */
-                dfdReader.readUint8(), /* bytesPlane1 */
-                dfdReader.readUint8(), /* bytesPlane2 */
-                dfdReader.readUint8(), /* bytesPlane3 */
-                dfdReader.readUint8(), /* bytesPlane4 */
-                dfdReader.readUint8(), /* bytesPlane5 */
-                dfdReader.readUint8(), /* bytesPlane6 */
-                dfdReader.readUint8(), /* bytesPlane7 */
-            ],
-            numSamples: 0,
-            samples: new Array<IKTX2_Sample>(),
-        };
-
-        dfdBlock.numSamples = (dfdBlock.descriptorBlockSize - 24) / 16;
-
-        for (let i = 0; i < dfdBlock.numSamples; i++) {
-            const sample = {
-                bitOffset: dfdReader.readUint16(),
-                bitLength: dfdReader.readUint8() + 1,
-                channelType: dfdReader.readUint8(),
-                channelFlags: 0,
-                samplePosition: [
-                    dfdReader.readUint8(), /* samplePosition0 */
-                    dfdReader.readUint8(), /* samplePosition1 */
-                    dfdReader.readUint8(), /* samplePosition2 */
-                    dfdReader.readUint8(), /* samplePosition3 */
-                ],
-                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 (dfdBlock.colorModel !== dfdModel.UASTC) {
-            throw new Error(`Failed to upload - Only UASTC color model files are currently supported.`);
-        }
-
-        /*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)
-         */
-        const sgd: IKTX2_SupercompressionGlobalData = this._sgd = {};
-
-        if (header.sgdByteLength > 0) {
-            const 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 = [];
-
-            for (let i = 0; i < header.levelCount; i ++) {
-                sgd.imageDescs.push({
-                    imageFlags: sgdReader.readUint32(),
-                    rgbSliceByteOffset: sgdReader.readUint32(),
-                    rgbSliceByteLength: sgdReader.readUint32(),
-                    alphaSliceByteOffset: sgdReader.readUint32(),
-                    alphaSliceByteLength: sgdReader.readUint32(),
-                });
-            }
-
-            const endpointsByteOffset = header.sgdByteOffset + sgdReader.byteOffset;
-            const selectorsByteOffset = endpointsByteOffset + sgd.endpointsByteLength;
-            const tablesByteOffset = selectorsByteOffset + sgd.selectorsByteLength;
-            const 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);
-
-    }
-
-    private get textureFormat(): textureFormat {
-        return this._dfdBlock.colorModel === dfdModel.UASTC ? textureFormat.UASTC4x4 : textureFormat.ETC1S;
-    }
-
-    private get hasAlpha(): boolean {
-        const tformat = this.textureFormat;
-
-        switch (tformat) {
-            case textureFormat.ETC1S:
-                return this._dfdBlock.numSamples === 2 && (this._dfdBlock.samples[0].channelType === dfdChannel_ETC1S.AAA || this._dfdBlock.samples[1].channelType === dfdChannel_ETC1S.AAA);
-            case textureFormat.UASTC4x4:
-                return this._dfdBlock.samples[0].channelType === dfdChannel_UASTC.RGBA;
-        }
-
-        return false;
-    }
-
-    private async _initMipmaps(internalTexture: InternalTexture) {
-
-        await this._getMSCBasisTranscoder();
-
-        const basisModule = this._mscBasisModule;
-
-        const TranscodeTarget: any = basisModule.TranscodeTarget;
-        const TextureFormat: any = basisModule.TextureFormat;
-        const ImageInfo: any = basisModule.ImageInfo;
-
-        /*await this.zstd.init();*/
-
-        var mipmaps = [];
-        var width = this._header.pixelWidth;
-        var height = this._header.pixelHeight;
-        var texFormat = this.textureFormat;
-        var hasAlpha = this.hasAlpha;
-        var isVideo = false;
-
-        var BasisLzEtc1sImageTranscoder = basisModule.BasisLzEtc1sImageTranscoder;
-        var UastcImageTranscoder = basisModule.UastcImageTranscoder;
-
-        var transcoder = texFormat === textureFormat.UASTC4x4 ? new UastcImageTranscoder() : new BasisLzEtc1sImageTranscoder();
-
-        let targetFormat = hasAlpha ? TranscodeTarget.BC7_RGBA : TranscodeTarget.BC1_RGB;
-        //let transcodedFormat = hasAlpha ? RGBA_S3TC_DXT5_Format : RGB_S3TC_DXT1_Format;
-        let transcodedFormat = COMPRESSED_RGBA_BPTC_UNORM_EXT;
-
-        const useMSCTranscoder = false;
-
-        for (var level = 0; level < this._header.levelCount; level ++) {
-            var levelWidth = width / Math.pow(2, level);
-            var levelHeight = height / Math.pow(2, level);
-
-            var numImagesInLevel = 1; // TODO(donmccurdy): Support cubemaps, arrays and 3D.
-            var imageOffsetInLevel = 0;
-            var imageInfo = new ImageInfo(texFormat, levelWidth, levelHeight, level);
-            var levelByteLength = this._levels[level].byteLength;
-            var levelUncompressedByteLength = this._levels[level].uncompressedByteLength;
-
-            for (var imageIndex = 0; imageIndex < numImagesInLevel; imageIndex ++) {
-                if (texFormat === textureFormat.UASTC4x4) {
-                    // UASTC
-                    let textureData: Nullable<Uint8Array> = null;
-                    if (useMSCTranscoder) {
-                        imageInfo.flags = 0;
-                        imageInfo.rgbByteOffset = 0;
-                        imageInfo.rgbByteLength = levelUncompressedByteLength;
-                        imageInfo.alphaByteOffset = 0;
-                        imageInfo.alphaByteLength = 0;
-
-                        let encodedData = new Uint8Array(this._data.buffer, this._levels[level].byteOffset + imageOffsetInLevel, levelByteLength);
-
-                        if (this._header.supercompressionScheme === supercompressionScheme.ZStandard) {
-                            //encodedData = this.zstd.decode( encodedData, levelUncompressedByteLength );
-                        }
-
-                        const result: any = transcoder.transcodeImage(targetFormat, encodedData, imageInfo, 0, hasAlpha, isVideo);
-
-                        if (result) {
-                            textureData = result.transcodedImage.get_typed_memory_view().slice();
-                            result.transcodedImage.delete();
-                        }
-                    } else {
-                        const nBlocks = ((levelWidth + 3) >> 2) * ((levelHeight + 3) >> 2);
-
-                        const texMemoryPages = (nBlocks * 16 + 65535) >> 16;
-                        const memory = new WebAssembly.Memory({ initial: texMemoryPages + 1 });
-                        const textureView = new Uint8Array(memory.buffer, 65536, nBlocks * 16);
-
-                        const encodedData = new Uint8Array(this._data.buffer, this._levels[level].byteOffset + imageOffsetInLevel, levelByteLength);
-
-                        textureView.set(encodedData);
-
-                        const transcoder = (
-                            await WebAssembly.instantiateStreaming(
-                                fetch(KhronosTextureContainer2.WasmModules.uastc_bc7),
-                                { env: { memory: memory } }
-                            )
-                        ).instance.exports;
-
-                        if ((transcoder as any).transcode(nBlocks) === 0) {
-                            textureData = textureView;
-                        }
-                    }
-                    if (textureData) {
-                        console.log("yes!");
-
-                        internalTexture.width = internalTexture.baseWidth = levelWidth;
-                        internalTexture.height = internalTexture.baseHeight = levelHeight;
-                        internalTexture.generateMipMaps = false;
-                        internalTexture.invertY = false;
-
-                        this._engine._bindTextureDirectly(this._engine._gl.TEXTURE_2D, internalTexture);
-                        this._engine._uploadCompressedDataToTextureDirectly(internalTexture, transcodedFormat, levelWidth, levelHeight, textureData, 0, 0);
-
-                        internalTexture.isReady = true;
-                    } else {
-                        console.log("no...");
-                    }
-
-                    /*if (this._header.supercompressionScheme === supercompressionScheme.ZStandard) {
-                        encodedData = this.zstd.decode(encodedData, levelUncompressedByteLength);
-                    }*/
-
-                    //result = transcoder.transcodeImage(targetFormat, encodedData, imageInfo, 0, hasAlpha, isVideo);
-                    //mipmaps.push({ data: levelData, width: levelWidth, height: levelHeight });
-                }
-
-                imageOffsetInLevel += levelByteLength;
-            }
-        }
-
-        //this.mipmaps = mipmaps;
-    }
-
-    public uploadAsync(data: ArrayBufferView, internalTexture: InternalTexture): Promise<void> {
-        this._data = data;
-        return KhronosTextureContainer2._ModulePromise.then((moduleWrapper: any) => {
-            const module = moduleWrapper.module;
-
-            this._parseData();
-            this._initMipmaps(internalTexture);
-
-            /*const ktxTexture = new module.ktxTexture(data);
-            try {
-                if (ktxTexture.needsTranscoding) {
-                    ktxTexture.transcodeBasis(KhronosTextureContainer2._TranscodeFormat, 0);
-                }
-
-                internalTexture.width = internalTexture.baseWidth = ktxTexture.baseWidth;
-                internalTexture.height = internalTexture.baseHeight = ktxTexture.baseHeight;
-                internalTexture.generateMipMaps = false;
-
-                const result = ktxTexture.glUpload();
-                if (result.error === 0) {
-                    internalTexture._webGLTexture = result.texture;
-                }
-                else {
-                    throw new Error(`Failed to upload: ${result.error}`);
-                }
-
-                internalTexture.isReady = true;
-            }
-            finally {
-                ktxTexture.delete();
-            }*/
-        });
-    }
-
-    private _determineTranscodeFormat(transcodeTarget: any, caps: EngineCapabilities): number {
-        if (caps.s3tc) {
-            return transcodeTarget.BC1_OR_3;
-        }
-        else if (caps.etc2) {
-            return transcodeTarget.ETC;
-        }
-
-        throw new Error("No compatible format available");
-    }
-
-    /**
-     * Checks if the given data starts with a KTX2 file identifier.
-     * @param data the data to check
-     * @returns true if the data is a KTX2 file or false otherwise
-     */
-    public static IsValid(data: ArrayBufferView): boolean {
-        if (data.byteLength >= 12)
-        {
-            // '«', 'K', 'T', 'X', ' ', '2', '0', '»', '\r', '\n', '\x1A', '\n'
-            const 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;
-    }
-}