Browse Source

Add new UASTC to RGBA universal transcoder support

Popov72 4 years ago
parent
commit
a226efa37a

+ 2 - 0
ktx2Decoder/src/Transcoders/index.ts

@@ -1,4 +1,6 @@
 export * from "./liteTranscoder";
 export * from "./liteTranscoder_UASTC_ASTC";
 export * from "./liteTranscoder_UASTC_BC7";
+export * from "./liteTranscoder_UASTC_RGBA_UNORM";
+export * from "./liteTranscoder_UASTC_RGBA_SRGB";
 export * from "./mscTranscoder";

+ 22 - 6
ktx2Decoder/src/Transcoders/liteTranscoder.ts

@@ -10,6 +10,7 @@ export class LiteTranscoder extends Transcoder {
     private _modulePath: string;
     private _modulePromise: Promise<{ module: any }>;
     private _memoryManager: WASMMemoryManager;
+    protected _transcodeInPlace: boolean;
 
     protected _loadModule(): Promise<{ module: any }> {
         if (this._modulePromise) {
@@ -35,6 +36,10 @@ export class LiteTranscoder extends Transcoder {
         this._modulePath = modulePath;
     }
 
+    public initialize(): void {
+        this._transcodeInPlace = true;
+    }
+
     public needMemoryManager(): boolean {
         return true;
     }
@@ -46,16 +51,27 @@ export class LiteTranscoder extends Transcoder {
     public transcode(src: sourceTextureFormat, dst: transcodeTarget, level: number, width: number, height: number, uncompressedByteLength: number, ktx2Reader: KTX2FileReader, imageDesc: IKTX2_ImageDesc | null, encodedData: Uint8Array): Promise<Uint8Array | null> {
         return this._loadModule().then((moduleWrapper: any) => {
             const transcoder: any = moduleWrapper.module;
+            const [textureView, uncompressedTextureView, nBlocks] = this._prepareTranscoding(width, height, uncompressedByteLength, encodedData);
 
-            const nBlocks = ((width + 3) >> 2) * ((height + 3) >> 2);
+            return transcoder.transcode(nBlocks) === 0 ? (this._transcodeInPlace ? textureView.slice() : uncompressedTextureView!.slice()) : null;
+        });
+    }
 
-            const texMemoryPages = ((nBlocks * 16 + 65535) >> 16) + 1;
+    protected _prepareTranscoding(width: number, height: number, uncompressedByteLength: number, encodedData: Uint8Array, forceRGBA = false): [Uint8Array, Uint8Array | null, number] {
+        const nBlocks = ((width + 3) >> 2) * ((height + 3) >> 2);
 
-            const textureView = this.memoryManager.getMemoryView(texMemoryPages, 65536, nBlocks * 16);
+        if (forceRGBA) {
+            uncompressedByteLength = width * ((height + 3) >> 2) * 4 * 4;
+        }
 
-            textureView.set(encodedData);
+        const texMemoryPages = ((nBlocks * 16 + 65535 + (this._transcodeInPlace ? 0 : uncompressedByteLength)) >> 16) + 1;
 
-            return transcoder.transcode(nBlocks) === 0 ? textureView.slice() : null;
-        });
+        const textureView = this.memoryManager.getMemoryView(texMemoryPages, 65536, nBlocks * 16);
+
+        const uncompressedTextureView = this._transcodeInPlace ? null : new Uint8Array(this._memoryManager.wasmMemory.buffer, 65536 + nBlocks * 16, forceRGBA ? width * height * 4 : uncompressedByteLength);
+
+        textureView.set(encodedData);
+
+        return [textureView, uncompressedTextureView, nBlocks]
     }
 }

+ 2 - 1
ktx2Decoder/src/Transcoders/liteTranscoder_UASTC_ASTC.ts

@@ -10,7 +10,7 @@ export class LiteTranscoder_UASTC_ASTC extends LiteTranscoder {
      */
     public static WasmModuleURL = "https://preview.babylonjs.com/ktx2Transcoders/uastc_astc.wasm";
 
-    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget): boolean {
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget, isInGammaSpace: boolean): boolean {
         return src === sourceTextureFormat.UASTC4x4 && dst === transcodeTarget.ASTC_4x4_RGBA;
     }
 
@@ -21,6 +21,7 @@ export class LiteTranscoder_UASTC_ASTC extends LiteTranscoder {
     }
 
     public initialize(): void {
+        super.initialize();
         this.setModulePath(LiteTranscoder_UASTC_ASTC.WasmModuleURL);
     }
 }

