Аппаратное сжатие текстур: AFRC, PVRIC4 и Metal

Сравнение ARM AFRC, Apple Metal Lossy и ImgTec PVRIC4 — трёх аппаратных кодеков сжатия текстур. Бенчмарки RMSE, примеры кода на Metal и Vulkan.

Обложка: Аппаратное сжатие текстур: AFRC, PVRIC4 и Metal

Перевод статьи 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 Metal: Lossy-сжатие

Apple представила сжатие текстур с потерями в чипах A15 и M2 (оба используют одно поколение GPU). Оно обеспечивает коэффициент сжатия 1:2. Включить его на удивление просто — API минималистичен. Свойство compressionType дескриптора MTLTextureDescriptor принимает значение из перечисления MTLTextureCompressionType, и установка MTLTextureCompressionTypeLossy зачастую является единственным необходимым изменением:

			MTLTextureDescriptor *descriptor = [MTLTextureDescriptor
    texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
    width:width
    height:height];
descriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
descriptor.storageMode = MTLStorageModePrivate;
descriptor.compressionType = MTLTextureCompressionTypeLossy;

id<MTLTexture> texture = [device newTextureWithDescriptor:descriptor];
		

Согласно таблицам возможностей Metal Feature Set Tables, все обычные пиксельные форматы поддерживают сжатие с потерями — включая 10-битные и форматы с плавающей точкой. Тестирование это подтвердило, однако основное внимание сосредоточено на форматах R8, RG8 и RGBA8.

Внутренний алгоритм Apple не задокументирован. По результатам реверс-инжиниринга lossy-форматов используется размер блока 8×4 пикселя — и они напоминают некоторые особенности форматов ETC и EAC. Несмотря на заявленное сжатие 1:2, на практике для каждого блока выделяется один байт метаданных, так что реальное потребление памяти чуть выше заявленного.

Качество на M4 Pro

Для форматов 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)
RMSE1,85791,84691,71492,33992,29221,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)
RMSE3,17573,30993,04424,22614,15923,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)
RMSE1,49476,29945,96865,36375,72135,35854,2136

Производительность на M4 Pro

С точки зрения производительности lossy-форматы показывают себя отлично и при достаточно большом размере текстуры упираются в пропускную способность памяти. Результаты на M4 Pro (16 ядер GPU) в МПикс/сек:

Метод409620481024512256
Uncompressed (blit)41 61826 68043 74970 11144 939
Metal Lossy (blit)41 80740 84743 10069 87348 729
BC7 High (GPU)35 56342 23037 08234 22410 985

Обратите внимание, что пропускная способность стандартных блитов остаётся достаточно стабильной вне зависимости от размера текстуры. Кодеки Spark, напротив, имеют фиксированные накладные расходы, которые становятся заметнее при уменьшении размеров текстур. Интересен скачок скорости блитов при 512×512 — у него пока нет объяснения. Также стоит учесть, что кодеки Spark требуют дополнительного копирования из буфера вывода кодека в финальную сжатую текстуру — это дополнительные накладные расходы, которых можно было бы избежать, если бы Metal поддерживал запись в блочно-сжатые текстуры, как это делает Vulkan.

Vulkan: расширение VK_EXT_image_compression_control

В Vulkan расширение VK_EXT_image_compression_control даёт приложениям возможность запрашивать сжатие изображений с фиксированной скоростью. Расширение уже доступно на флагманских устройствах от ARM и Imagination. Включение потерявого сжатия в Vulkan несколько многословнее, чем в Metal, но на практике ненамного сложнее: нужно лишь расширить структуру VkImageCreateInfo, добавив в цепочку VkImageCompressionControlEXT.

Можно использовать флаг VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT, чтобы позволить реализации самой выбрать параметры сжатия:

			VkImageCompressionControlEXT compression_control = { 0 };
compression_control.sType = VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT;
compression_control.flags = VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT;
compression_control.pFixedRateFlags = nullptr;
		

