Руководство для начинающих

Что происходит с текстурой внутри GPU

PNG размером 300 КБ может незаметно занять 5 МБ видеопамяти. Это руководство объясняет почему, как GPU уменьшают текстуры с помощью блочного сжатия и как выбрать формат. К каждой идее прилагается то, что можно попробовать самому.

Около 10 минут5 интерактивных демо
Начать чтение

Глава 1

Размер файла — не размер в памяти

PNG и JPEG созданы для хранения и передачи изображений. Прежде чем GPU сможет нарисовать текстуру, изображение распаковывается в «сырые» тексели — обычно по 4 байта на каждый.

GPU выбирает эти тексели миллионы раз за кадр, поэтому они должны быть всегда готовы к чтению. Значит, расход памяти зависит только от разрешения и формата пикселей, а не от того, насколько хорошо был сжат файл. Мип-уровни — уменьшенные копии для удалённых поверхностей — добавляют ещё примерно треть.

ПопробуйтеКалькулятор видеопамятиМеняйте разрешение и число текстур, чтобы увидеть, сколько памяти GPU нужно одним и тем же текстурам в каждом формате.
Разрешение
  • RGBA8
    32 бит на тексель
    213 MiB
    Эталон
    21,3 MiB на текстуру
  • BC7 · ASTC 4×4
    8 бит на тексель
    53,3 MiB
    в 4× меньше
    5,3 MiB на текстуру
  • BC1 · ETC2 RGB
    4 бит на тексель
    26,7 MiB
    в 8× меньше
    2,7 MiB на текстуру
  • ASTC 6×6
    3,56 бит на тексель
    23,8 MiB
    в 9× меньше
    2,4 MiB на текстуру
  • ASTC 8×8
    2 бит на тексель
    13,3 MiB
    в 16× меньше
    1,3 MiB на текстуру
  • ASTC 12×12
    0,89 бит на тексель
    6 MiB
    в 35,7× меньше
    612 KiB на текстуру

Блочные форматы округляются вверх до целых блоков на каждом мип-уровне, поэтому числа совпадают с тем, что реально выделяет GPU.

Глава 2

Почему GPU не может хранить PNG

Когда шейдер выбирает данные из текстуры, он может запросить любой тексель в любом порядке. Оборудование должно найти его мгновенно.

PNG и JPEG используют кодирование переменной длины: чтобы добраться до пикселя в середине, нужно сначала декодировать всё, что идёт перед ним. Форматы GPU имеют фиксированную скорость: каждый блок 4×4 занимает ровно одинаковое число байтов, поэтому адрес любого текселя вычисляется простым умножением. Текстура остаётся сжатой в памяти, а текстурный блок декодирует данные при чтении.

ПопробуйтеНайдите тексельНажмите на любой тексель этой текстуры 32×32. Блочный формат может вычислить, где он хранится, а PNG — нет.

Перемещайте выделение клавишами со стрелками.

Выбранный тексель
x 13, y 18
Его блок
столбец 3, строка 4
Адрес в BC1
(4 × 8 + 3) × 8 байт = байт 280

64 блока в том порядке, в каком они лежат в памяти

0511

Тот же тексель в PNG

Сначала нужно распаковать строки с 0 по 18 (19 из 32)

Глава 3

Внутри блока 4×4

BC1, самый старый из широко используемых форматов, нагляднее всего показывает идею. Он хранит блок из 16 текселей всего в 8 байтах.

Кодировщик выбирает два опорных цвета. Декодер смешивает их в четыре цвета на прямой линии, а каждый тексель хранит 2-битный номер ближайшего из них. Блоки с плавными переходами цвета сохраняются хорошо, а блоки с несколькими несвязанными цветами — нет.

ПопробуйтеИсследователь блоков BC1Выберите блок 4×4 в примере текстуры и посмотрите, как именно BC1 его хранит.
Блок 12, 4

Глава 4

Много форматов — разные компромиссы

Новые форматы расходуют биты разумнее, чем BC1: больше опорных точек, несколько цветовых линий в блоке, лучший альфа-канал или выбор размера блока.

BC7 и ASTC 4×4 используют 8 бит на тексель и выглядят очень близко к оригиналу. ASTC меняет качество на размер через размер блока: блок всегда занимает 128 бит, поэтому блок 12×12 стоит меньше одного бита на тексель. Basis Universal идёт другим путём: он поставляет один компактный файл и перекодирует его на устройстве в формат, который поддерживает этот GPU.

