Any3DAny3D
·Any3D Team

Texturen, der Vielfraß, der Ihren VRAM auffrisst

3d-compressiontexture-compressionwebglwebgpu

Letztes Mal haben wir die Vertices halbiert und das Modell ist etwas geschrumpft, aber es wurde kein Blitz – denn der wahre Größen-Fresser war noch da: Texturen. In einem PBR-Modell machen Texturen meist 80 %+ der Größe aus, und genau dieser Teil bläht sich im VRAM am stärksten auf.

Dieser Artikel ist das Heilmittel gegen den „VRAM-Vielfraß" der Texturen. Drei Dinge: warum PNG/JPG in den Augen der GPU schuldig sind; wie GPU-native Texturformate aussehen und warum man sie nicht direkt verwenden kann; und wie Basis Universal + KTX2 alle drei verbinden.

Rückblick: Warum JPG den VRAM explodieren lässt

Im vorherigen Artikel gab es eine Formel:

VRAM-Nutzung = Breite * Höhe * 4 Bytes (RGBA) * 1.333 (mit Mipmaps)

Eine 4096×4096-Textur wird im VRAM zu ~87 MB, egal ob auf der Festplatte ein 1,5-MB-JPG oder ein 8-MB-PNG ist. Ein Grund: die GPU versteht kein JPG/PNG.

Der Textur-Sampler der GPU versteht nur eines: bei gegebenen UV-Koordinaten eine Farbe aus einem Block fester Pixelgröße zu lesen. Er verlangt, dass die Textur im VRAM als „ausgebreitete Rohpixel" vorliegt. Bevor der Browser also ein JPG zur GPU hochlädt, muss er es auf der CPU vollständig in RGBA-Pixel dekomprimieren und den gesamten Block in den VRAM schieben.

Dieser Vorgang hat drei Probleme:

  1. VRAM-Explosion: die dekomprimierten Rohpixel sind riesig. 87 MB sind keine Übertreibung, sondern das Ergebnis der Formel.
  2. Upload-Blockade: das Verschieben eines großen Pixel-Blocks vom CPU-Speicher in den VRAM der GPU ist ein langsamer Vorgang, der das erste Frame blockiert.
  3. CPU-Dekodierungskosten: das Dekodieren eines großen Bildes kostet selbst Zeit, auf Mobilgeräten besonders.

Die Metapher der „komprimierten Schwamms" vom letzten Mal fortgesetzt: PNG/JPG ist ein für den Transport flach gepresster Schwamm; auf der GPU saugt der Schwamm sich mit Wasser voll und kehrt zur vollen Größe zurück. Der Download wurde schneller, am VRAM wurde nichts gespart.

Die eigenen Texturformate der GPU: von Natur aus im VRAM komprimiert

Wenn die GPU kein vorkomprimiertes PNG akzeptiert, können wir die Textur auch im VRAM komprimiert halten? Die GPU dekodiert beim Sampling einzelne Pixelblöcke on the fly, fast ohne Kosten.

Genau das tun GPU-native Texturformate. Repräsentative Familien:

Format-FamilieVollständiger NameHauptplattformenHinweise
BC1-7Block CompressionDesktop (PC, Mac)Veteran, 4×4-Pixel-Block-Kompression pro Generation
ETC1/2Ericsson Texture CompressionMobil (ältere Android/iOS)Alter Mobil-Standard
ASTCAdaptive Scalable Texture CompressionMobil/VR (neuere Geräte)Flexibel, beste Qualität, pro Block einstellbar
PVRTCPowerVRÄltere iOSWird von ASTC verdrängt

Diese Formate haben eines gemeinsam: Texturen werden komprimiert in 4×4-Pixel-Blöcken (Blocks) gespeichert, und die GPU dekodiert beim Sampling einen kleinen Block bei Bedarf – heraus kommt nicht ein einzelnes Pixel, sondern ein ganzer Block. Der Vorteil: die VRAM-Nutzung schrumpft unabhängig vom Inhalt um ein festes Verhältnis.

Vergleich:

PNG/JPG (traditionell)GPU-native Formate
FestplattengrößeKlein (JPG besonders)Mittel (Block-komprimiert, feste Bitrate)
VRAM-NutzungGroß (zu Rohpixeln dekomprimiert)Klein (Block-komprimiert, resident)
Upload zur GPULangsam (CPU-Dekodierung + großer Transfer)Schnell (einfach verschieben, kein Dekodieren)
Sampling-GeschwindigkeitSchnell (bereits Rohpixel)Schnell (Hardware-Echtzeitdekodierung)