Альтернативно можно явно указать флаги фиксированной скорости для управления допустимыми коэффициентами сжатия:

			VkFlags fixed_rate_flags = VK_IMAGE_COMPRESSION_FIXED_RATE_3BPC_BIT_EXT |
    VK_IMAGE_COMPRESSION_FIXED_RATE_4BPC_BIT_EXT;

compression_control.flags = VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT;
compression_control.pFixedRateFlags = &fixed_rate_flags;
		

BPC (bits per component — бит на компонент) — несколько нестандартная единица, но она позволяет задавать коэффициент сжатия единообразно вне зависимости от количества каналов. Для справки, BPC существующих форматов блочного сжатия GPU:

ФорматКаналыРазмер на пиксельРазмер на канал
BC1RGB4 bpp~1,33 bpc
BC4R4 bpp4 bpc
BC5RG8 bpp4 bpc
BC7RGBA8 bpp2 bpc
ASTC 4×4RGBA8 bpp2 bpc
ASTC 6×6RGBA~3,55 bpp~1,18 bpc

Расширение VK_EXT_image_compression_control также присутствует в некоторых драйверах AMD и Qualcomm, однако, насколько известно, ни один из этих вендоров не поддерживает аппаратное сжатие изображений с фиксированной скоростью. В случае AMD расширение присутствует в драйвере RADV, чтобы Proton мог отключать потерявое сжатие фреймбуфера в некоторых играх, где оно вызывало проблемы с корректностью — через флаг VK_IMAGE_COMPRESSION_DISABLED_EXT.

ARM AFRC: лучший результат среди всех

ARM Fixed Rate Compression (AFRC) было анонсировано в 2021 году и впервые появилось в Mali-G510 в 2022-м, однако широкого распространения этот дизайн не получил. По-настоящему массовым AFRC стало с выпуском Mali-G715 и Mali-G615 в том же году. Тестирование проводилось на Pixel 8 с GPU Mali-G715.

Устройство сообщило поддержку следующих форматов сжатия с фиксированной скоростью (возможности значительно шире Metal Lossy):

Формат2 bpc3 bpc4 bpc5 bpc
R82 bpp3 bpp4 bpp
RG84 bpp6 bpp8 bpp
RGB86 bpp12 bpp15 bpp
RGBA88 bpp12 bpp16 bpp

В отличие от Metal Lossy, AFRC не использует дополнительных байт метаданных — все управляющие биты находятся внутри самого блока. Изображение делится на блоки 8×8 пикселей, в ряде случаев разбиваемые на подблоки меньшего размера. Размер каждого блока 8×8 в байтах:

Формат2 bpc3 bpc4 bpc5 bpc
R8162432
RG8324864
RGB86496128
RGBA86496128

По результатам реверс-инжиниринга удалось установить: AFRC представляет цвета с использованием преобразования YCoCg, а представление пикселей напоминает вейвлет Хаара. Для каждого подблока 4×4 используется 16 коэффициентов, квантование которых зависит от режима. Форматы RGB и RGBA принципиально одинаковы — флаг в заголовке просто указывает, присутствует ли альфа-канал или блок полностью непрозрачен.

Качество AFRC

Качество 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)
RMSE1,49372,33992,29221,8636

Формат RG:

МетрикаAFRC (1:2)EAC_RG Low (1:2)EAC_RG Medium (1:2)EAC_RG High (1:2)
RMSE2,20794,22614,15923,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)
RMSE0,66793,41846,29945,96865,3637

Во всех случаях RMSE значительно ниже, то есть AFRC превосходит Spark ASTC при нацеливании на ASTC с кодировщиком реального времени. Тем не менее есть несколько случаев, когда Spark даёт более высокое качество: на очень гладких изображениях сжатие AFRC приводит к заметным паттернам дизеринга, раскрывающим границы блоков. Это особенно заметно при использовании AFRC в качестве текстуры с увеличением, тогда как для сжатия фреймбуфера — основного сценария применения — такие паттерны практически незаметны.