Распространённые форматы текстур GPU
ФорматБит на тексельАльфаТипичные платформыЛучше всего для
BC141 битПК, консолиНепрозрачный цвет при нехватке памяти
BC38ДаПК, консолиЦвет с плавной альфой в старом контенте
BC44Каналы данныхПК, консолиОдин канал: маски, шероховатость, высота
BC58Каналы данныхПК, консолиКарты нормалей (X и Y)
BC6H8НетПК, консолиHDR-цвет, например скайбоксы и карты освещения
BC78ДаПК, консолиКачественный цвет с альфой или без неё
ETC14НетПочти все AndroidНепрозрачный цвет на очень старых устройствах
ETC24–8ДаAndroid, iOSШироко поддерживаемый запасной вариант для мобильных
ASTC0.89–8ДаСовременные Android и iOS, Apple SiliconНастраиваемое качество на мобильных
PVRTC2–4ДаСтарые iPhone и iPadТолько старые устройства Apple
Basis ETC1SЗависит от целевого форматаДаЛюбой GPU через KTX2Маленькие загрузки, работает на любом GPU
Basis UASTCЗависит от целевого форматаДаЛюбой GPU через KTX2Высокое качество, работает на любом GPU
ПопробуйтеЗапустите настоящие кодировщикиСожмите пример 512×512 теми же кодировщиками WebAssembly, что используют наши инструменты, и сравните увеличенный фрагмент.
Работает в браузере. Ничего не загружается на сервер.
Увеличенная область
Оригинал
RGBA8
1 MiB в памяти GPU
BC1
BC1
4 бит на тексель
128 KiB в памяти GPU
BC7
BC7
8 бит на тексель
256 KiB в памяти GPU
ASTC 6×6
ASTC 6×6
3,56 бит на тексель
116 KiB в памяти GPU
ASTC 12×12
ASTC 12×12
0,89 бит на тексель
29 KiB в памяти GPU

Нажмите «Запустить кодировщики», чтобы сжать пример. При первом запуске кодировщики будут загружены.

Глава 5

Как выбрать формат

Два вопроса быстро сужают выбор: какие GPU должны загружать текстуру и какие данные она содержит?

Цветовые текстуры, карты нормалей и одноканальные маски по-разному реагируют на сжатие, а настольные, мобильные и веб-платформы поддерживают разные семейства форматов. Выберите свой случай, чтобы получить разумную отправную точку, а затем проверьте результат на своих текстурах.

ПопробуйтеПодбор форматаВыберите, где будет работать текстура и что в ней хранится.
Где она будет работать?
Что содержит текстура?

Начните с

ASTC 6×6

Также подойдёт: ASTC 4×4 · ETC2 RGB

ASTC работает на современных GPU Android и iOS. 6×6 — хороший баланс; для ключевых ресурсов используйте 4×4, а для старых устройств — ETC2.

Открыть кодировщик ASTC

Глава 6

Практические советы

Несколько привычек предотвращают большинство проблем со сжатием текстур.

  1. 01

    Помечайте цвет как sRGB, а данные — как линейные

    Текстуры альбедо и интерфейса хранятся в sRGB. Карты нормалей, шероховатости и другие данные должны оставаться линейными, иначе освещение будет неверным.

  2. 02

    Отдельный формат для карт нормалей

    Используйте двухканальные форматы, такие как BC5 или EAC RG11, и восстанавливайте Z в шейдере. Цветовые форматы тратят биты впустую и добавляют заметные неровности.

  3. 03

    Генерируйте мип-уровни

    Они добавляют около 33% памяти, но убирают мерцание удалённых поверхностей и ускоряют выборку.

  4. 04

    Подбирайте размеры под блоки

    Большинство форматов работает блоками 4×4, поэтому размеры, кратные 4, не требуют дополнения. Старым форматам вроде PVRTC также нужны квадратные размеры, равные степени двойки.

  5. 05

    Проверяйте реальный контент на реальных устройствах

    Сравните декодированный результат с оригиналом при полном увеличении и убедитесь, что целевые GPU поддерживают формат.

Частые вопросы

Сжатие текстур GPU происходит без потерь?

Нет. BC, ETC, ASTC и PVRTC — форматы с потерями и фиксированным размером блока. Насколько упадёт качество, зависит от формата, размера блока и изображения.

Почему бы не использовать PNG или JPEG как текстуры напрямую?

Они быстрее скачиваются, но GPU не может выбирать из них данные, поэтому они декодируются в «сырые» тексели и занимают полный объём памяти. Форматы GPU остаются компактными и в памяти.

Какой формат текстур лучше всего?

BC7 — надёжный выбор по умолчанию на ПК, ASTC — на современных мобильных устройствах, а KTX2 с Basis Universal — когда один файл должен работать на многих GPU, например в вебе.

Что значит «бит на тексель»?

Это средние затраты памяти на один тексель. RGBA8 без сжатия использует 32 бита, BC1 — 4, BC7 — 8, а ASTC — от 8 до примерно 0,89 в зависимости от размера блока.

Попробуйте на своих текстурах

Все кодировщики работают локально в браузере. Загрузите PNG или JPEG, выберите формат и сравните результат.