Аппаратное сжатие текстур: AFRC, PVRIC4 и Metal
Сравнение ARM AFRC, Apple Metal Lossy и ImgTec PVRIC4 — трёх аппаратных кодеков сжатия текстур. Бенчмарки RMSE, примеры кода на Metal и Vulkan.
Перевод статьи Hardware Image Compression Игнасио Кастаньо (Ignacio Castaño). Оригинал опубликован в марте 2026 года.
Одним из поводов для разочарования в области аппаратных форматов изображений всегда было медленное развитие. Разработчики обычно не решались поставлять текстуры в новом формате, пока он не становился повсеместно доступным — поддерживался большинством целевого железа и всеми вендорами без исключения. Сегодня три вендора предлагают собственные форматы аппаратного сжатия текстур на лету: ARM AFRC, ImgTec PVRIC4 и Apple Metal Lossy. Игнасио Кастаньо исследовал все три и сравнил их с программным сжатием реального времени Spark от NVIDIA.
Ключевые выводы
— ARM AFRC (Pixel 8, Mali-G715) — явный победитель: по метрике RMSE значительно обходит Spark и других конкурентов во всех форматах.
— Apple Metal Lossy (A15/M2+) — коэффициент сжатия 1:2, API минималистичен: один флаг в дескрипторе текстуры.
— ImgTec PVRIC4 (Pixel 10) разочаровал: драйвер игнорирует запрошенный bitrate, качество хуже Spark для R и RG форматов.
— Vulkan-расширение VK_EXT_image_compression_control унифицирует доступ к аппаратному сжатию через параметр BPC (bits per component).
— Аппаратное сжатие — перспективная альтернатива программному, но пока ограничено современными топовыми устройствами.
Разработчики обычно не решались поставлять текстуры в новом формате, пока он не становился широко доступным — то есть поддерживался большинством целевого железа и всеми вендорами. Например, хотя ATI представила форматы 3Dc в 2004 году вместе с Radeon X800 (R420) и открыла их через расширения D3D9, их использование не стало распространённым, когда Direct3D 10 стандартизировал их как BC4 и BC5 в 2007 году. Массовое применение началось лишь тогда, когда Direct3D 10 стал минимальным требованием к железу.
Crysis стала первой крупной игрой, поставляемой с BC5-текстурами, но большинство игр ещё долгие годы не решались устанавливать такое жёсткое требование к железу. Чтобы избежать задержек с принятием, форматы BC6 и BC7 разрабатывались совместно ATI и NVIDIA для Direct3D 11.
Именно поэтому сжатие текстур в реальном времени так интересно: когда кодировщик работает в реальном времени, внедрять новые аппаратные форматы значительно проще — не нужно ждать, пока будет подготовлен контент, целенаправленно созданный под них. Аппаратное сжатие устраняет эту проблему принятия: детали форматов не документируются, их использование абсолютно прозрачно — приложению не нужно явно указывать эти форматы, драйвер сжимает текстуры динамически в процессе рендеринга и загрузки изображений.
Apple представила сжатие текстур с потерями в чипах A15 и M2 (оба используют одно поколение GPU). Оно обеспечивает коэффициент сжатия 1:2. Включить его на удивление просто — API минималистичен. Свойство compressionType дескриптора MTLTextureDescriptor принимает значение из перечисления MTLTextureCompressionType, и установка MTLTextureCompressionTypeLossy зачастую является единственным необходимым изменением:
Согласно таблицам возможностей Metal Feature Set Tables, все обычные пиксельные форматы поддерживают сжатие с потерями — включая 10-битные и форматы с плавающей точкой. Тестирование это подтвердило, однако основное внимание сосредоточено на форматах R8, RG8 и RGBA8.
Внутренний алгоритм Apple не задокументирован. По результатам реверс-инжиниринга lossy-форматов используется размер блока 8×4 пикселя — и они напоминают некоторые особенности форматов ETC и EAC. Несмотря на заявленное сжатие 1:2, на практике для каждого блока выделяется один байт метаданных, так что реальное потребление памяти чуть выше заявленного.
Для форматов R и RG Metal Lossy показывает результаты лучше кодеков Spark EAC, но хуже BC4 и BC5. Результаты RMSE (Root Mean Square Error — среднеквадратичная ошибка; меньше = лучше):
Формат R:
| Метрика | Metal Lossy (1:2) | BC4 Medium (1:2) | BC4 High (1:2) | EAC_RG Low (1:2) | EAC_RG Medium (1:2) | EAC_RG High (1:2) |
|---|---|---|---|---|---|---|
| RMSE | 1,8579 | 1,8469 | 1,7149 | 2,3399 | 2,2922 | 1,8636 |
Формат RG:
| Метрика | Metal Lossy (1:2) | BC5 Medium (1:2) | BC5 High (1:2) | EAC_RG Low (1:2) | EAC_RG Medium (1:2) | EAC_RG High (1:2) |
|---|---|---|---|---|---|---|
| RMSE | 3,1757 | 3,3099 | 3,0442 | 4,2261 | 4,1592 | 3,3601 |
Прямое сравнение lossy RGBA8 с форматами Spark некорректно из-за разных коэффициентов сжатия: Metal Lossy поддерживает только 1:2, тогда как форматы Spark RGB(A) — 1:4. Тем не менее, для полноты картины:
Формат RGBA:
| Метрика | Metal Lossy (1:2) | ASTC 4×4 Low (1:4) | ASTC 4×4 Medium (1:4) | ASTC 4×4 High (1:4) | BC7 Low (1:4) | BC7 Medium (1:4) | BC7 High (1:4) |
|---|---|---|---|---|---|---|---|
| RMSE | 1,4947 | 6,2994 | 5,9686 | 5,3637 | 5,7213 | 5,3585 | 4,2136 |
С точки зрения производительности lossy-форматы показывают себя отлично и при достаточно большом размере текстуры упираются в пропускную способность памяти. Результаты на M4 Pro (16 ядер GPU) в МПикс/сек:
| Метод | 4096 | 2048 | 1024 | 512 | 256 |
|---|---|---|---|---|---|
| Uncompressed (blit) | 41 618 | 26 680 | 43 749 | 70 111 | 44 939 |
| Metal Lossy (blit) | 41 807 | 40 847 | 43 100 | 69 873 | 48 729 |
| BC7 High (GPU) | 35 563 | 42 230 | 37 082 | 34 224 | 10 985 |
Обратите внимание, что пропускная способность стандартных блитов остаётся достаточно стабильной вне зависимости от размера текстуры. Кодеки Spark, напротив, имеют фиксированные накладные расходы, которые становятся заметнее при уменьшении размеров текстур. Интересен скачок скорости блитов при 512×512 — у него пока нет объяснения. Также стоит учесть, что кодеки Spark требуют дополнительного копирования из буфера вывода кодека в финальную сжатую текстуру — это дополнительные накладные расходы, которых можно было бы избежать, если бы Metal поддерживал запись в блочно-сжатые текстуры, как это делает Vulkan.
В Vulkan расширение VK_EXT_image_compression_control даёт приложениям возможность запрашивать сжатие изображений с фиксированной скоростью. Расширение уже доступно на флагманских устройствах от ARM и Imagination. Включение потерявого сжатия в Vulkan несколько многословнее, чем в Metal, но на практике ненамного сложнее: нужно лишь расширить структуру VkImageCreateInfo, добавив в цепочку VkImageCompressionControlEXT.
Можно использовать флаг VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT, чтобы позволить реализации самой выбрать параметры сжатия:
Альтернативно можно явно указать флаги фиксированной скорости для управления допустимыми коэффициентами сжатия:
BPC (bits per component — бит на компонент) — несколько нестандартная единица, но она позволяет задавать коэффициент сжатия единообразно вне зависимости от количества каналов. Для справки, BPC существующих форматов блочного сжатия GPU:
| Формат | Каналы | Размер на пиксель | Размер на канал |
|---|---|---|---|
| BC1 | RGB | 4 bpp | ~1,33 bpc |
| BC4 | R | 4 bpp | 4 bpc |
| BC5 | RG | 8 bpp | 4 bpc |
| BC7 | RGBA | 8 bpp | 2 bpc |
| ASTC 4×4 | RGBA | 8 bpp | 2 bpc |
| ASTC 6×6 | RGBA | ~3,55 bpp | ~1,18 bpc |
Расширение VK_EXT_image_compression_control также присутствует в некоторых драйверах AMD и Qualcomm, однако, насколько известно, ни один из этих вендоров не поддерживает аппаратное сжатие изображений с фиксированной скоростью. В случае AMD расширение присутствует в драйвере RADV, чтобы Proton мог отключать потерявое сжатие фреймбуфера в некоторых играх, где оно вызывало проблемы с корректностью — через флаг VK_IMAGE_COMPRESSION_DISABLED_EXT.
ARM Fixed Rate Compression (AFRC) было анонсировано в 2021 году и впервые появилось в Mali-G510 в 2022-м, однако широкого распространения этот дизайн не получил. По-настоящему массовым AFRC стало с выпуском Mali-G715 и Mali-G615 в том же году. Тестирование проводилось на Pixel 8 с GPU Mali-G715.
Устройство сообщило поддержку следующих форматов сжатия с фиксированной скоростью (возможности значительно шире Metal Lossy):
| Формат | 2 bpc | 3 bpc | 4 bpc | 5 bpc |
|---|---|---|---|---|
| R8 | 2 bpp | 3 bpp | 4 bpp | — |
| RG8 | 4 bpp | 6 bpp | 8 bpp | — |
| RGB8 | 6 bpp | — | 12 bpp | 15 bpp |
| RGBA8 | 8 bpp | 12 bpp | 16 bpp | — |
В отличие от Metal Lossy, AFRC не использует дополнительных байт метаданных — все управляющие биты находятся внутри самого блока. Изображение делится на блоки 8×8 пикселей, в ряде случаев разбиваемые на подблоки меньшего размера. Размер каждого блока 8×8 в байтах:
| Формат | 2 bpc | 3 bpc | 4 bpc | 5 bpc |
|---|---|---|---|---|
| R8 | 16 | 24 | 32 | — |
| RG8 | 32 | 48 | 64 | — |
| RGB8 | 64 | — | 96 | 128 |
| RGBA8 | 64 | 96 | 128 | — |
По результатам реверс-инжиниринга удалось установить: AFRC представляет цвета с использованием преобразования YCoCg, а представление пикселей напоминает вейвлет Хаара. Для каждого подблока 4×4 используется 16 коэффициентов, квантование которых зависит от режима. Форматы RGB и RGBA принципиально одинаковы — флаг в заголовке просто указывает, присутствует ли альфа-канал или блок полностью непрозрачен.
Качество AFRC впечатляет. В отличие от Metal, здесь можно сделать прямое сравнение с ASTC в реальном времени, поскольку Mali поддерживает сжатие 1:4:
Формат R:
| Метрика | AFRC (1:2) | EAC_R Low (1:2) | EAC_R Medium (1:2) | EAC_R High (1:2) |
|---|---|---|---|---|
| RMSE | 1,4937 | 2,3399 | 2,2922 | 1,8636 |
Формат RG:
| Метрика | AFRC (1:2) | EAC_RG Low (1:2) | EAC_RG Medium (1:2) | EAC_RG High (1:2) |
|---|---|---|---|---|
| RMSE | 2,2079 | 4,2261 | 4,1592 | 3,3601 |
Формат RGBA:
| Метрика | AFRC (1:2) | AFRC (1:4) | ASTC 4×4 Low (1:4) | ASTC 4×4 Medium (1:4) | ASTC 4×4 High (1:4) |
|---|---|---|---|---|---|
| RMSE | 0,6679 | 3,4184 | 6,2994 | 5,9686 | 5,3637 |
Во всех случаях RMSE значительно ниже, то есть AFRC превосходит Spark ASTC при нацеливании на ASTC с кодировщиком реального времени. Тем не менее есть несколько случаев, когда Spark даёт более высокое качество: на очень гладких изображениях сжатие AFRC приводит к заметным паттернам дизеринга, раскрывающим границы блоков. Это особенно заметно при использовании AFRC в качестве текстуры с увеличением, тогда как для сжатия фреймбуфера — основного сценария применения — такие паттерны практически незаметны.
Включение AFRC не вызывает значительных накладных расходов по сравнению с несжатыми загрузками текстур, за исключением некоторых размеров. Результаты на Pixel 8 в МПикс/сек:
| Метод | 4096 | 2048 | 1024 | 512 | 256 |
|---|---|---|---|---|---|
| Uncompressed | 4 961 | 3 951 | 3 063 | 2 290 | 2 337 |
| AFRC 4 bpc | 5 508 | 3 792 | 1 771 | 2 341 | 2 318 |
| AFRC 2 bpc | 5 041 | 4 433 | 2 556 | 2 267 | 2 332 |
| Spark ASTC Q0 | 4 810 | 4 207 | 2 503 | 3 662 | 2 259 |
| Spark ASTC Q2 | 4 481 | 3 715 | 2 319 | 2 950 | 1 903 |
Пропускная способность здесь масштабируется с размером текстуры, а не остаётся стабильной. Накладные расходы одинаково влияют на блиты и compute-шейдеры Spark. В отличие от секции Metal, где lossy-блиты явно доминировали над Spark на малых размерах, здесь картина смешанная: Spark вплотную приближается к AFRC или превосходит его. Это показывает, что кодирование текстур в реальном времени вполне конкурентоспособно с аппаратным сжатием. Абсолютные числа здесь значительно ниже, чем на M4 Pro, — это принципиально разные классы устройств.
ImgTec впервые анонсировала поддержку PVRIC4 ещё в 2018 году для GPU серии Series 6, однако протестировать её удалось только с выходом Pixel 10 на чипе Series D. Первоначальное объявление намекало на то, что, как и Metal Lossy, PVRIC4 поддерживает только 50%-е сжатие, но расширение декларирует более широкий спектр опций:
| Формат | 1 bpc | 2 bpc | 3 bpc | 4 bpc |
|---|---|---|---|---|
| R8 | 1 bpp | 2 bpp | 3 bpp | 4 bpp |
| RG8 | 2 bpp | 4 bpp | 6 bpp | 8 bpp |
| RGBA8 | 4 bpp | 8 bpp | 12 bpp | 16 bpp |
К большому удивлению, качество вывода оказалось одинаковым вне зависимости от BPC. Дальнейшее расследование показало: драйвер игнорирует запрошенный BPC и всегда использует 4 bpc (сжатие 1:2). Формат блоков PVRIC4 — наиболее сложный из всех трёх вендоров: размер блоков составляет 16×16 пикселей, и, как в Metal Lossy, присутствует один байт метаданных на блок. Сделать реверс-инжиниринг практически не удалось.
Качество оказалось разочаровывающим. Для форматов R и RG Spark фактически превосходит PVRIC4 при нацеливании на стандартные форматы блочного сжатия, поддерживаемые этим железом:
Формат R:
| Метрика | PVRIC4 (1:2) | BC4 Medium (1:2) | BC4 High (1:2) | EAC_R Low (1:2) | EAC_R Medium (1:2) | EAC_R High (1:2) |
|---|---|---|---|---|---|---|
| RMSE | 3,4346 | 1,8469 | 1,7149 | 2,3399 | 2,2922 | 1,8636 |
Формат RG:
| Метрика | PVRIC4 (1:2) | BC5 Medium (1:2) | BC5 High (1:2) | EAC_RG Low (1:2) | EAC_RG Medium (1:2) | EAC_RG High (1:2) |
|---|---|---|---|---|---|---|
| RMSE | 5,4392 | 3,3099 | 3,0442 | 4,2261 | 4,1592 | 3,3601 |
Для RGBA прямое сравнение невозможно из-за разных коэффициентов сжатия, но качество также значительно хуже других вендоров:
Формат RGBA:
| Метрика | PVRIC4 (1:2) | ASTC 4×4 Low (1:4) | ASTC 4×4 Medium (1:4) | ASTC 4×4 High (1:4) |
|---|---|---|---|---|
| RMSE | 2,3160 | 6,2994 | 5,9686 | 5,3637 |
| Метод | 4096 | 2048 | 1024 | 512 | 256 |
|---|---|---|---|---|---|
| Uncompressed | 2 299 | 2 629 | 2 643 | 1 909 | 1 178 |
| PVRIC4 4 bpc | 2 582 | 2 972 | 3 851 | 2 877 | 1 102 |
| Spark ASTC Q0 | 3 327 | 3 509 | 3 097 | 2 051 | 911 |
| Spark ASTC Q2 | 3 002 | 2 759 | 2 498 | 1 485 | 634 |
Кривая пропускной способности на этом устройстве существенно отличается от Pixel 8: пик приходится на размеры 1024–2048, а не монотонно возрастает с размером. На больших размерах пропускная способность Spark фактически выше, чем у несжатых загрузок текстур. Это характерно для устройств, ограниченных пропускной способностью памяти: обычный блит должен прочитать всё входное изображение и записать обратно тот же объём данных, тогда как Spark записывает лишь 1/4 входных данных. Экономия на записи зачастую перекрывает вычислительные затраты кодирования.
Комментарий PowerVR Dev (ImgTec)
PVRIC4 поддерживает только сжатие 1:2. TFBC (доступен исключительно в чипах Rogue XE) поддерживал соотношения 1:2, 1:4 и 1:3. Дизайн расширения был спорным — многим не нравилась многословность выбора BPC, поэтому было решено экспонировать все BPC в рамках поддерживаемых соотношений и повышать их до поддерживаемого ratio. Это считалось допустимым, поскольку качество не деградирует относительно запроса приложения, а приложение всё равно обязано запрашивать реальный размер изображения, который не гарантирует уменьшения.
Spark — библиотека NVIDIA для программного сжатия текстур в реальном времени. Работает на GPU как compute shader, поддерживает форматы BC1–BC7 и ASTC. В тесте выступает базовой точкой сравнения как лучшее доступное программное сжатие без специализированного железа.
Spark показывает стабильные результаты по форматам, но требует явного вызова compute-прохода — это дополнительный этап в пайплайне. Аппаратное сжатие, напротив, встроено в рендеринг и не требует дополнительного кода. Отдельный плюс Spark — предсказуемое и согласованное поведение на всех устройствах, что важно, если унифицированный вывод критичен для вашего сценария. Ни один из форматов аппаратного сжатия пока не доступен через WebGPU — если это изменится, расширить spark.js для их поддержки будет несложно.
ARM AFRC — явный победитель. Это не только превосходит программные реализации вроде Spark, но и обходит все остальные форматы по всем метрикам. Итоговая таблица RMSE по всем протестированным форматам:
| Формат | RMSE 1:2 | RMSE 1:4 |
|---|---|---|
| R8 Metal Lossy | 1,8579 | — |
| R8 AFRC | 1,4937 | — |
| R8 PVRIC4 | 3,4346 | — |
| Spark BC4 | 1,7149 | — |
| RG8 Metal Lossy | 3,1757 | — |
| RG8 AFRC | 2,2079 | — |
| RG8 PVRIC4 | 5,4392 | — |
| Spark BC5 | 3,0442 | — |
| RGBA8 Metal Lossy | 1,4947 | — |
| RGBA8 AFRC | 0,6679 | 3,4184 |
| RGBA8 PVRIC4 | 2,3160 | — |
| Spark BC7 | — | 4,2136 |
Стоит оговориться: результаты PVRIC4 могут не отражать полного потенциала железа — драйвер игнорирует запрошенный коэффициент сжатия и всегда использует 1:2. Возможно, эти результаты удастся пересмотреть после исправления проблемы.
Аппаратное сжатие — убедительная альтернатива программному. Главная оговорка — оно сейчас ограничено современными топовыми устройствами, которые как раз и располагают наибольшим объёмом памяти и пропускной способностью.
Даже когда нативное аппаратное сжатие доступно, есть веские причины продолжать использовать Spark. Вывод аппаратного сжатия различается у разных вендоров, и в некоторых случаях — как мы видели с PVRIC4 — качество уступает кодировщику реального времени. Если для вашего сценария критичен единообразный и предсказуемый вывод на всех вендорах, Spark остаётся правильным выбором.
Частые вопросы
Чем аппаратное сжатие текстур отличается от форматов BC/ASTC?
Форматы BC и ASTC — это сжатие, которое создаётся заранее (офлайн) и хранится на диске. GPU только декодирует их при чтении. Аппаратное сжатие (AFRC, PVRIC4, Metal Lossy) работает на лету: GPU сжимает изображение в момент записи в память и распаковывает при чтении. Это сокращает трафик памяти без участия CPU и без предварительной обработки.
Что такое BPC и как его интерпретировать?
BPC (bits per component) — количество бит на компонент цвета. Исходный RGBA8 — это 8 BPC. Сжатие 1:2 даёт 4 BPC. Удобная метрика, потому что не зависит от количества каналов: 4 BPC означает «вдвое меньше данных» как для R, так и для RGBA.
Можно ли использовать AFRC в Android-приложении?
Да, через Vulkan и расширение VK_EXT_image_compression_control. Нужно проверить поддержку расширения на устройстве, добавить структуру VkImageCompressionControlEXT при создании изображения и указать желаемый BPC. На Mali-G715 (Pixel 8) это работает корректно.
Почему PVRIC4 игнорирует запрошенный BPC?
Как разъяснил представитель PowerVR: PVRIC4 аппаратно поддерживает только 1:2. TFBC (чипы Rogue XE) поддерживал 1:2, 1:4 и 1:3, но сейчас недоступен. Драйвер экспонирует все значения BPC, но промоутирует запрошенный BPC к фактически поддерживаемому соотношению. Качество при этом не деградирует относительно запроса — просто всегда применяется 1:2.
Что такое Spark от NVIDIA?
Spark — библиотека NVIDIA для сжатия текстур в реальном времени, работающая как GPU compute shader. Поддерживает форматы BC1–BC7 и ASTC, доступна кроссплатформенно. Выступает эталоном для сравнения с аппаратным сжатием: это лучшее доступное программное решение без специализированного железа.