RygsDXTc и сжатие текстур на лету

Очередная часть Марлезонского балета с компрессией текстур. Я долго думал, делать или не делать в движке сжатие в DXT, и ответ пришел в виде проекта RygsDXTc, быстрого компрессора DXT1/DXT5. Причем написанного не кем-нибудь, а самим Фабианом Гизеном aka ryg из легендарного Farbrausch!

Залипнув на вечер, я портировал на D эту тысячу строк зубодробительного C-кода, под завязку набитого черной магией целочисленной арифметики, и вот результат — в Dagon теперь можно простым переключением свойства отправлять текстуры в видеопамять в сжатом виде:

aTexture = addTextureAsset("data/input.png");
aTexture.compress = true;
aTexture.generateMipmaps = true;

При включенном generateMipmaps перед сжатием будут сгенерированы мип-уровни. Полноценно поддерживаются NPOT-текстуры. При этом оверхед на все про все ничтожный — libktx при транскодировании из Basis Universal и то срабатывает дольше.

Если задать перед загрузкой aTexture.persistent = true, то ассет сохранит исходный TextureBuffer в памяти, и вы сможете сохранить сжатую текстуру в DDS:

auto outputStrm = game.vfs.stdfs.openForOutput("out.dds");
saveDDS(outputStrm, &aTexture.buffer);
Delete(outputStrm);

Сжатие текстур, часть V. ASTC и Basis Universal

Продолжение серии постов о сжатых текстурных форматах. Предыдущие части: часть I, часть II, часть III, часть IV.

Вот мы и подошли к state of the art текстурного сжатия по состоянию на 2025 год.

ASTC

Adaptive Scalable Texture Compression

Современный формат, созданный с учетом особенностей мобильных платформ. На сегодняшний день крайне мало распространен. Программно поддерживается в Vulkan и новейших версиях OpenGL ES, но аппаратно — далеко не всеми видеокартами. В десктопном OpenGL поддержки нет и, судя по всему, не предвидится (но это не проблема — см. ниже).

В отличие от S3TC, BPTC и RGTC, формат работает с блоками от 4×4 до 12×12. Размер блока ASTC всегда составляет 16 байт, поэтому чем больше блок, тем выше степень сжатия, но ниже качество. Можно выбирать формат блока (например, 5×5, 6×6, 8×8 и т.д.) в зависимости от баланса между качеством и размером. ASTC поддерживает от 1 до 4 каналов.

ASTC поддерживает HDR и является на сегодняшний день единственным универсальным решением для сжатия HDR-текстур на мобильных GPU.

Basis Universal

Использовать ASTC напрямую непрактично, если вы хотите охватить широкий спектр платформ и конфигураций. Для этого была придумана особая система хранения сжатых текстур, которая позволяет приложениям на лету транскодировать их в формат, подходящий для каждой конкретной платформы — ASTC, BPTC, S3TC, RGTC, PVRTC и в несжатые форматы. Basis Universal охватывает как LDR, так и HDR-текстуры в рамках пяти режимов сжатия: ETC1S, UASTC LDR 4×4, UASTC HDR 4×4, UASTC HDR 6×6, UASTC HDR 6×6 intermediate.

ETC1S. Режим низкого и среднего качества, основанный на подмножестве ETC1. Поддерживает переменный баланс веса/качества (наподобие JPEG). Поддерживает альфа-канал. Этот вид текстур можно быстро транскодировать практически в любые другие сжатые форматы.

UASTC (Universal ASTC) LDR 4×4. Подмножество ASTC для стандартных LDR-текстур с 8 битами на канал. Транскодируется также очень эффективно, особенно в ASTC и BC7.

UASTC HDR 4×4. Режим для HDR-текстур, подмножество ASTC HDR 4×4 8bpp. Разработан для обеспечения высокого качества, эффективно транскодируется (с небольшими потерями) в BC6H. В ASTC HDR превращается вообще без какого-либо оверхеда и без потерь. Этот режим также можно транскодировать в различные несжатые HDR-форматы 32-64 bpp.