GPU-Formate wirken wie die perfekte Lösung. Warum also nicht einfach verwenden?

Das Problem: Verschiedene Geräte erkennen verschiedene Formate

Das ist die größte Falle der GPU-Texturformate – Fragmentierung.

  • Desktop-PCs erkennen BC1-7, nicht ASTC
  • Android-Handys erkennen ETC2/ASTC, meist nicht BC
  • iOS (A7+) erkennt ASTC, ältere Geräte PVRTC
  • WebGPU/WebGL nutzen dieselbe Hardware-Fähigkeit hinter dem Gerät

Wenn Sie eine Textur „auf jedem Gerät als GPU-natives Format existierend" haben wollen, müssen Sie für jede Plattform eine separate Kopie vorbereiten. Ein Produkt mit Desktop + Android + iOS braucht für dieselbe Textur BC + ETC2/ASTC – drei Versionen. Paket verdreifacht sich, Engineering-Aufwand verdreifacht sich.

Schlimmer noch: Im Web wissen Sie nicht, welches Gerät der Nutzer öffnet. Vorab alle Formate zu generieren ist unrealistisch, und Laufzeit-Erkennung kommt zu spät.

Basis Universal: einmal kodieren, überall transcodieren

Basis Universal (kurz Basis) wurde geboren, um diese Fragmentierung zu lösen. Die Idee in einem Satz:

Zuerst die Textur in ein „Zwischenformat" kodieren, dann zur Laufzeit gemäß den GPU-Fähigkeiten des aktuellen Geräts in das entsprechende native Format transcodieren.

Transcoding-Ablauf (schematisch):

Quelltextur (PNG/JPG)
      │  einmalige Offline-Kodierung (langsam, einmal)
      ▼
Basis-Zwischenformat (ETC1S oder UASTC)
      │  in einen KTX2-Container gepackt
      ▼
Web veröffentlichen ──┬── Desktop-GPU ──→ Laufzeit-Transcoding → BC1/3/7
                     ├── Android ────→ Laufzeit-Transcoding → ETC2
                     └── iOS/VR ─────→ Laufzeit-Transcoding → ASTC

Schlüsselpunkte:

  • Offline-Kodierung erfolgt einmal und liefert eine kompakte Zwischendarstellung
  • Laufzeit-Transcoding ist sehr schnell (reine Berechnung, wenige Millisekunden), und es transcodiert Block-Formate – kein Pixelweises Dekomprimieren nötig
  • Was nach dem Transcodieren in den VRAM gelangt, ist ein echtes GPU-natives Format, sodass die VRAM-Nutzung nach Block-Kompressionsraten berechnet wird, identisch mit GPU-nativen Formaten

Basis bietet zwei intermediate Kodierungsmodi; der nächste Artikel entfaltet sie, aber merken Sie sich die Namen:

  • ETC1S: extrem hohes Kompressionsverhältnis, geeignet für Diffuse/Albedo und andere Farb-Maps
  • UASTC: höhere Qualität, geeignet für Normal- und andere präzisionsempfindliche Maps

KTX2: der Standard-Container für GPU-Texturen

Es bleibt eine Engineering-Frage: Wohin mit den kodierten Basis-Daten, wie werden sie markiert und wie hängen sie mit glTF zusammen? Die Antwort ist KTX2.

KTX2 (Khronos Texture 2) ist kein weiteres Bildformat, sondern ein Container-Format. Wie eine .zip nicht interessiert, ob sie Dokumente oder Bilder enthält, packt KTX2 lediglich GPU-Texturdaten (einschließlich Basis-kodierter) in eine Standardstruktur mit Metadaten (Format, Mipmap-Level, Farbraum usw.).

In glTF wird KTX2 über die Erweiterung KHR_texture_basisu eingebunden: Die Textur ist keine PNG-Datei mehr, sondern eine KTX2-Datei mit Basis-Kodierung. Beim Laden erkennt die Engine die Gerätefähigkeiten und transcodiert zum passenden BC/ETC/ASTC.

Die drei Rollen entwirren – nicht verwechseln:

NameRolleAnalogie
Basis UniversalKodierungsschema (wie eine Textur ins Zwischenformat komprimiert wird)Ein „Kompressionsalgorithmus"
KTX2Container-Format (wie die kodierten Daten verpackt werden)Eine „Box"
KHR_texture_basisuglTF-Erweiterung (sagt der Engine, dass es eine Basis-Textur ist)Ein „Etikett"

