Any3DAny3D
·Any3D Team

Les textures, ce glouton qui dévore votre VRAM

3d-compressiontexture-compressionwebglwebgpu

La dernière fois, nous avons réduit les sommets de moitié et le modèle a un peu rétréci, mais il n'est pas devenu un éclair — parce que le vrai poids lourd était encore là : les textures. Dans un modèle PBR, les textures représentent généralement plus de 80 % de la taille, et c'est la part qui gonfle le plus dans le VRAM.

Cet article est le remède au problème du « glouton de VRAM » des textures. Trois choses : pourquoi PNG/JPG sont coupables aux yeux du GPU ; à quoi ressemblent les formats de texture natifs du GPU et pourquoi on ne peut pas les utiliser directement ; et comment Basis Universal + KTX2 relient les trois.

Rappel : pourquoi le JPG fait exploser le VRAM

Dans l'article précédent, nous avons donné une formule :

Utilisation VRAM = largeur * hauteur * 4 octets (RGBA) * 1.333 (avec mipmaps)

Une texture 4096×4096, qu'elle soit un JPG de 1,5 Mo ou un PNG de 8 Mo sur le disque, devient ~87 Mo dans le VRAM. Une seule raison : le GPU ne comprend pas JPG/PNG.

L'unité d'échantillonnage de texture du GPU ne comprend qu'une chose : étant données des coordonnées UV, lire une couleur dans un bloc de pixels de taille fixe. Elle exige que la texture soit dans le VRAM comme « pixels bruts étalés ». Donc avant qu'un navigateur uploade un JPG vers le GPU, il doit d'abord le décompresser entièrement en pixels RGBA sur le CPU, puis pousser tout le bloc dans le VRAM.

Ce processus a trois problèmes :

  1. Explosion du VRAM : les pixels bruts décompressés sont énormes. 87 Mo n'est pas une exagération, c'est ce que calcule la formule.
  2. Blocage de l'upload : déplacer un grand bloc de pixels de la mémoire CPU vers le VRAM du GPU est une opération lente qui bloque la première image.
  3. Coût de décodage CPU : décoder une grande image coûte du temps en soi, surtout sur mobile.

En prolongeant la métaphore de l'« éponge compressée » de la dernière fois : PNG/JPG est une éponge aplatie pour faciliter le transport ; une fois sur le GPU, l'éponge absorbe l'eau et reprend sa taille complète. Le téléchargement a été accéléré ; le VRAM n'a rien économisé.

Les formats de texture du GPU : compressés dans le VRAM par nature

Puisque le GPU n'accepte pas un PNG précompressé, peut-on garder la texture compressée même à l'intérieur du VRAM ? Le GPU décode un seul bloc de pixels à la volée lors de l'échantillonnage, presque sans coût.

C'est exactement ce que font les formats de texture natifs GPU. Familles représentatives :

Famille de formatNom completPrincipales plateformesNotes
BC1-7Block CompressionBureau (PC, Mac)Vétéran, compression par bloc 4×4 pixels à chaque génération
ETC1/2Ericsson Texture CompressionMobile (anciens Android/iOS)Ancien standard mobile
ASTCAdaptive Scalable Texture CompressionMobile/VR (nouveaux appareils)Souple, meilleure qualité, ajustable par bloc
PVRTCPowerVRAnciens iOSRemplacé progressivement par ASTC

Ces formats partagent un trait : les textures sont stockées compressées en petits blocs de 4×4 pixels (blocks), et le GPU décode un petit bloc à la demande lors de l'échantillonnage — ce qui sort n'est pas un seul pixel mais tout un bloc. L'avantage est que l'utilisation du VRAM se réduit selon un ratio fixe quel que soit le contenu.

Comparaison :

PNG/JPG (traditionnel)Formats natifs GPU
Taille disquePetite (JPG surtout)Moyenne (compression par bloc, bitrate fixe)
Utilisation VRAMGrande (décompressée en pixels bruts)Petite (compression par bloc, résidente)
Upload vers GPULent (décodage CPU + gros transfert)Rapide (juste déplacer, pas de décodage)
Vitesse d'échantillonnageRapide (déjà pixels bruts)Rapide (décodage matériel en temps réel)