Сравнение AFRC с дизерингом и без: AFRC сохраняет детали значительно лучше конкурентов при том же bitrate
Сравнение AFRC с дизерингом и без: AFRC сохраняет детали значительно лучше конкурентов при том же bitrate
Производительность AFRC (Pixel 8)

Включение AFRC не вызывает значительных накладных расходов по сравнению с несжатыми загрузками текстур, за исключением некоторых размеров. Результаты на Pixel 8 в МПикс/сек:

Метод409620481024512256
Uncompressed4 9613 9513 0632 2902 337
AFRC 4 bpc5 5083 7921 7712 3412 318
AFRC 2 bpc5 0414 4332 5562 2672 332
Spark ASTC Q04 8104 2072 5033 6622 259
Spark ASTC Q24 4813 7152 3192 9501 903

Пропускная способность здесь масштабируется с размером текстуры, а не остаётся стабильной. Накладные расходы одинаково влияют на блиты и compute-шейдеры Spark. В отличие от секции Metal, где lossy-блиты явно доминировали над Spark на малых размерах, здесь картина смешанная: Spark вплотную приближается к AFRC или превосходит его. Это показывает, что кодирование текстур в реальном времени вполне конкурентоспособно с аппаратным сжатием. Абсолютные числа здесь значительно ниже, чем на M4 Pro, — это принципиально разные классы устройств.

ImgTec PVRIC4: разочарование от Pixel 10

ImgTec впервые анонсировала поддержку PVRIC4 ещё в 2018 году для GPU серии Series 6, однако протестировать её удалось только с выходом Pixel 10 на чипе Series D. Первоначальное объявление намекало на то, что, как и Metal Lossy, PVRIC4 поддерживает только 50%-е сжатие, но расширение декларирует более широкий спектр опций:

Формат1 bpc2 bpc3 bpc4 bpc
R81 bpp2 bpp3 bpp4 bpp
RG82 bpp4 bpp6 bpp8 bpp
RGBA84 bpp8 bpp12 bpp16 bpp

К большому удивлению, качество вывода оказалось одинаковым вне зависимости от BPC. Дальнейшее расследование показало: драйвер игнорирует запрошенный BPC и всегда использует 4 bpc (сжатие 1:2). Формат блоков PVRIC4 — наиболее сложный из всех трёх вендоров: размер блоков составляет 16×16 пикселей, и, как в Metal Lossy, присутствует один байт метаданных на блок. Сделать реверс-инжиниринг практически не удалось.

Качество PVRIC4

Качество оказалось разочаровывающим. Для форматов 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)
RMSE3,43461,84691,71492,33992,29221,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)
RMSE5,43923,30993,04424,22614,15923,3601

Для RGBA прямое сравнение невозможно из-за разных коэффициентов сжатия, но качество также значительно хуже других вендоров:

Формат RGBA:

МетрикаPVRIC4 (1:2)ASTC 4×4 Low (1:4)ASTC 4×4 Medium (1:4)ASTC 4×4 High (1:4)
RMSE2,31606,29945,96865,3637

Производительность PVRIC4 (Pixel 10)

Метод409620481024512256
Uncompressed2 2992 6292 6431 9091 178
PVRIC4 4 bpc2 5822 9723 8512 8771 102
Spark ASTC Q03 3273 5093 0972 051911
Spark ASTC Q23 0022 7592 4981 485634

Кривая пропускной способности на этом устройстве существенно отличается от 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 ASTC с дизерингом и без. Программный Spark показывает хорошие результаты, но AFRC его превосходит
Сравнение Spark ASTC с дизерингом и без. Программный Spark показывает хорошие результаты, но AFRC его превосходит
Spark: программная точка отсчёта

Spark — библиотека NVIDIA для программного сжатия текстур в реальном времени. Работает на GPU как compute shader, поддерживает форматы BC1–BC7 и ASTC. В тесте выступает базовой точкой сравнения как лучшее доступное программное сжатие без специализированного железа.

