diff --git a/packages/loader/src/ktx2/KTX2Loader.ts b/packages/loader/src/ktx2/KTX2Loader.ts index ccb2d8a23c..9b890af62a 100644 --- a/packages/loader/src/ktx2/KTX2Loader.ts +++ b/packages/loader/src/ktx2/KTX2Loader.ts @@ -22,8 +22,9 @@ import { TranscodeResult } from "./transcoder/AbstractTranscoder"; import { BinomialLLCTranscoder } from "./transcoder/BinomialLLCTranscoder"; import { KhronosTranscoder } from "./transcoder/KhronosTranscoder"; -@resourceLoader(AssetType.KTX, ["ktx2"]) +@resourceLoader(AssetType.KTX2, ["ktx2"]) export class KTX2Loader extends Loader { + private static _isBinomialInit: boolean = false; private static _binomialLLCTranscoder: BinomialLLCTranscoder; private static _khronosTranscoder: KhronosTranscoder; private static _supportedMap = { @@ -47,12 +48,11 @@ export class KTX2Loader extends Loader { private static _decideTargetFormat( engine: Engine, ktx2Container: KTX2Container, - formatPriorities?: KTX2TargetFormat[] + priorityFormats?: KTX2TargetFormat[] ): KTX2TargetFormat { - // @ts-ignore - const renderer = engine._hardwareRenderer as WebGLRenderer; + const renderer = (engine as any)._hardwareRenderer; - const targetFormat = this._detectSupportedFormat(renderer, formatPriorities) as KTX2TargetFormat; + const targetFormat = this._detectSupportedFormat(renderer, priorityFormats) as KTX2TargetFormat; if ( targetFormat === KTX2TargetFormat.PVRTC && @@ -68,13 +68,12 @@ export class KTX2Loader extends Loader { Logger.warn("Can't support any compressed texture, downgrade to RGBA8"); return KTX2TargetFormat.R8G8B8A8; } - // TODO support bc7: https://github.com/galacean/engine/issues/1371 return targetFormat; } private static _detectSupportedFormat( renderer: any, - formatPriorities: KTX2TargetFormat[] = [ + priorityFormats: KTX2TargetFormat[] = [ KTX2TargetFormat.ASTC, KTX2TargetFormat.ETC, KTX2TargetFormat.BC7, @@ -82,11 +81,11 @@ export class KTX2Loader extends Loader { KTX2TargetFormat.PVRTC ] ): KTX2TargetFormat | null { - for (let i = 0; i < formatPriorities.length; i++) { - const capabilities = this._supportedMap[formatPriorities[i]]; + for (let i = 0; i < priorityFormats.length; i++) { + const capabilities = this._supportedMap[priorityFormats[i]]; for (let j = 0; j < capabilities.length; j++) { if (renderer.canIUse(capabilities[j])) { - return formatPriorities[i]; + return priorityFormats[i]; } } } @@ -94,6 +93,7 @@ export class KTX2Loader extends Loader { } private static _getBinomialLLCTranscoder(workerCount: number = 4) { + KTX2Loader._isBinomialInit = true; return (this._binomialLLCTranscoder ??= new BinomialLLCTranscoder(workerCount)); } @@ -104,10 +104,7 @@ export class KTX2Loader extends Loader { override initialize(engine: Engine, configuration: EngineConfiguration): Promise { if (configuration.ktx2Loader) { const options = configuration.ktx2Loader; - if ( - // @ts-ignore - KTX2Loader._detectSupportedFormat(engine._hardwareRenderer, options.formatPriorities) === KTX2TargetFormat.ASTC - ) { + if (this._isKhronosSupported(options.priorityFormats, engine)) { return KTX2Loader._getKhronosTranscoder(options.workerCount).init(); } else { return KTX2Loader._getBinomialLLCTranscoder(options.workerCount).init(); @@ -124,15 +121,15 @@ export class KTX2Loader extends Loader { ): AssetPromise { return this.request(item.url!, { type: "arraybuffer" }).then((buffer) => { const ktx2Container = new KTX2Container(buffer); - const formatPriorities = item.params?.formatPriorities; + const formatPriorities = item.params?.priorityFormats; const targetFormat = KTX2Loader._decideTargetFormat(resourceManager.engine, ktx2Container, formatPriorities); let transcodeResultPromise: Promise; - if (targetFormat === KTX2TargetFormat.ASTC && ktx2Container.isUASTC) { - const khronosWorker = KTX2Loader._getKhronosTranscoder(); - transcodeResultPromise = khronosWorker.init().then(() => khronosWorker.transcode(ktx2Container)); - } else { + if (KTX2Loader._isBinomialInit || !KhronosTranscoder.transcoderMap[targetFormat] || !ktx2Container.isUASTC) { const binomialLLCWorker = KTX2Loader._getBinomialLLCTranscoder(); transcodeResultPromise = binomialLLCWorker.init().then(() => binomialLLCWorker.transcode(buffer, targetFormat)); + } else { + const khronosWorker = KTX2Loader._getKhronosTranscoder(); + transcodeResultPromise = khronosWorker.init().then(() => khronosWorker.transcode(ktx2Container)); } return transcodeResultPromise.then((result) => { const { width, height, faces } = result; @@ -168,7 +165,7 @@ export class KTX2Loader extends Loader { }); } - private _getEngineTextureFormat(basisFormat: KTX2TargetFormat, transcodeResult: TranscodeResult) { + private _getEngineTextureFormat(basisFormat: KTX2TargetFormat, transcodeResult: TranscodeResult): TextureFormat { const { hasAlpha } = transcodeResult; switch (basisFormat) { case KTX2TargetFormat.ASTC: @@ -178,21 +175,47 @@ export class KTX2Loader extends Loader { case KTX2TargetFormat.BC7: return TextureFormat.BC7; case KTX2TargetFormat.BC1_BC3: - return hasAlpha ? TextureFormat.BC1 : TextureFormat.BC3; + return hasAlpha ? TextureFormat.BC3 : TextureFormat.BC1; case KTX2TargetFormat.PVRTC: return hasAlpha ? TextureFormat.PVRTC_RGBA4 : TextureFormat.PVRTC_RGB4; case KTX2TargetFormat.R8G8B8A8: return TextureFormat.R8G8B8A8; } } + + private _isKhronosSupported( + priorityFormats: KTX2TargetFormat[] | KTX2TargetFormat[][] = [ + KTX2TargetFormat.ASTC, + KTX2TargetFormat.ETC, + KTX2TargetFormat.BC7, + KTX2TargetFormat.BC1_BC3, + KTX2TargetFormat.PVRTC, + KTX2TargetFormat.R8G8B8A8 + ], + engine: any + ): boolean { + const supportedList = new Array(); + if (Array.isArray(priorityFormats[0])) { + for (let i = 0; i < priorityFormats.length; i++) { + supportedList.push( + KTX2Loader._detectSupportedFormat(engine._hardwareRenderer, priorityFormats[i]) + ); + } + } else { + supportedList.push( + KTX2Loader._detectSupportedFormat(engine._hardwareRenderer, priorityFormats) + ); + } + return supportedList.every((format) => KhronosTranscoder.transcoderMap[format]); + } } /** * KTX2 loader params interface. */ export interface KTX2Params { - /** Transcoder Format priorities, default is ASTC/ETC/DXT/PVRTC/RGBA8. */ - formatPriorities: KTX2TargetFormat[]; + /** Priority transcoding format queue, default is ASTC/ETC/DXT/PVRTC/RGBA8. */ + priorityFormats: KTX2TargetFormat[]; } declare module "@galacean/engine-core" { @@ -201,8 +224,8 @@ declare module "@galacean/engine-core" { ktx2Loader?: { /** Worker count for transcoder, default is 4. */ workerCount?: number; - /** Transcoder Format priorities, default is ASTC/ETC/DXT/PVRTC/RGBA8. */ - formatPriorities?: KTX2TargetFormat[]; + /** Pre-initialization according to the priority transcoding format queue, default is ASTC/ETC/DXT/PVRTC/RGBA8. */ + priorityFormats?: KTX2TargetFormat[] | KTX2TargetFormat[][]; }; } } diff --git a/packages/loader/src/ktx2/WorkerPool.ts b/packages/loader/src/ktx2/WorkerPool.ts index 9f08ee6512..09a42afba9 100644 --- a/packages/loader/src/ktx2/WorkerPool.ts +++ b/packages/loader/src/ktx2/WorkerPool.ts @@ -30,21 +30,23 @@ export class WorkerPool { /** * Post message to worker. - * @param message - message which posted to worker - * @returns return a promise of message + * @param message - Message which posted to worker + * @returns Return a promise of message */ postMessage(message: T): Promise { return new Promise((resolve, reject) => { const workerId = this._getIdleWorkerId(); if (workerId !== -1) { + this._workerStatus |= 1 << workerId; const workerItems = this._workerItems; - Promise.resolve(workerItems[workerId] ?? this._initWorker(workerId)).then(() => { - this._workerStatus |= 1 << workerId; - const workerItem = workerItems[workerId]; - workerItem.resolve = resolve; - workerItem.reject = reject; - workerItem.worker.postMessage(message); - }); + Promise.resolve(workerItems[workerId] ?? this._initWorker(workerId)) + .then(() => { + const workerItem = workerItems[workerId]; + workerItem.resolve = resolve; + workerItem.reject = reject; + workerItem.worker.postMessage(message); + }) + .catch(reject); } else { this._taskQueue.push({ resolve, reject, message }); } @@ -70,7 +72,6 @@ export class WorkerPool { private _initWorker(workerId: number): Promise { return Promise.resolve(this._workerCreator()).then((worker) => { worker.addEventListener("message", this._onMessage.bind(this, workerId)); - worker.addEventListener("error", this._onError.bind(this, workerId)); this._workerItems[workerId] = { worker, resolve: null, reject: null }; return worker; }); @@ -83,13 +84,14 @@ export class WorkerPool { return -1; } - private _onError(workerId, e: ErrorEvent) { - this._workerItems[workerId].reject(e); - this._nextTask(workerId); - } - - private _onMessage(workerId: number, msg: U) { - this._workerItems[workerId].resolve(msg); + private _onMessage(workerId: number, msg: MessageEvent) { + // onerror of web worker can't catch error in promise + const error = (msg.data as ErrorMessageData).error; + if (error) { + this._workerItems[workerId].reject(error); + } else { + this._workerItems[workerId].resolve(msg.data); + } this._nextTask(workerId); } @@ -106,6 +108,10 @@ export class WorkerPool { } } +interface ErrorMessageData { + error: unknown; +} + interface WorkerItem { worker: Worker; resolve: (item: U | PromiseLike) => void; diff --git a/packages/loader/src/ktx2/transcoder/BinomialLLCTranscoder.ts b/packages/loader/src/ktx2/transcoder/BinomialLLCTranscoder.ts index b97f51ec05..a519b7fcd3 100644 --- a/packages/loader/src/ktx2/transcoder/BinomialLLCTranscoder.ts +++ b/packages/loader/src/ktx2/transcoder/BinomialLLCTranscoder.ts @@ -31,12 +31,10 @@ export class BinomialLLCTranscoder extends AbstractTranscoder { } transcode(buffer: ArrayBuffer, format: KTX2TargetFormat): Promise { - return this._transcodeWorkerPool - .postMessage({ - buffer, - format, - type: "transcode" - }) - .then((ev) => ev.data); + return this._transcodeWorkerPool.postMessage({ + buffer, + format, + type: "transcode" + }); } } diff --git a/packages/loader/src/ktx2/transcoder/KhronosTranscoder.ts b/packages/loader/src/ktx2/transcoder/KhronosTranscoder.ts index e680312157..f3dc7666b4 100644 --- a/packages/loader/src/ktx2/transcoder/KhronosTranscoder.ts +++ b/packages/loader/src/ktx2/transcoder/KhronosTranscoder.ts @@ -74,8 +74,8 @@ export class KhronosTranscoder extends AbstractTranscoder { messageData[faceIndex] = mipmapData; } - return this._transcodeWorkerPool.postMessage(postMessageData).then((message) => { - decodedData.faces = message.data; + return this._transcodeWorkerPool.postMessage(postMessageData).then((data) => { + decodedData.faces = data; decodedData.hasAlpha = true; return decodedData; });