UASTC HDR 6×6. Режим HDR-текстур 3.56 bpp. Также на 100% эквивалентен ASTC.

UASTC HDR 6×6 Intermediate («GPU Photo»). Быстро транскодирутся в ASTC HDR 6×6, BC6H и различные несжатые HDR-форматы.

ETC1S и UASTC LDR 4×4 могут быть транскодированы в:

  • ASTC LDR 4×4 L/LA/RGB/RGBA 8bpp
  • BC1-5 RGB/RGBA/X/XY
  • BC7 RGB/RGBA
  • ETC1 RGB, ETC2 RGBA, ETC2 EAC R11/RG11
  • PVRTC1 4bpp RGB/RGBA, PVRTC2 RGB/RGBA
  • ATC RGB/RGBA, FXT1 RGB
  • Несжатые LDR-форматы.

UASTC HDR 4×4 и UASTC HDR 6×6 могут быть транскодированы в:

  • ASTC HDR 4×4 (8bpp, только UASTC HDR 4×4)
  • ASTC HDR 6×6 RGB (3.56bpp, ASTC HDR 6×6 или UASTC HDR 6×6 intermediate)
  • BC6H RGB (8bpp, UASTC HDR 4×4 или UASTC HDR 6×6)
  • Несжатые HDR-форматы.

KTX2 и суперкомпрессия

Basis Universal — это отлично, но как с ним работать и с удобством хранить? Для упрощения этой задачи Khronos Group предложили формат контейнера KTX2, развитие KTX. Если Basis Universal — это механизм сжатия, то KTX2 — чемодан, в который можно упаковать вообще все. Он поддерживает все существующие форматы сжатия, включая ASTC, UASTC, ETC1S, BC1-7, ETC, PVRTC, и сверх того позволяет сжимать текстуры при помощи lossless-алгоритмов (Zstandard, Zlib, BasisLZ). Несжатые форматы, определенные спецификацией Vulkan, также поддерживаются в полной мере. В KTX можно хранить не только 1D-, 2D- и 3D-текстуры, но и кубические карты, mip-уровни и массивы текстур.

Khronos предоставляет набор утилит KTX Software для конвертации изображений в KTX/KTX2, а также для валидации и анализа файлов. Плюс, разумеется, библиотека libktx для декодирования файлов в приложениях — она полностью берет на себя задачу по транскодированию, и на выходе вы получаете текстуру в нужном вам формате, которую остается лишь отправить в графический API.

Сжатие текстур, часть IV. Мобильные форматы: ETC и PVRTC

Продолжение серии постов о сжатых текстурных форматах. Предыдущие части: часть I, часть II, часть III.

Ситуация со сжатием текстур на мобильных платформах довольно запутанная, так как 3D-ускорители там существенно отличаются от десктопных. К сожалению, ни iOS, ни Android не поддерживают S3TC (и, тем более, BPTC). В мобильных системах используются свои специализированные форматы сжатия — ETC на Android и PVRTC на iOS.

ETC1 и ETC2

Ericsson Texture Compression / iPACKMAN

Формат сжатия от Ericsson. Поддерживается как в мобильных устройствах, так и в современных браузерах (кроме Firefox).

ETC1 поддерживается практически на всех android-устройствах и является стандартным форматом сжатия в OpenGL ES 2.0. Не поддерживает прозрачность. Блок 4×4 преобразуется в 64-битное представление. Блок разбивается на два субблока (4×2 или 2×4), им присваиваются базовые цвета — либо каждому RGB 4:4:4, либо одному 5:5:5, а второму смещение 3:3:3 относительно первого. Пиксели в субблоке представляются в виде суммы базового цвета и одного из четырех смещений — так называемых модификаторов: pixelColor = baseColor + RGB(modifier, modifier, modifier). Модификаторы, определяемые спецификацией, представляют собой 4×8 таблицу констант — целочисленных значений со знаком. Индексы ряда в таблице [0, 7] хранятся как два 3-битных значения (по одному на субблок), индексы столбца [0, 3] — как 16 2-битных значений (по одному на каждый пиксель). Оставшиеся два бита определяют ориентацию субблоков (flip-бит) и тип хранения базовых цветов (diff-бит). Результат суммирования нормализуется в 8 бит на канал.