+ 2 - 1
ktx2Decoder/src/Transcoders/liteTranscoder_UASTC_BC7.ts

@@ -10,7 +10,7 @@ export class LiteTranscoder_UASTC_BC7 extends LiteTranscoder {
      */
     public static WasmModuleURL = "https://preview.babylonjs.com/ktx2Transcoders/uastc_bc7.wasm";
 
-    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget): boolean {
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget, isInGammaSpace: boolean): boolean {
         return src === sourceTextureFormat.UASTC4x4 && dst === transcodeTarget.BC7_RGBA;
     }
 
@@ -21,6 +21,7 @@ export class LiteTranscoder_UASTC_BC7 extends LiteTranscoder {
     }
 
     public initialize(): void {
+        super.initialize();
         this.setModulePath(LiteTranscoder_UASTC_BC7.WasmModuleURL);
     }
 }

+ 38 - 0
ktx2Decoder/src/Transcoders/liteTranscoder_UASTC_RGBA_SRGB.ts

@@ -0,0 +1,38 @@
+import { sourceTextureFormat, transcodeTarget } from '../transcoder';
+import { LiteTranscoder } from './liteTranscoder';
+import { KTX2FileReader, IKTX2_ImageDesc } from '../ktx2FileReader';
+
+/**
+ * @hidden
+ */
+export class LiteTranscoder_UASTC_RGBA_SRGB extends LiteTranscoder {
+    /**
+     * URL to use when loading the wasm module for the transcoder (srgb)
+     */
+    public static WasmModuleURL = "https://preview.babylonjs.com/ktx2Transcoders/uastc_rgba32_srgb.wasm";
+
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget, isInGammaSpace: boolean): boolean {
+        return src === sourceTextureFormat.UASTC4x4 && dst === transcodeTarget.RGBA32 && isInGammaSpace;
+    }
+
+    public static Name = "UniversalTranscoder_UASTC_RGBA_SRGB";
+
+    public getName(): string {
+        return LiteTranscoder_UASTC_RGBA_SRGB.Name;
+    }
+
+    public initialize(): void {
+        super.initialize();
+        this._transcodeInPlace = false;
+        this.setModulePath(LiteTranscoder_UASTC_RGBA_SRGB.WasmModuleURL);
+    }
+
+    public transcode(src: sourceTextureFormat, dst: transcodeTarget, level: number, width: number, height: number, uncompressedByteLength: number, ktx2Reader: KTX2FileReader, imageDesc: IKTX2_ImageDesc | null, encodedData: Uint8Array): Promise<Uint8Array | null> {
+        return this._loadModule().then((moduleWrapper: any) => {
+            const transcoder: any = moduleWrapper.module;
+            const [, uncompressedTextureView, ] = this._prepareTranscoding(width, height, uncompressedByteLength, encodedData, true);
+
+            return transcoder.decodeRGBA32(width, height) === 0 ? uncompressedTextureView!.slice() : null;
+        });
+    }
+}

+ 38 - 0
ktx2Decoder/src/Transcoders/liteTranscoder_UASTC_RGBA_UNORM.ts

@@ -0,0 +1,38 @@
+import { sourceTextureFormat, transcodeTarget } from '../transcoder';
+import { LiteTranscoder } from './liteTranscoder';
+import { KTX2FileReader, IKTX2_ImageDesc } from '../ktx2FileReader';
+
+/**
+ * @hidden
+ */
+export class LiteTranscoder_UASTC_RGBA_UNORM extends LiteTranscoder {
+    /**
+     * URL to use when loading the wasm module for the transcoder (unorm)
+     */
+    public static WasmModuleURL = "https://preview.babylonjs.com/ktx2Transcoders/uastc_rgba32_unorm.wasm";
+
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget, isInGammaSpace: boolean): boolean {
+        return src === sourceTextureFormat.UASTC4x4 && dst === transcodeTarget.RGBA32 && !isInGammaSpace;
+    }
+
+    public static Name = "UniversalTranscoder_UASTC_RGBA_UNORM";
+
+    public getName(): string {
+        return LiteTranscoder_UASTC_RGBA_UNORM.Name;
+    }
+
+    public initialize(): void {
+        super.initialize();
+        this._transcodeInPlace = false;
+        this.setModulePath(LiteTranscoder_UASTC_RGBA_UNORM.WasmModuleURL);
+    }
+
+    public transcode(src: sourceTextureFormat, dst: transcodeTarget, level: number, width: number, height: number, uncompressedByteLength: number, ktx2Reader: KTX2FileReader, imageDesc: IKTX2_ImageDesc | null, encodedData: Uint8Array): Promise<Uint8Array | null> {
+        return this._loadModule().then((moduleWrapper: any) => {
+            const transcoder: any = moduleWrapper.module;
+            const [, uncompressedTextureView, ] = this._prepareTranscoding(width, height, uncompressedByteLength, encodedData, true);
+
+            return transcoder.decodeRGBA32(width, height) === 0 ? uncompressedTextureView!.slice() : null;
+        });
+    }
+}

