From 306604e580f4fd7ed18d18270e37b91e01d7d9c6 Mon Sep 17 00:00:00 2001 From: Ed Preston Date: Thu, 5 Feb 2026 23:36:16 +1100 Subject: [PATCH 1/6] Update KTX2Loader.js for webgl2, bc4 and bc5 are provided by "rgtc". This is GL_COMPRESSED_RED_RGTC1 and GL_COMPRESSED_SIGNED_RG_RGTC2. for webgpu, bc4 and bc5 and provided by "bptc". This is bc4-r-unorm and bc5-rg-unorm --- examples/jsm/loaders/KTX2Loader.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/jsm/loaders/KTX2Loader.js b/examples/jsm/loaders/KTX2Loader.js index b2ad541a296a31..e064ef858195c4 100644 --- a/examples/jsm/loaders/KTX2Loader.js +++ b/examples/jsm/loaders/KTX2Loader.js @@ -232,6 +232,7 @@ class KTX2Loader extends Loader { etc1Supported: renderer.hasFeature( 'texture-compression-etc1' ), etc2Supported: renderer.hasFeature( 'texture-compression-etc2' ), dxtSupported: renderer.hasFeature( 'texture-compression-s3tc' ), + rgtcSupported: false, // 1 and 2 channel textures provided by bc (bptc) bptcSupported: renderer.hasFeature( 'texture-compression-bc' ), pvrtcSupported: renderer.hasFeature( 'texture-compression-pvrtc' ) }; @@ -245,6 +246,7 @@ class KTX2Loader extends Loader { etc1Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc1' ), etc2Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc' ), dxtSupported: renderer.extensions.has( 'WEBGL_compressed_texture_s3tc' ), + rgtcSupported: renderer.extensions.has( 'EXT_texture_compression_rgtc' ), bptcSupported: renderer.extensions.has( 'EXT_texture_compression_bptc' ), pvrtcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_pvrtc' ) || renderer.extensions.has( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ) From 3f71146c13132a15bd6d24edafac280891ebbfad Mon Sep 17 00:00:00 2001 From: Ed Preston Date: Thu, 5 Feb 2026 23:44:37 +1100 Subject: [PATCH 2/6] Create fail_load.ktx2 add missing texture from original source: https://github.com/donmccurdy/KTX2-Samples --- examples/textures/ktx2/fail_load.ktx2 | Bin 0 -> 8888 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/textures/ktx2/fail_load.ktx2 diff --git a/examples/textures/ktx2/fail_load.ktx2 b/examples/textures/ktx2/fail_load.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..92b092d27a3edefe0b40b82ca0e8b8751a715c08 GIT binary patch literal 8888 zcmeH|$xmff5Qi_V5$(jJm+Hp1G%;yGd+EZxTNl0PjkYX0x-f)j2a*>;3=rmdo`*0K zW+V{inS>>42^;?k%KIH|b&BJKeyzqpyeBD6Rh=4sHQdL<*WWx%eV()W{=<(Rei{Tp zR!E;j6khJ2dne+55ASCoeHkjBL^^x@_Fg1?c>R9))|LEK6iDws2p*m~@BTHCzrT78 z^e%|>k0QO^KSlbtBfZ{Wy}e!f z`}?J{vs0Rzo29X_QB*fRJ}v_T15#UCD(^+uJME)z#A1*C)fn!!kNL zDjgjiQd3joZ0qXkq@kg~#e#>3QB_st{E%B(TBN?dUfSB)q^qmT@l8!lGB`NseAS1X z#O7XFT9Wnkby;0qmF4ASXG>1vf<^I8O-(sW$n*2_t~T<6xx2etW@ctwjl?A%HMF+2 zx_5A6N1f2z+?;G~Zo2%lv$L)byo0s8yxd{KhZ=f%dK?DgO-@d_nB)Z`L`~fITSBiy zPvE(IQXBtPAN8~Eb(NKsQdU;xbks<_#33JgWNUBp5{sG9S8S-4 zp6u-GxY^^&Y;=JulTwLtpvQyMRJl+lu59Rdq)a?s9gYVwnUTnP5(o#7%I1n&U zGx)Z)wuD_IE;|Ia-Q8WcQ`AC#;_$Is^q&1C#>&cy!$qz5(|^84Z1@)0J@8`7HyqbL zojd*J`$oneVpjOEzr6ESQBmRQ^keY`(NktdpZOk{5%r)0A9FfCKX;h;HmQ@CItPto z=Yh@1$%*S9bD^K~fV;4;@YU{spFR+eURjJ94}8R99$=%kv$Hcd)8pe~DJm*@6_1*k zPdc5x77L6Tk9}c3!BtXH;(j+47Z+VGRDW@C;p)YPT;vBoJqCk~2Tv~`czQ-H5Inz& z{8mB8VC1(LJ($qpXUX;fJ+kWf{+Khw8#4XV-z6I-6Q2A^*uzs}9Ii~_B{tV*nMptF ze6iPAD{tkvc>cVQ*+*uGuYI?>8W$_kz4`p^YtJM+w_)d9Pn_N5<>g;~C(zmRPku*; z6PFjA{}v~c+sF7i`KEkvdOxSN@!za$d3-D;%lmIOekQ!1$NKnhR<^v4;jZ8VqdmH3 zcLksAwZ&w4zqi&W6Q25Svh&}pYG94AK#vRiqqZX7iaJLXDXB9He1c5w{MHy-~TwB@0Y238$KVCFOwqUTaa=-u^ePwy(SYt{-uA?t0r? is?*!PC4BO~H`#$?2a+8~b|Be-WCxNRNOs^p?ZBT|!&IRF literal 0 HcmV?d00001 From 16f7be399c89209bc956ba73c3b3b0ec9643d854 Mon Sep 17 00:00:00 2001 From: Ed Preston Date: Fri, 6 Feb 2026 00:12:05 +1100 Subject: [PATCH 3/6] Update webgl_loader_texture_ktx2.html update webgl2 example. use the support matrix given to worker threads to populate the "supported" field already expected by the example code. bc1 and bc3 are dxt on webgl, bptc on webgpu bc4 and bc5 are rgtc on webgl, bptc on webgpu --- examples/webgl_loader_texture_ktx2.html | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/webgl_loader_texture_ktx2.html b/examples/webgl_loader_texture_ktx2.html index 846c42e9ae865f..0f142f3d23c9c6 100644 --- a/examples/webgl_loader_texture_ktx2.html +++ b/examples/webgl_loader_texture_ktx2.html @@ -86,7 +86,18 @@ import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; - let canvas, renderer; + const canvas = document.getElementById( 'c' ); + + const renderer = new THREE.WebGLRenderer( { canvas, antialias: true } ); + renderer.setClearColor( 0xffffff, 1 ); + renderer.setPixelRatio( window.devicePixelRatio ); + + const loader = new KTX2Loader() + .setTranscoderPath( 'jsm/libs/basis/' ) + .setPath( 'textures/ktx2/' ) + .detectSupport( renderer ); + + const formats = loader.workerConfig; const scenes = []; @@ -110,14 +121,14 @@ + ' reducing memory cost. Requires native support on the device GPU: no single compressed' + ' format is supported on every device.', textures: [ - { path: '2d_astc4x4.ktx2' }, - { path: '2d_etc1.ktx2' }, - { path: '2d_etc2.ktx2' }, - { path: '2d_bc1.ktx2' }, - { path: '2d_bc3.ktx2' }, - { path: '2d_bc4.ktx2' }, - { path: '2d_bc5.ktx2' }, - { path: '2d_bc7.ktx2' }, + { path: '2d_astc4x4.ktx2', supported: formats[ 'astcSupported' ] }, + { path: '2d_etc1.ktx2', supported: formats[ 'etc1Supported' ] }, + { path: '2d_etc2.ktx2', supported: formats[ 'etc2Supported' ] }, + { path: '2d_bc1.ktx2', supported: formats[ 'dxtSupported' ] || formats[ 'bptcSupported' ] }, + { path: '2d_bc3.ktx2', supported: formats[ 'dxtSupported' ] || formats[ 'bptcSupported' ] }, + { path: '2d_bc4.ktx2', supported: formats[ 'rgtcSupported' ] || formats[ 'bptcSupported' ] }, + { path: '2d_bc5.ktx2', supported: formats[ 'rgtcSupported' ] || formats[ 'bptcSupported' ] }, + { path: '2d_bc7.ktx2', supported: formats[ 'bptcSupported' ] } ] }, @@ -138,17 +149,6 @@ async function init() { - canvas = document.getElementById( 'c' ); - - renderer = new THREE.WebGLRenderer( { canvas, antialias: true } ); - renderer.setClearColor( 0xffffff, 1 ); - renderer.setPixelRatio( window.devicePixelRatio ); - - const loader = new KTX2Loader() - .setTranscoderPath( 'jsm/libs/basis/' ) - .setPath( 'textures/ktx2/' ) - .detectSupport( renderer ); - const geometry = flipY( new THREE.PlaneGeometry( 1, 1 ) ); const content = document.getElementById( 'content' ); From b8fc5f99250086208934a32b363a0fe6a8884c49 Mon Sep 17 00:00:00 2001 From: Ed Preston Date: Fri, 6 Feb 2026 00:59:16 +1100 Subject: [PATCH 4/6] Update KTX2Loader.js clarify webgpu texture support matrix support --- examples/jsm/loaders/KTX2Loader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/jsm/loaders/KTX2Loader.js b/examples/jsm/loaders/KTX2Loader.js index e064ef858195c4..66a611a1727be1 100644 --- a/examples/jsm/loaders/KTX2Loader.js +++ b/examples/jsm/loaders/KTX2Loader.js @@ -229,9 +229,9 @@ class KTX2Loader extends Loader { this.workerConfig = { astcSupported: renderer.hasFeature( 'texture-compression-astc' ), astcHDRSupported: false, // https://github.com/gpuweb/gpuweb/issues/3856 - etc1Supported: renderer.hasFeature( 'texture-compression-etc1' ), + etc1Supported: false, // support provided by etc2 etc2Supported: renderer.hasFeature( 'texture-compression-etc2' ), - dxtSupported: renderer.hasFeature( 'texture-compression-s3tc' ), + dxtSupported: false, // rgb565 smooth and hard alpha provided by bc (bptc) rgtcSupported: false, // 1 and 2 channel textures provided by bc (bptc) bptcSupported: renderer.hasFeature( 'texture-compression-bc' ), pvrtcSupported: renderer.hasFeature( 'texture-compression-pvrtc' ) From 3507f40fc884057964d3d5107cb6158351eb0c64 Mon Sep 17 00:00:00 2001 From: Ed Preston Date: Fri, 6 Feb 2026 01:12:19 +1100 Subject: [PATCH 5/6] Update webgpu_loader_texture_ktx2.html update webgpu example. use the support matrix given to worker threads to populate the "supported" field already expected by the example code. some rearrangement required to have an initialized renderer to populate the data fields. ect1 and ect2 support are from the ect2 extension bc1-bc7 are from bptc on webgpu --- examples/webgpu_loader_texture_ktx2.html | 119 ++++++++++++----------- 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/examples/webgpu_loader_texture_ktx2.html b/examples/webgpu_loader_texture_ktx2.html index e4c8fc307a6c59..5cd292fe5893e5 100644 --- a/examples/webgpu_loader_texture_ktx2.html +++ b/examples/webgpu_loader_texture_ktx2.html @@ -88,69 +88,78 @@ import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; - let canvas, renderer; + const canvas = document.getElementById( 'c' ); - const scenes = []; + const renderer = new THREE.WebGPURenderer( { canvas, antialias: true, forceWebGL: false } ); + renderer.setClearColor( 0xffffff, 1 ); + renderer.setPixelRatio( window.devicePixelRatio ); - const sections = [ - { - title: 'Uncompressed', - description: 'Uncompressed formats (rgba8, rgba16, rgba32) load as THREE.DataTexture objects.' - + ' Lossless, easy to read/write, uncompressed on GPU, optionally compressed over the network.', - textures: [ - { path: '2d_rgba8.ktx2' }, - { path: '2d_rgba8_linear.ktx2' }, - { path: '2d_rgba16_linear.ktx2' }, - { path: '2d_rgba32_linear.ktx2' }, - { path: '2d_rgb9e5_linear.ktx2' }, - { path: '2d_r11g11b10_linear.ktx2' }, - ] - }, - { - title: 'Compressed', - description: 'Compressed formats (ASTC, BCn, ...) load as THREE.CompressedTexture objects,' - + ' reducing memory cost. Requires native support on the device GPU: no single compressed' - + ' format is supported on every device.', - textures: [ - { path: '2d_astc4x4.ktx2' }, - { path: '2d_etc1.ktx2' }, - { path: '2d_etc2.ktx2' }, - { path: '2d_bc1.ktx2' }, - { path: '2d_bc3.ktx2' }, - { path: '2d_bc4.ktx2' }, - { path: '2d_bc5.ktx2' }, - { path: '2d_bc7.ktx2' }, - ] - }, - - { - title: 'Universal', - description: 'Basis Universal textures are specialized intermediate formats supporting fast' - + ' runtime transcoding into other GPU texture compression formats. After transcoding,' - + ' universal textures can be used on any device at reduced memory cost.', - textures: [ - { path: '2d_etc1s.ktx2' }, - { path: '2d_uastc.ktx2' }, - ] - }, - ]; - - init(); + const loader = new KTX2Loader() + .setTranscoderPath( 'jsm/libs/basis/' ) + .setPath( 'textures/ktx2/' ); - async function init() { + const scenes = []; - canvas = document.getElementById( 'c' ); + let sections; - renderer = new THREE.WebGPURenderer( { canvas, antialias: true, forceWebGL: false } ); - renderer.setClearColor( 0xffffff, 1 ); - renderer.setPixelRatio( window.devicePixelRatio ); + initRenderer(); + async function initRenderer() { + await renderer.init(); - const loader = new KTX2Loader() - .setTranscoderPath( 'jsm/libs/basis/' ) - .setPath( 'textures/ktx2/' ) - .detectSupport( renderer ); + loader.detectSupport( renderer ); + + const formats = loader.workerConfig; + + sections = [ + { + title: 'Uncompressed', + description: 'Uncompressed formats (rgba8, rgba16, rgba32) load as THREE.DataTexture objects.' + + ' Lossless, easy to read/write, uncompressed on GPU, optionally compressed over the network.', + textures: [ + { path: '2d_rgba8.ktx2' }, + { path: '2d_rgba8_linear.ktx2' }, + { path: '2d_rgba16_linear.ktx2' }, + { path: '2d_rgba32_linear.ktx2' }, + { path: '2d_rgb9e5_linear.ktx2' }, + { path: '2d_r11g11b10_linear.ktx2' }, + ] + }, + { + title: 'Compressed', + description: 'Compressed formats (ASTC, BCn, ...) load as THREE.CompressedTexture objects,' + + ' reducing memory cost. Requires native support on the device GPU: no single compressed' + + ' format is supported on every device.', + textures: [ + { path: '2d_astc4x4.ktx2', supported: formats[ 'astcSupported' ] }, + { path: '2d_etc1.ktx2', supported: formats[ 'etc2Supported' ] }, + { path: '2d_etc2.ktx2', supported: formats[ 'etc2Supported' ] }, + { path: '2d_bc1.ktx2', supported: formats[ 'bptcSupported' ] }, + { path: '2d_bc3.ktx2', supported: formats[ 'bptcSupported' ] }, + { path: '2d_bc4.ktx2', supported: formats[ 'bptcSupported' ] }, + { path: '2d_bc5.ktx2', supported: formats[ 'bptcSupported' ] }, + { path: '2d_bc7.ktx2', supported: formats[ 'bptcSupported' ] } + ] + }, + + { + title: 'Universal', + description: 'Basis Universal textures are specialized intermediate formats supporting fast' + + ' runtime transcoding into other GPU texture compression formats. After transcoding,' + + ' universal textures can be used on any device at reduced memory cost.', + textures: [ + { path: '2d_etc1s.ktx2' }, + { path: '2d_uastc.ktx2' }, + ] + }, + ]; + + await init(); + + } + + async function init() { const geometry = flipY( new THREE.PlaneGeometry( 1, 1 ) ); From 3d7ea291779ab9ad78a3d675fd14cded3fd49ce6 Mon Sep 17 00:00:00 2001 From: Ed Preston Date: Sat, 7 Feb 2026 14:19:06 +1100 Subject: [PATCH 6/6] Update KTX2Loader.js trigger CI on PR --- examples/jsm/loaders/KTX2Loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jsm/loaders/KTX2Loader.js b/examples/jsm/loaders/KTX2Loader.js index 66a611a1727be1..15971e805d5ba1 100644 --- a/examples/jsm/loaders/KTX2Loader.js +++ b/examples/jsm/loaders/KTX2Loader.js @@ -229,7 +229,7 @@ class KTX2Loader extends Loader { this.workerConfig = { astcSupported: renderer.hasFeature( 'texture-compression-astc' ), astcHDRSupported: false, // https://github.com/gpuweb/gpuweb/issues/3856 - etc1Supported: false, // support provided by etc2 + etc1Supported: false, // webgpu support provided by etc2 etc2Supported: renderer.hasFeature( 'texture-compression-etc2' ), dxtSupported: false, // rgb565 smooth and hard alpha provided by bc (bptc) rgtcSupported: false, // 1 and 2 channel textures provided by bc (bptc)