Spark показывает стабильные результаты по форматам, но требует явного вызова compute-прохода — это дополнительный этап в пайплайне. Аппаратное сжатие, напротив, встроено в рендеринг и не требует дополнительного кода. Отдельный плюс Spark — предсказуемое и согласованное поведение на всех устройствах, что важно, если унифицированный вывод критичен для вашего сценария. Ни один из форматов аппаратного сжатия пока не доступен через WebGPU — если это изменится, расширить spark.js для их поддержки будет несложно.

Итоги: кто победил

ARM AFRC — явный победитель. Это не только превосходит программные реализации вроде Spark, но и обходит все остальные форматы по всем метрикам. Итоговая таблица RMSE по всем протестированным форматам:

ФорматRMSE 1:2RMSE 1:4
R8 Metal Lossy1,8579
R8 AFRC1,4937
R8 PVRIC43,4346
Spark BC41,7149
RG8 Metal Lossy3,1757
RG8 AFRC2,2079
RG8 PVRIC45,4392
Spark BC53,0442
RGBA8 Metal Lossy1,4947
RGBA8 AFRC0,66793,4184
RGBA8 PVRIC42,3160
Spark BC74,2136

Стоит оговориться: результаты PVRIC4 могут не отражать полного потенциала железа — драйвер игнорирует запрошенный коэффициент сжатия и всегда использует 1:2. Возможно, эти результаты удастся пересмотреть после исправления проблемы.

Аппаратное сжатие — убедительная альтернатива программному. Главная оговорка — оно сейчас ограничено современными топовыми устройствами, которые как раз и располагают наибольшим объёмом памяти и пропускной способностью.

Даже когда нативное аппаратное сжатие доступно, есть веские причины продолжать использовать Spark. Вывод аппаратного сжатия различается у разных вендоров, и в некоторых случаях — как мы видели с PVRIC4 — качество уступает кодировщику реального времени. Если для вашего сценария критичен единообразный и предсказуемый вывод на всех вендорах, Spark остаётся правильным выбором.
Частые вопросы
1
Чем аппаратное сжатие текстур отличается от форматов BC/ASTC?

Форматы BC и ASTC — это сжатие, которое создаётся заранее (офлайн) и хранится на диске. GPU только декодирует их при чтении. Аппаратное сжатие (AFRC, PVRIC4, Metal Lossy) работает на лету: GPU сжимает изображение в момент записи в память и распаковывает при чтении. Это сокращает трафик памяти без участия CPU и без предварительной обработки.

2
Что такое BPC и как его интерпретировать?

BPC (bits per component) — количество бит на компонент цвета. Исходный RGBA8 — это 8 BPC. Сжатие 1:2 даёт 4 BPC. Удобная метрика, потому что не зависит от количества каналов: 4 BPC означает «вдвое меньше данных» как для R, так и для RGBA.

3
Можно ли использовать AFRC в Android-приложении?

Да, через Vulkan и расширение VK_EXT_image_compression_control. Нужно проверить поддержку расширения на устройстве, добавить структуру VkImageCompressionControlEXT при создании изображения и указать желаемый BPC. На Mali-G715 (Pixel 8) это работает корректно.

4
Почему PVRIC4 игнорирует запрошенный BPC?

Как разъяснил представитель PowerVR: PVRIC4 аппаратно поддерживает только 1:2. TFBC (чипы Rogue XE) поддерживал 1:2, 1:4 и 1:3, но сейчас недоступен. Драйвер экспонирует все значения BPC, но промоутирует запрошенный BPC к фактически поддерживаемому соотношению. Качество при этом не деградирует относительно запроса — просто всегда применяется 1:2.

5
Что такое Spark от NVIDIA?

Spark — библиотека NVIDIA для сжатия текстур в реальном времени, работающая как GPU compute shader. Поддерживает форматы BC1–BC7 и ASTC, доступна кроссплатформенно. Выступает эталоном для сравнения с аппаратным сжатием: это лучшее доступное программное решение без специализированного железа.