# Support for RawTexture2DArray with compression from basis textures

**URL:** <https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782>\
**Category:** Questions\
**Created:** [June 2, 2025, 9:52am UTC](https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782 "2025-06-02T09:52:23Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![knervous](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/knervous/32/46952_2.png) [@knervous](https://forum.babylonjs.com/u/knervous)\
**Post date:** [June 2, 2025, 9:52am UTC](https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782/1 "2025-06-02T09:52:23Z")

</div>

Hello, I am on a journey now optimizing for a web game I’m making, there are many steps to get entities scaling up and I’m trying to tick the boxes where it will count.

My solution for entities en masse that share the same model but varying textures (a plate breastplate vs chain, a polar bear texture vs a brown bear) was using asset container + mesh instances + single ShaderMaterial + VAT + contiguous texture atlas via `basisu` with a 2darray texture type. I tried atlasing with uv scale and offset and had mixed results, and things are working nicely with RawTexture2DArray.

I noticed when i was packing these atlases with raw RGB(A) data, they got pretty big, like 5mb-10mb, and that’s all uploaded to the GPU. The interface around the raw textures via RawTexture2DArray doesn’t expose compression via configuration, and the config around raw textures that does take in compression is keying into the engine’s s3tc capabilities… In my particular case I am transcoding to astc.

My question is: Are there current plans on providing high level support for compressed RawTexture2DArray? If not, should there be?

I got something working that works in WebGL2, I need to modify the shader code for WebGPU, although that’s not specific to the texture compression here anyway. I am new to a lot of this so really just hacking it in where I can.

The storage on disk was reduced significantly when storing raw 2darray basis data rather than rgba (6mb down to 150kb) and when expanded in memory all the slices totaled around 1.6mb rather than 6mb.

I wanted to share it and see what people thought. 500 entities with different textures, animations, secondary meshes, all sharing one material.

> **[Babylon.js Playground](https://playground.babylonjs.com/?BabylonToolkit#D7PKC7%235)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

---

<div class="post-metadata">

**Author:** ![knervous](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/knervous/32/46952_2.png) [@knervous](https://forum.babylonjs.com/u/knervous)\
**Post date:** [June 2, 2025, 1:03pm UTC](https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782/2 "2025-06-02T13:03:25Z")

</div>

Side question: has anyone experimented with compressing VAT textures? Those things can get huge!

---

<div class="post-metadata">

**Author:** ![Deltakosh](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/deltakosh/32/26635_2.png) [@Deltakosh](https://forum.babylonjs.com/u/Deltakosh)\
**Post date:** [June 2, 2025, 3:05pm UTC](https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782/3 "2025-06-02T15:05:52Z")

</div>

cc @alexchuber

I’d love to see this feature in the engine honestly 🙂 Would you be open to do a PR @knervous ? Maybe with Alex’s help

---

<div class="post-metadata">

**Author:** ![knervous](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/knervous/32/46952_2.png) [@knervous](https://forum.babylonjs.com/u/knervous)\
**Post date:** [June 3, 2025, 5:27am UTC](https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782/4 "2025-06-03T05:27:28Z")

</div>

After getting some results across different GPUs it seems like even when mapping engine capabilities to the basis compression type there ends up with some bad sampling, so it seems like it’s not a given that this could work right out of the box… Also in a bit over my head here maybe 🙂 I will continue tinkering with it but don’t know if I have the background to know all the gotchas in this setup. If I take another pass and can get consistent results I will circle back and see if it fits in the engine.

---

<div class="post-metadata">

**Author:** ![alexchuber](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/alexchuber/32/52000_2.png) [@alexchuber](https://forum.babylonjs.com/u/alexchuber)\
**Post date:** [June 3, 2025, 3:45pm UTC](https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782/5 "2025-06-03T15:45:46Z")

</div>

Wow, this looks awesome! That’s a significant difference in memory.

As you’ve probably noticed, we don’t currently support 2D texture arrays in our basis, ktx, and dds loaders-- only 2D and cube textures are handled. I believe these loaders actually predate, or nearly predate, texture 2D array support in major browsers.

A logical first step might be expanding our texture loaders to handle 2D texture arrays, similar to how cube textures are currently supported. Understandably, that’s not a trivial task, so maybe just updating RawTexture2DArray to expose compression parameters would be a better start.

Also, you mentioned how the RawTexture2DArray methods in the engine assume s3tc compression by default. That looks like a bug, since there’s no real reason (as far as I know) to lock it to that format.

As for the bad sampling… out of curiosity, are you copying the compression type mappings from our basis or KTX loaders?

Love the setup you’re using. You’ve got a real neat combo of optimizations. Thanks for sharing all the details and results!

---

<div class="post-metadata">

**Author:** ![knervous](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/knervous/32/46952_2.png) [@knervous](https://forum.babylonjs.com/u/knervous)\
**Post date:** [June 5, 2025, 11:49am UTC](https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782/6 "2025-06-05T11:49:42Z")

</div>

Thanks! Here’s an updated PG with some preflight config for the basis transcoder to tell it which formats to try to transcode to…

> **[Babylon.js Playground](https://playground.babylonjs.com/?BabylonToolkit#T3QKRV%2314)**
>
> Babylon.js playground is a live editor for Babylon.js WebGL and WebGPU 3D scenes

Not super familiar with the API iirc those params are just passed into the compiled wasm, but if there’s a format enabled true and it comes back with a success it will yield all that data in that format, as well as the basis format. From there I’m using the tools in the engine `glInternalFormat = BABYLON.GetInternalFormatFromBasisFormat(basisFormat, engine);`

I don’t know if this logic is already in the engine somewhere for checking what we should potentially be asking for

```auto
    if (isWebGL) {
        const gl = engine._gl;
        supportedCompressionFormats.etc1 = !!gl.getExtension('WEBGL_compressed_texture_etc1');
        supportedCompressionFormats.s3tc = !!gl.getExtension('WEBGL_compressed_texture_s3tc');
        supportedCompressionFormats.pvrtc = !!gl.getExtension('WEBGL_compressed_texture_pvrtc');
        supportedCompressionFormats.etc2 = !!gl.getExtension('WEBGL_compressed_texture_etc');
        supportedCompressionFormats.astc = !!gl.getExtension('WEBGL_compressed_texture_astc');
        supportedCompressionFormats.bc7 = !!gl.getExtension('EXT_texture_compression_bptc');
    } else if (isWebGPU) {
        // WebGPU: Check supported texture formats
        const adapter = await engine._device;
        const features = await adapter.features; // Hypothetical; WebGPU API may vary
        supportedCompressionFormats.etc1 = false; // ETC1 not typically supported in WebGPU
        supportedCompressionFormats.s3tc = features.has('texture-compression-bc'); // Includes BC7
        supportedCompressionFormats.pvrtc = false; // PVRTC not typically supported
        supportedCompressionFormats.etc2 = features.has('texture-compression-etc2');
        supportedCompressionFormats.astc = features.has('texture-compression-astc');
        supportedCompressionFormats.bc7 = features.has('texture-compression-bc');
    }

```

I’d be happy to take a look sometime at some small PRs around compression on RawTexture2DArray and RawTexture, might be a bit til I get around to it. Busy baking in this solution to my web game now - if anyone is interested, it’s EverQuest in the browser, yeehaw 8) [https://eqrequiem.com](https://eqrequiem.com)

---

<div class="post-metadata">

**Author:** ![ryantrem](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/ryantrem/32/34010_2.png) [@ryantrem](https://forum.babylonjs.com/u/ryantrem)\
**Post date:** [July 29, 2026, 11:27pm UTC](https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782/7 "2026-07-29T23:27:29Z")

</div>

@knervous this may be relevant: [Populate 2D texture array from image asset - Feature requests - Babylon.js](https://forum.babylonjs.com/t/populate-2d-texture-array-from-image-asset/35140/9)

---

<div class="post-metadata">

**Author:** ![knervous](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/knervous/32/46952_2.png) [@knervous](https://forum.babylonjs.com/u/knervous)\
**Post date:** [July 30, 2026, 4:12am UTC](https://forum.babylonjs.com/t/support-for-rawtexture2darray-with-compression-from-basis-textures/58782/8 "2026-07-30T04:12:06Z")

</div>

Thanks @ryantrem - so cool to see you guys knocking down the dominos of the past so quickly, and it’s great to see the array handling move into the decoder/engine rather than requiring private engine patches.

I eventually worked around the original gap downstream in Shado/Requiem. The assets are multi-image Basis 2D arrays with a separate layer manifest—for example [`hum.basis`](https://github.com/knervous/eqrequiem/blob/main/shader-object/sandbox/public/shado/eq-demo/armor/hum.basis) and [`hum.json`](https://github.com/knervous/eqrequiem/blob/main/shader-object/sandbox/public/shado/eq-demo/armor/hum.json).

Looks like current full BJS path takes the portable route: [`BasisTools.TranscodeAsync`](https://github.com/knervous/eqrequiem/blob/main/shader-object/src/showcase/EqShowcase.ts#L431-L489), conversion to RGBA8, then `RawTexture2DArray`. The Lite path similarly uses `BasisFile`, transcodes each layer to RGBA32, and uploads it with `queue.writeTexture({ origin: [0, 0, layer] })`: [`loadLiteArmorAtlas`](https://github.com/knervous/eqrequiem/blob/main/shader-object/sandbox/src/LitePlayground.ts#L379-L455).

Shado already consumes the resulting array in both shader languages: [`sampler2DArray` in GLSL and `texture_2d_array<f32>` in WGSL like this example](https://github.com/knervous/eqrequiem/blob/main/shader-object/src/showcase/EqShowcaseShader.ts#L20-L92) so is sort of a custom solution

I also retained the older experimental compressed upload implementation in Requiem: [format selection and Basis transcoding](https://github.com/knervous/eqrequiem/blob/main/client/src/Game/Model/basis-texture.ts#L23-L131), [WebGL `compressedTexImage3D` / `compressedTexSubImage3D`](https://github.com/knervous/eqrequiem/blob/main/client/src/Game/Model/basis-texture.ts#L140-L245), and the [WebGPU compressed-array upload experiment](https://github.com/knervous/eqrequiem/blob/main/client/src/Game/Model/basis-texture.ts#L248-L350). Prod in requiem currently defaults that path to decompression because the experiment only handled mip 0 and had incomplete format/block handling.

Looks like i’d be using the latest API changes like this if i am reading this right with some outstanding caveats

- Babylon Core’s [`CreateTexture2DArrayFromKTX2Async`](https://github.com/BabylonJS/Babylon.js/pull/18740) removes the custom array decoding/assembly, but currently forces RGBA and uploads only the base mip. It therefore does not yet preserve the compressed-VRAM advantage from my original question.
- Babylon Lite’s [`loadKtx2Texture2DArray` / `uploadKtx2Texture2DArray`](https://github.com/BabylonJS/Babylon-Lite/pull/499) is almost exactly what i’m looking for (i am also targeting lite in my production game): BC/ETC2/ASTC remains compressed in VRAM and the authored mip chain is uploaded.

One remaining diff is Shado’s runtime atlas builder, which packs arbitrary source textures into pages with padding and edge bleed before creating the array: [`AtlasBuilder`](https://github.com/knervous/eqrequiem/blob/main/shader-object/src/extensions/AtlasBuilder/AtlasBuilder.ts#L87-L229). That dynamic case remains inherently RGBA unless the packed pages are generated offline as a layered KTX2.

I’ll try to plug this in to emit ithe existing armor arrays as layered KTX2 and replace the manual Lite transcoder with the new loader. The Lite PR notes that a real UASTC/ETC1S array KTX2 was not available for runtime validation; these EQ armor arrays could make a useful practical test case, I’ll try to spin up a PG how it plugs in on my side. Thanks for the hard work and ping! \<3