+ 1 - 1
ktx2Decoder/src/Transcoders/mscTranscoder.ts

@@ -51,7 +51,7 @@ export class MSCTranscoder extends Transcoder {
         return this._mscBasisTranscoderPromise;
     }
 
-    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget): boolean {
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget, isInGammaSpace: boolean): boolean {
         return true;
     }
 

+ 7 - 1
ktx2Decoder/src/ktx2Decoder.ts

@@ -12,6 +12,8 @@ import { KTX2FileReader, SupercompressionScheme, IKTX2_ImageDesc } from './ktx2F
 import { TranscoderManager } from './transcoderManager';
 import { LiteTranscoder_UASTC_ASTC } from './Transcoders/liteTranscoder_UASTC_ASTC';
 import { LiteTranscoder_UASTC_BC7 } from './Transcoders/liteTranscoder_UASTC_BC7';
+import { LiteTranscoder_UASTC_RGBA_UNORM } from './Transcoders/liteTranscoder_UASTC_RGBA_UNORM';
+import { LiteTranscoder_UASTC_RGBA_SRGB } from './Transcoders/liteTranscoder_UASTC_RGBA_SRGB';
 import { MSCTranscoder } from './Transcoders/mscTranscoder';
 import { transcodeTarget, sourceTextureFormat } from './transcoder';
 import { ZSTDDecoder } from './zstddec';