ETC2 является стандартным форматом сжатия в OpenGL ES 3.0. Поддерживает прозрачность. ETC2 — это обратно-совместимое надмножество ETC1. Альфа-канал кодируется по такому же принципу, что и цвет: значение прозрачности для пикселя — это сумма базовой альфы и модификатора из таблицы констант 8×16. Блоку 4×4 присваиваются дополнительные 64 бита: 8-битное базовое значение альфа, 4-битный индекс ряда в таблице модификаторов, 4-битный множитель и 16 3-битных индексов столбцов.

Также в ETC2 есть отдельный режим punch-through (GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2), аналогичный ETC1, но в котором diff-бит заменяется битом прозрачности — таким образом, блок интерпретируется либо как полностью прозрачный, либо как полностью непрозрачный.

PVRTC1 и PVRTC2

PowerVR Texture Compression

Используется в видеоускорителях PowerVR (iPhone и другие устройства Apple). Основной целью разработчики формата поставили устранение разрывов цвета вдоль границ блоков, которая присуща S3TC. PVRTC поддерживает альфа-канал.

Имеет две разновидности — 4bpp и 2bpp. На блок всегда выделяется 64 бита информации, поэтому в режиме 4bpp блок имеет размер 4×4 пикселя, в режиме 2bpp — 8×4. В некоторых аппаратных реализациях блоки расположены в памяти не в порядке сканирования (снизу вверх, слева направо), а в Z-последовательности для увеличения пространственной локальности и, как следствие, более эффективного кэширования.

В каждом блоке хранится шесть переменных: для PVRTC1 — данные модуляции (32 бит), флаг punch-through alpha (1 бит), цвет A (15 бит), флаг прозрачности цвета A (1 бит), цвет B (14 бит) и флаг прозрачности цвета B (1 бит). Для PVRTC2 — данные модуляции (32 бит), флаг модуляции (1 бит), цвет B (15 бит), флаг hard transition (1 бит), цвет A (15 бит) и флаг прозрачности (1 бит). Битовая глубина значений A и B задается по-разному в зависимости от того, есть ли альфа-канал: либо RGB 5:5:4(5), либо RGBA 3:4:4:3(4), в скобках указан вариант для 15-битного цвета. Дополнительный бит прозрачности определяет наличие у цвета альфа-канала. В PVRTC его можно задавать независимо для A и B, когда как в PVRTC2 бит прозрачности только один, и оба цвета должны быть в одинаковом формате — либо RGB, либо RGBA. Цвет пикселя вычисляется билинейной интерполяцией цветов A и B. Каждому пикселю блока сопоставляется, в зависимости от разновидности формата, 2-битное или 1-битное значение модуляции, кодирующее вес интерполяции между A и B.

PVRTC2 расширяет алгоритм поддержкой четырех разных режимов блока, задаваемых флагами hard transition в сочетании с флагом модуляции: стандартная билинейная, punch-through alpha, резкий переход, локальная палитра.

Сжатие текстур, часть III. BPTC

Продолжение серии постов о сжатых текстурных форматах (часть I, часть II).

BPTC (BC6, BC7)

Block Partition Texture Compression

BPTC является частью ядра OpenGL начиная с версии 4.2. Обеспечивает лучшее качество по сравнению с семейством S3TC, при этом у него хорошая поддержка на десктопных платформах. У формата есть есть две разновидности: BC6 и BC7 (в обозначении DXGI).

BC7 используется для сжатия беззнаковых нормализованных изображений (то есть, обычных изображений глубиной цвета 8 бит на канал). Блок 4×4 преобразуется в 128 бит. Принцип сжатия во многом аналогичен S3TC — хранятся начальный и конечный пороговые цвета (endpoints), вместо пикселей сохраняются индексы интерполированных значений между ними. Отличие в том, что BPTC может хранить отдельные градиенты для каждого канала, подобно тому, как DXT5 разделяет цвет и альфу. BPTC поддерживает гибкий механизм группировки каналов: каждый блок может использовать один из 7 разных режимов группировки.

