From 2a7f67feb35ab66ff573ccfca6c9ac41b3417fa4 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Wed, 10 Sep 2025 00:10:42 -0700 Subject: [PATCH] KTX2Loader: Fix ETC1S/UASTC prioritization Due to a historical accident, the list of formats for ETC1S was sorted using UASTC priority. This change fixes that. However, this exposes a problem that already exists for UASTC to ETC1S as well: on Linux, Mesa drivers for various Intel and AMD GPUs expose support for ETC2 and ASTC extensions even if the hardware does not support them, as part of GLES compatibility. When a texture with an emulated format is uploaded, the driver runs a very expensive CPU side decompression; this runs on the main thread and causes performance and memory issues. When using Chrome based browsers, ANGLE filters out ASTC and ETC extensions for us; we now detect Gecko based browsers like Firefox that don't use ANGLE and do this filtering ourselves. In principle, it is possible for GPUs to support all formats - notably, Safari exposes all formats on macOS when using Apple Silicon hardware, as it genuinely supports all possible formats. In this case we still should prefer native (ASTC/ETC2) format targets as they are faster to transcode to. A corner case is a combination of Firefox / Asahi Linux on Apple Silicon hardware; in the future it might be possible to detect the unmasked vendor to disambiguate, but even that combination will simply use BC7 for UASTC or dual-slice ETC1S which is probably reasonable. --- examples/jsm/loaders/KTX2Loader.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/examples/jsm/loaders/KTX2Loader.js b/examples/jsm/loaders/KTX2Loader.js index e4c4e5ce17894d..3a6c2db99d05de 100644 --- a/examples/jsm/loaders/KTX2Loader.js +++ b/examples/jsm/loaders/KTX2Loader.js @@ -189,7 +189,7 @@ class KTX2Loader extends Loader { * Async version of {@link KTX2Loader#detectSupport}. * * @async - * @param {WebGPURenderer|WebGLRenderer} renderer - The renderer. + * @param {WebGPURenderer} renderer - The renderer. * @return {Promise} A Promise that resolves when the support has been detected. */ async detectSupportAsync( renderer ) { @@ -242,6 +242,21 @@ class KTX2Loader extends Loader { pvrtcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_pvrtc' ) || renderer.extensions.has( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ) }; + + if ( typeof navigator !== 'undefined' && + navigator.platform.indexOf( 'Linux' ) >= 0 && navigator.userAgent.indexOf( 'Firefox' ) >= 0 && + this.workerConfig.astcSupported && this.workerConfig.etc2Supported && + this.workerConfig.bptcSupported && this.workerConfig.dxtSupported ) { + + // On Linux, Mesa drivers for AMD and Intel GPUs expose ETC2 and ASTC even though the hardware doesn't support these. + // Using these extensions will result in expensive software decompression on the main thread inside the driver, causing performance issues. + // When using ANGLE (e.g. via Chrome), these extensions are not exposed except for some specific Intel GPU models - however, Firefox doesn't perform this filtering. + // Since a granular filter is a little too fragile and we can transcode into other GPU formats, disable formats that are likely to be emulated. + + this.workerConfig.astcSupported = false; + this.workerConfig.etc2Supported = false; + + } } @@ -841,13 +856,9 @@ KTX2Loader.BasisWorker = function () { ]; const OPTIONS = { - // TODO: For ETC1S we intentionally sort by _UASTC_ priority, preserving - // a historical accident shown to avoid performance pitfalls for Linux with - // Firefox & AMD GPU (RadeonSI). Further work needed. - // See https://github.com/mrdoob/three.js/pull/29730. [ BasisFormat.ETC1S ]: FORMAT_OPTIONS .filter( ( opt ) => opt.basisFormat.includes( BasisFormat.ETC1S ) ) - .sort( ( a, b ) => a.priorityUASTC - b.priorityUASTC ), + .sort( ( a, b ) => a.priorityETC1S - b.priorityETC1S ), [ BasisFormat.UASTC ]: FORMAT_OPTIONS .filter( ( opt ) => opt.basisFormat.includes( BasisFormat.UASTC ) )