Les formats GPU semblent la solution parfaite. Alors pourquoi ne pas les utiliser directement ?

Le problème : des appareils reconnaissent des formats différents

C'est le plus grand piège des formats de texture GPU — fragmentation.

  • Les PC de bureau reconnaissent BC1-7, pas ASTC
  • Les téléphones Android reconnaissent ETC2/ASTC, la plupart pas BC
  • iOS (A7+) reconnaît ASTC, les anciens PVRTC
  • WebGPU/WebGL s'appuient sur les mêmes capacités matérielles derrière l'appareil

Si vous voulez qu'une texture « existe au format natif GPU sur tous les appareils », vous devez préparer une copie séparée pour chaque plateforme. Un produit pour bureau + Android + iOS a besoin de BC + ETC2/ASTC pour la même texture — trois versions. Le paquet triple, l'effort aussi.

Pire : sur le web, vous ne savez pas quel appareil ouvre la page. Tout pré-générer n'est pas réaliste, et la détection à l'exécution arrive trop tard.

Basis Universal : encodez une fois, transcodez partout

Basis Universal (Basis en raccourci) est né pour résoudre cette fragmentation. Son idée en une phrase :

D'abord encodez la texture dans un « format intermédiaire », puis à l'exécution transcodez-la dans le format natif correspondant selon les capacités GPU de l'appareil courant.

Flux de transcodage (schématique) :

Texture source (PNG/JPG)
      │  encodage hors ligne unique (lent, une fois)
      ▼
Format intermédiaire Basis (ETC1S ou UASTC)
      │  empaqueté dans un conteneur KTX2
      ▼
Publier sur le web ──┬── GPU bureau ──→ transcodage à l'exécution → BC1/3/7
                    ├── Android ────→ transcodage à l'exécution → ETC2
                    └── iOS/VR ─────→ transcodage à l'exécution → ASTC

Points clés :

  • L'encodage hors ligne se fait une fois, produisant une représentation intermédiaire compacte
  • Le transcodage à l'exécution est très rapide (calcul pur, quelques millisecondes), et il transcode des formats de bloc — pas besoin de décompression par pixel
  • Ce qui entre dans le VRAM après transcodage est un vrai format natif GPU, donc l'utilisation du VRAM se calcule aux taux de compression par bloc, identique aux formats natifs GPU

Basis offre deux modes d'encodage intermédiaire ; le prochain article les détaille, mais retenez les noms :

  • ETC1S : taux de compression extrêmement élevé, adapté aux diffuse/albedo et autres color maps
  • UASTC : qualité supérieure, adapté aux normales et autres maps sensibles à la précision

KTX2 : le conteneur standard pour les textures GPU

Il reste une question d'ingénierie : où placer les données Basis encodées, comment les marquer et comment les relier à glTF ? La réponse est KTX2.

KTX2 (Khronos Texture 2) n'est pas un autre format d'image, mais un format conteneur. Comme un .zip ne se soucie pas de contenir documents ou images, KTX2 se contente d'empaqueter les données de texture GPU (y compris encodées Basis) dans une structure standard avec des métadonnées (format, niveaux de mipmap, espace colorimétrique, etc.).

Dans glTF, KTX2 se branche via l'extension KHR_texture_basisu : la texture n'est plus un fichier PNG mais un fichier KTX2 contenant l'encodage Basis. Au chargement, le moteur détecte les capacités de l'appareil et transcode vers le BC/ETC/ASTC correspondant.

Démêlons les trois rôles — ne pas confondre :