Eine KTX2-Datei kann Basis-Kodierung (plattformübergreifend) oder ein natives Format (z. B. rohes BC7) enthalten. Im Web enthält sie zu 99 % Basis, denn wir wollen „einmal kodieren, überall transcodieren".

VRAM-Beispiel: eine 4096-Textur verglichen

Formel und GPU-Formate übereinandergelegt, hier der reale Footprint einer 4096×4096-RGBA-Textur unter verschiedenen Optionen:

OptionFestplattengrößeVRAM-Nutzung (mit Mipmaps)Upload-GeschwindigkeitPlattformübergreifend
PNG~8 MB~87 MBLangsam (Dekodierung nötig)
JPG~1.5 MB~87 MBLangsam (Dekodierung nötig)
WebP~2 MB~87 MBLangsam (Dekodierung nötig)
KTX2 (ETC1S)~2-3 MB~11-14 MBSchnell✅ (Transcode)
KTX2 (UASTC)~6-8 MB~22 MBSchnell✅ (Transcode)

Herkunft der VRAM-Zahlen: GPU-Block-Kompression rechnet meist mit 4bpp (4 Bits pro Pixel) oder 8bpp. 4096×4096 bei 4bpp sind etwa 8 MB, ×1.333 mit Mipmaps ≈ 11 MB. UASTC wird meist auf 8bpp transcodiert, also etwa 22 MB.

Es geht nicht um die exakte Zahl einer Zeile, sondern um zwei Punkte:

  1. Traditionelle Formate (PNG/JPG/WebP) haben nahezu identische VRAM-Nutzung – alles dekomprimierte Rohpixel, 87 MB. So klein auf der Festplatte, im VRAM wird nichts gespart.
  2. KTX2 senkt den VRAM auf 1/4 bis 1/8, und die Festplattengröße ist ebenfalls konkurrenzfähig.

Deshalb kommen VR und Mobil-Web fast immer mit KTX2 aus – wie viele 87-MB-Texturen passen in den 2-GB-VRAM eines Handys? Bei 11 MB passen sieben.

Plattform-Support-Matrix: Welche GPUs erkennen welche Formate

Basis schirmt uns von den Details ab, aber das Verständnis des zugrunde liegenden Mappings hilft bei der Fehlersuche. Hier der aktuelle Mainstream-Support für native Formate:

Plattform / GerätBC1-7ETC2ASTCPVRTC
Desktop-PC (D3D11/12, Vulkan, WebGPU)Partiell (neuere GPUs)
macOS (Metal)✅ (neuere Geräte)
Android (Mainstream)
iOS (A8+)✅ (ältere Geräte)
WebGL 2Je nach ErweiterungPartiell
WebGPU✅ (Desktop)✅ (geräteabhängig)

Basis erkennt diese Fähigkeiten zur Laufzeit und transcodiert dieselbe Zwischenkodierung in die beste Passung. Deshalb ist diese Basis-Schicht im Web nahezu unersetzlich – Sie können das Gerät des Nutzers vor der Veröffentlichung nicht vorhersagen.

Upload-Ablauf im Vergleich: traditionell vs. GPU-Formate

Abschließend den Unterschied in einem Ablaufdiagramm fixieren.

Traditionelles PNG/JPG:

PNG-Datei ──Download──> CPU-Speicher ──CPU-Dekodierung (langsam)──> RGBA-Pixel-Block ──Upload (groß, langsam)──> VRAM (87 MB)

KTX2 + Basis:

KTX2-Datei ──Download──> CPU-Speicher ──Laufzeit-Transcoding (schnell)──> GPU-Block-Format ──Upload (klein, schnell)──> VRAM (11 MB)

Letzteres lässt den großen Schritt „pixelweise CPU-Dekodierung" weg, und die hochgeladenen Daten sind eine Größenordnung kleiner. Schnelleres erstes Frame, weniger VRAM – das ist der Kernwert dieses Ansatzes.

Nächster Schritt

Theorie erledigt; der nächste Artikel ist Praxis. Wir komprimieren mit toktx und gltf-transform tatsächlich Texturen zu KTX2, laden sie in Three.js / Babylon.js und besprechen, wie man ETC1S vs. UASTC wählt und Kompressionsparameter justiert.

Unterstützen Sie uns