@@ -63,6 +65,8 @@ export interface IKTX2DecoderOptions {
      * list of transcoders to bypass when looking for a suitable transcoder. The available transcoders are:
      *      UniversalTranscoder_UASTC_ASTC
      *      UniversalTranscoder_UASTC_BC7
+     *      UniversalTranscoder_UASTC_RGBA_UNORM
+     *      UniversalTranscoder_UASTC_RGBA_SRGB
      *      MSCTranscoder
     */
     bypassTranscoders?: string[];
@@ -153,7 +157,7 @@ export class KTX2Decoder {
             roundToMultiple4 = false;
         }
 
-        const transcoder = this._transcoderMgr.findTranscoder(srcTexFormat, targetFormat, options?.bypassTranscoders);
+        const transcoder = this._transcoderMgr.findTranscoder(srcTexFormat, targetFormat, kfr.isInGammaSpace, options?.bypassTranscoders);
 
         if (transcoder === null) {
             throw new Error(`no transcoder found to transcode source texture format "${sourceTextureFormat[srcTexFormat]}" to format "${transcodeTarget[targetFormat]}"`);
@@ -243,4 +247,6 @@ export class KTX2Decoder {
 // Put in the order you want the transcoders to be used in priority
 TranscoderManager.RegisterTranscoder(LiteTranscoder_UASTC_ASTC);
 TranscoderManager.RegisterTranscoder(LiteTranscoder_UASTC_BC7);
+TranscoderManager.RegisterTranscoder(LiteTranscoder_UASTC_RGBA_UNORM);
+TranscoderManager.RegisterTranscoder(LiteTranscoder_UASTC_RGBA_SRGB);
 TranscoderManager.RegisterTranscoder(MSCTranscoder); // catch all transcoder - will throw an error if the format can't be transcoded

+ 1 - 1
ktx2Decoder/src/transcoder.ts

@@ -29,7 +29,7 @@ export enum transcodeTarget {
  */
 export class Transcoder {
 
-    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget): boolean {
+    public static CanTranscode(src: sourceTextureFormat, dst: transcodeTarget, isInGammaSpace: boolean): boolean {
         return false;
     }
 

+ 2 - 2
ktx2Decoder/src/transcoderManager.ts

@@ -16,13 +16,13 @@ export class TranscoderManager {
 
     private _wasmMemoryManager: WASMMemoryManager;
 
-    public findTranscoder(src: sourceTextureFormat, dst: transcodeTarget, bypass?: string[]): Transcoder | null {
+    public findTranscoder(src: sourceTextureFormat, dst: transcodeTarget, isInGammaSpace: boolean, bypass?: string[]): Transcoder | null {
         let transcoder: Transcoder | null = null;
 
         const key = sourceTextureFormat[src] + "_" + transcodeTarget[dst];
 
         for (let i = 0; i < TranscoderManager._Transcoders.length; ++i) {
-            if (TranscoderManager._Transcoders[i].CanTranscode(src, dst) && (!bypass || bypass.indexOf(TranscoderManager._Transcoders[i].Name) < 0)) {
+            if (TranscoderManager._Transcoders[i].CanTranscode(src, dst, isInGammaSpace) && (!bypass || bypass.indexOf(TranscoderManager._Transcoders[i].Name) < 0)) {
                 transcoder = this._getExistingTranscoder(key, TranscoderManager._Transcoders[i].Name);
                 if (!transcoder) {
                     transcoder = new TranscoderManager._Transcoders[i]();

+ 20 - 3
src/Misc/khronosTextureContainer2.ts

@@ -21,6 +21,8 @@ export class KhronosTextureContainer2 {
      *     URLConfig.jsDecoderModule
      *     URLConfig.wasmUASTCToASTC
      *     URLConfig.wasmUASTCToBC7
+     *     URLConfig.wasmUASTCToRGBA_UNORM
+     *     URLConfig.wasmUASTCToRGBA_SRGB
      *     URLConfig.jsMSCTranscoder
      *     URLConfig.wasmMSCTranscoder
      * You can see their default values in this PG: https://playground.babylonjs.com/#EIJH8L#9
@@ -29,8 +31,10 @@ export class KhronosTextureContainer2 {
         jsDecoderModule: "https://preview.babylonjs.com/babylon.ktx2Decoder.js",
         wasmUASTCToASTC: null,
         wasmUASTCToBC7: null,
+        wasmUASTCToRGBA_UNORM: null,
+        wasmUASTCToRGBA_SRGB: null,
         jsMSCTranscoder: null,
-        wasmMSCTranscoder: null
+        wasmMSCTranscoder: null,
     };
 
     /**
@@ -140,7 +144,7 @@ export class KhronosTextureContainer2 {
                                     reject({ message: message.data.msg });
                                 } else {
                                     try {
-                                        this._createTexture(message.data.decodedData, internalTexture);
+                                        this._createTexture(message.data.decodedData, internalTexture, options);
                                         resolve();
                                     } catch (err) {
                                         reject({ message: err });
@@ -187,9 +191,16 @@ export class KhronosTextureContainer2 {
         delete KhronosTextureContainer2._WorkerPoolPromise;
     }
 
-    protected _createTexture(data: any /* IDecodedData */, internalTexture: InternalTexture) {
+    protected _createTexture(data: any /* IDecodedData */, internalTexture: InternalTexture, options?: any) {
         this._engine._bindTextureDirectly(this._engine._gl.TEXTURE_2D, internalTexture);
 
+        if (options) {
+            // return back some information about the decoded data
+            options.transcodedFormat = data.transcodedFormat;
+            options.isInGammaSpace = data.isInGammaSpace;
+            options.transcoderName = data.transcoderName;
+        }
+
         if (data.transcodedFormat === 0x8058 /* RGBA8 */) {
             internalTexture.type = Constants.TEXTURETYPE_UNSIGNED_BYTE;
             internalTexture.format = Constants.TEXTUREFORMAT_RGBA;
@@ -267,6 +278,12 @@ function workerFunc(): void {
                 if (urls.wasmUASTCToBC7 !== null) {
                     KTX2DECODER.LiteTranscoder_UASTC_BC7.WasmModuleURL = urls.wasmUASTCToBC7;
                 }
+                if (urls.wasmUASTCToRGBA_UNORM !== null) {
+                    KTX2DECODER.LiteTranscoder_UASTC_RGBA_UNORM.WasmModuleURL = urls.wasmUASTCToRGBA_UNORM;
+                }
+                if (urls.wasmUASTCToRGBA_SRGB !== null) {
+                    KTX2DECODER.LiteTranscoder_UASTC_RGBA_SRGB.WasmModuleURL = urls.wasmUASTCToRGBA_SRGB;
+                }
                 if (urls.jsMSCTranscoder !== null) {
                     KTX2DECODER.MSCTranscoder.JSModuleURL = urls.jsMSCTranscoder;
                 }