NomRôleAnalogie
Basis UniversalSchéma d'encodage (comment compresser une texture en format intermédiaire)Une sorte d'« algorithme de compression »
KTX2Format conteneur (comment empaqueter les données encodées)Une « boîte »
KHR_texture_basisuExtension glTF (dit au moteur que c'est une texture Basis)Une « étiquette »

Un fichier KTX2 peut contenir un encodage Basis (multiplateforme) ou un format natif (par ex. BC7 brut). Sur le web, à 99 % il contient Basis, parce que ce que nous voulons c'est « encoder une fois, transcoder partout ».

Exemple VRAM : une texture 4096 comparée

En empilant la formule et les formats GPU, voici l'empreinte réelle d'une texture 4096×4096 RGBA selon les options :

OptionTaille disqueUtilisation VRAM (avec mipmaps)Vitesse d'uploadMultiplateforme
PNG~8 Mo~87 MoLent (nécessite décodage)
JPG~1.5 Mo~87 MoLent (nécessite décodage)
WebP~2 Mo~87 MoLent (nécessite décodage)
KTX2 (ETC1S)~2-3 Mo~11-14 MoRapide✅ (transcodage)
KTX2 (UASTC)~6-8 Mo~22 MoRapide✅ (transcodage)

D'où viennent les chiffres VRAM : la compression par bloc GPU se compte généralement à 4bpp (4 bits par pixel) ou 8bpp. 4096×4096 à 4bpp fait environ 8 Mo, ×1.333 avec mipmaps ≈ 11 Mo. UASTC est majoritairement transcodé vers 8bpp, donc environ 22 Mo.

L'important n'est pas le chiffre exact d'une ligne, mais ces deux points :

  1. Les formats traditionnels (PNG/JPG/WebP) ont une utilisation VRAM quasi identique — tous des pixels bruts décompressés, 87 Mo. Aussi petits sur le disque, le VRAM n'est pas économisé.
  2. KTX2 fait chuter le VRAM à 1/4 ou 1/8, et la taille disque est aussi compétitive.

C'est pourquoi VR et le web mobile vont presque toujours au KTX2 — combien de textures de 87 Mo tiennent dans le VRAM de 2 Go d'un téléphone ? À 11 Mo, sept tiennent.

Matrice de support des plateformes : quel GPU reconnaît quel format

Basis nous masque les détails, mais comprendre le mappage sous-jacent aide au dépannage. Voici le support actuel des appareils majeurs pour les formats natifs :

Plateforme / appareilBC1-7ETC2ASTCPVRTC
PC bureau (D3D11/12, Vulkan, WebGPU)Partiel (GPU récents)
macOS (Metal)✅ (machines récentes)
Android (grand public)
iOS (A8+)✅ (anciens appareils)
WebGL 2Selon extensionPartiel
WebGPU✅ (bureau)✅ (selon appareil)

Basis détecte ces capacités à l'exécution et transcode le même encodage intermédiaire vers la meilleure correspondance. C'est pourquoi cette couche Basis est presque irremplaçable sur le web — vous ne pouvez pas prédire l'appareil de l'utilisateur avant publication.

Flux d'upload comparé : traditionnel vs formats GPU

Pour finir, fixons la différence dans un diagramme de flux.

PNG/JPG traditionnel :

Fichier PNG ──téléchargement──> mémoire CPU ──décodage CPU (lent)──> bloc de pixels RGBA ──upload (grand, lent)──> VRAM (87 Mo)

KTX2 + Basis :

Fichier KTX2 ──téléchargement──> mémoire CPU ──transcodage runtime (rapide)──> format de bloc GPU ──upload (petit, rapide)──> VRAM (11 Mo)

Ce dernier supprime la grande étape de « décodage CPU par pixel », et les données uploadées sont d'un ordre de grandeur plus petites. Première image plus rapide, moins de VRAM — c'est la valeur centrale de cette approche.

Étape suivante

La théorie est faite ; le prochain article est pratique. Nous comprimerons des textures en KTX2 avec toktx et gltf-transform, les chargerons dans Three.js / Babylon.js et aborderons comment choisir ETC1S vs UASTC et ajuster les paramètres de compression.

Nous soutenir