BC7 кодирует изображения с альфа-каналом. У него есть две версии — в линейном (GL_COMPRESSED_RGBA_BPTC_UNORM_ARB) и гамма-пространстве (GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB). Они математически эквивалентны, различие существует лишь для удобства интерпретации данных в приложениях (то есть, сэмплы из sRGB-текстур нужно, как обычно, переводить в линейное пространство перед тем, как использовать в каких-либо вычислениях).

BC6 (BPTC_FLOAT) — самый распространенный на сегодняшний день (и единственный на большинстве платформ) формат сжатия для HDR-изображений. Формат кодирует числа с плавающей запятой и не поддерживает альфа-канал. Первый endpoint хранится с высокой точностью, второй представляет собой смещение относительно первого, хранящееся с низкой точностью.

BC6 существует в двух версиях — COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB и COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, которые, соответственно, кодируют знаковые и беззнаковые значения.

Сжатие текстур, часть II. RGTC

Продолжение серии постов о сжатых текстурных форматах. Первая часть тут.

RGTC (BC4, BC5)

Red Green Texture Compression

Форматы сжатия для 1- и 2-канальных изображений — условно «красных» и «красно-зеленых». Разработаны ATI (ныне AMD), поддерживаются всеми современными десктопными видеокартами.

BC4 (также известный как RGTC1, ATI1 и 3Dc+) предназначен для хранения монохромных изображений (не обязательно красных, разумеется). Формат использует 64 бита на блок 4×4. Endpoint’ы хранятся в виде 8-битных значений, из них создается 6 промежуточных значений. Индексы пикселей 3-битные. Преимущество BC4 — значительно более высокое качество при хранении монохромных текстур, чем при использовании BC1. Это делает формат самым подходящим выбором для карт высот и различных нецветовых данных, таких как шероховатость и металличность. Качество градиентов почти неотличимо на глаз от несжатого оригинала.

BC5 (также известный как RGTC2, ATI2 и 3Dc) хранит двухканальные изображения, условно называемые «красно-зелеными». Принцип сжатия аналогичен BC4, только в данном случае каждый блок 4×4 описывается двумя каналами по 64 бита каждый, при этом красный и зеленый каналы сжимаются независимо друг от друга. В BC5 удобно хранить, например, совмещенные текстуры шероховатости и металличности.

Стандарт RGTC определяет текстуры со знаком и беззнаковые. Они полностью идентичны, разница лишь в том, что формат со знаком кодирует значения от -128 до 127, а не от 0 до 255. Для формата со знаком действует правило: если первый endpoint равен -127, второй не должен быть -128.

Сжатие текстур, часть I. S3TC

Я как-то обещал написать пост с подробным разбором всех форматов сжатия текстур и соответствующего инструментария — и вот, наконец, начинаю публиковать частями. Сегодня рассмотрим один из старейших и самых популярных — S3TC (DXTn).

Тот или иной способ сжатия сегодня используется почти во всех стандартных графических форматах. Однако такие форматы, как PNG или JPEG, хотя и сжимают весьма эффективно, для текстур в видеопамяти не годятся — они предназначены для хранения на диске и передачи по сети. В качестве текстур их можно использовать только после декомпрессии. Для хранения текстур в сжатом виде были созданы специализированные форматы, которые позволяют считывать пиксели на лету, без полной декомпрессии данных.

(далее…)

Texture Tools Exporter

Отличный бесплатный конвертер текстур от NVIDIA, о котором я узнал почему-то только недавно. Поддерживает системы сжатия BC1 (S3TC/DXT1), BC2 (S3TC/DXT3), BC3 (S3TC/DXT5), BC4/5 (RGTC), BC6/7 (BPTC), ASTC, а также несжатые 8-битные целочисленные форматы и 16- и 32-битные с плавающей запятой. Можно экспортировать как 2D текстуры, так и кубические карты. Список поддерживаемых контейнеров также внушителен: DDS, KTX2, OpenEXR, HDR и все обычные — PNG, JPEG, BMP, TGA, PPM и др.

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