突破内存的桎梏:移动端纹理压缩应用与分析

作者:Lingtonke(柯灵杰)

导语

最近一段时间AR技术成为了时下热门,越来越多的应用开发者投身到这些技术中来。应用中出现了3D的AR场景,图形学也成为了必备的技术基础。在开发过程中,往往为了追求更好的效果而使用了更加高清的素材,使得本就内存吃紧的手机面对更加严峻的挑战,尤其是对iOS开发者而言。 为了解决这个问题,我们使用了纹理压缩技术。使用这个技术可以大幅度的降低APP的内存(共享显存)占用,从而在有限的内存限制下,使用更丰富的素材。

1 前言

最近一段时间AR技术成为了时下热门,越来越多的应用开发者投身到这些技术中来。应用中出现了3D的AR场景,图形学也成为了必备的技术基础。在开发过程中,往往为了追求更好的效果而使用了更加高清的素材,使得本就内存吃紧的手机面对更加严峻的挑战,尤其是对iOS开发者而言。

为了解决这个问题,我们使用了纹理压缩技术。使用这个技术可以大幅度的降低APP的内存(共享显存)占用,从而在有限的内存限制下,使用更丰富的素材。

2 什么是纹理压缩

常见的图片文件格式,比如PNG,JPG,BMP等,是图像为了存储信息而使用的对信息的特殊编码方式。它存储在磁盘中,或者内存中,但是并不能被GPU所识别。

这些文件格式当被读入后,还是需要经过CPU解压成bitmap,再传送到GPU端进行使用。

纹理格式是能被GPU所识别的像素格式,能被快速寻址并采样。压缩纹理,是一种GPU能直接读取并显示的格式,使得图像无需解压即可进行渲染,节约大量的内存。

3 常见的压缩纹理格式

3.1 DXT

DXT纹理压缩格式来源于S3(Silicon & Software Systems)公司提出的S3TC(S3 Texture Compression),基本思想是把4x4的像素块压缩成一个64或128位的数据块,是有损压缩方式。DXT1-DXT5是S3TC算法的五种变化,用于各种Windows设备。

压缩率:DXT1,DXT4,DXT5为4:1,DXT2、DXT3为2:1

主要支持Windows平台及Tegra系列的GPU的Android手机

支持GPU:

3.2 ETC

Ericsson Texture Compression,是由 Khronos 支持的开放标准,在移动平台中广泛采用。它是一种为感知质量设计的有损算法,其依据是人眼对亮度改变的反应要高于色度改变。类似于DXT,ETC也是把4x4的像素块压缩成一个64或128位的数据块,也是有损压缩。

Alpha

压缩率

适用

ETC1

N

6:1

OpenGLES 2.0

ETC2

Y

6:1

OpenGLES3.0

这个系列,可以说是适用机型最广的格式。

ETC1支持几乎所有市面上的Android机,所有iPhone

ETC2支持大部分高端Android机,iPhone 5S及以上

3.3 PVRTC

PowerVR Texture Compression,PVRTC格式与基于块的压缩格式,比如S3TC、ETC的不同之处是,它使用2张双线性放大的低分辨率图,根据精度和每个像素的权重,融合到一起来呈现纹理,并且2-bpp和4-bpp都支持ARGB数据。PVRTC格式压缩比较高,也是有损压缩。

Alpha

压缩率

适用

PVRTC 2BPP

Y

16:1

OpenGLES 2.0

PVRTC 4BPP

Y

8:1

OpenGLES2.0

这个系列,是iPhone支持最广的格式

只支持长宽相等且为2的幂次方的纹理

支持部分Android机(GPU:PowerVR系列),iPhone全系列机型

支持的GPU

3.4 ASTC

ASTC(Adaptive Scalable Texture Compression,自适应扩展纹理压缩),这是ARM提出的,去年被Khronos组织认可,纳入到标准中来,不过并不是强制性的

有多种压缩方式可选,具有不同的压缩率

Block footprint

Bit rate

压缩率

4x4

8.00

25%

5x4

6.40

25%

5x5

5.12

20%

6x5

4.27

20%

6x6

3.56

14%

8x5

3.20

20%

8x6

2.67

5%

10x5

2.56

20%

10x6

2.13

7%

8x8

2.00

25%

10x8

1.60

25%

10x10

1.28

20%

12x10

1.07

20%

12x12

0.89

这个系列,可以说是综合性能和使用便捷性最好的系列。

支持部分高端Android机型,iPhone6及以上机型

4 主要优缺点

在几乎不损害图片质量和显示性能的情况下,大幅度降低内存(显存)开销,纹理压缩就是这样的一个技术。

不过,任何的技术都有其适用范围和优缺点,需要仔细评估再决定。

4.1 主要优点

占用内存(显存)大幅度降低

无额外性能开销

使用方便,只需少量代码

4.2 主要缺点

硬件相关,要考虑兼容性

压缩纹理文件大小比常规PNG和JPG文件大

需要额外的制作工具,无法直接在移动端生成

5 如何使用压缩纹理

5.1 保存格式

压缩纹理是图片数据的一种编码方式,我们还缺少一个容器去承载。就像MP4文件是H264的视频的容器一样。

我们选择了使用KTX的格式。

KTX是一个为OpenGL和OpenGLES程序设计的纹理存储格式。它可以简单的辨别里面所存储的纹理格式和其他相关信息。

5.2 文件结构

Byte[12] identifier
UInt32 endianness
UInt32 glType
UInt32 glTypeSize
UInt32 glFormat
Uint32 glInternalFormat
Uint32 glBaseInternalFormat
UInt32 pixelWidth
UInt32 pixelHeight
UInt32 pixelDepth
UInt32 numberOfArrayElements
UInt32 numberOfFaces
UInt32 numberOfMipmapLevels
UInt32 bytesOfKeyValueData

for each keyValuePair that fits in bytesOfKeyValueData
    UInt32   keyAndValueByteSize
    Byte     keyAndValue[keyAndValueByteSize]
    Byte     valuePadding[3 - ((keyAndValueByteSize + 3) % 4)]
end

for each mipmap_level in numberOfMipmapLevels*
    UInt32 imageSize; 
    for each array_element in numberOfArrayElements*
       for each face in numberOfFaces
           for each z_slice in pixelDepth*
               for each row or row_of_blocks in pixelHeight*
                   for each pixel or block_of_pixels in pixelWidth
                       Byte data[format-specific-number-of-bytes]**
                   end
end
           end
           Byte cubePadding[0-3]
       end
    end
    Byte mipPadding[3 - ((imageSize + 3) % 4)]
end

5.3 使用KTX格式

typedef struct __attribute__((packed))
{
    uint8_t identifier[12];
    uint32_t endianness;
    uint32_t glType;
    uint32_t glTypeSize;
    uint32_t glFormat;
    uint32_t glInternalFormat;
    uint32_t glBaseInternalFormat;
    uint32_t width;
    uint32_t height;
    uint32_t depth;
    uint32_t arrayElementCount;
    uint32_t faceCount;
    uint32_t mipmapCount;
    uint32_t keyValueDataLength;
} KTXHeader;




KTXHeader *header = (KTXHeader *)[data bytes];

BOOL endianSwap = (header->endianness == 0x01020304);

self.width = endianSwap ? CFSwapInt32(header->width) : header->width;
self.height = endianSwap ? CFSwapInt32(header->height) : header->height;
self.internalFormat = endianSwap ? CFSwapInt32(header->glInternalFormat) : header->glInternalFormat;

uint32_t mipCount = endianSwap ? CFSwapInt32(header->mipmapCount) : header->mipmapCount;
uint32_t keyValueDataLength = endianSwap ? CFSwapInt32(header->keyValueDataLength) : header->keyValueDataLength;

const uint8_t *bytes = [data bytes] + sizeof(KTXHeader) + keyValueDataLength;
constsize_tdataLength = [data length] - (sizeof(KTXHeader) + keyValueDataLength);

NSMutableArray *levelDatas = [NSMutableArrayarrayWithCapacity:MAX(mipCount, 1)];

const uint32_t blockSize = 16;
uint32_t dataOffset = 0;
uint32_t levelWidth = self.width, levelHeight = self.height;
while (dataOffset<dataLength)
{
    uint32_t levelSize = *(uint32_t *)(bytes + dataOffset);
    dataOffset += sizeof(uint32_t);

    NSData *mipData = [NSDatadataWithBytes:bytes + dataOffsetlength:levelSize];
    [levelDatasaddObject:mipData];

    dataOffset += levelSize;

    levelWidth = MAX(levelWidth / 2, 1);
    levelHeight = MAX(levelHeight / 2, 1);
}

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jimoer

Java设计模式学习记录-抽象工厂模式

1063
来自专栏java工会

java设计模式-中介者模式

2006
来自专栏数据小魔方

R语言学习笔记之——数据处理神器data.table

数据处理在数据分析流程中的地位相信大家都有目共睹,也是每一个数据从业者面临的最为繁重的工作任务。 在实际应用场景下,虽然SQL(SQL类专业的etl语言)是数...

2958
来自专栏斑斓

一个完整的TDD演练案例(一)

实现猜数字的游戏。游戏有四个格子,每个格子有一个0到9的数字,任意两个格子的数字都不一样。你有6次猜测的机会,如果猜对则获胜,否则失败。每次猜测时需依序输入4个...

1133
来自专栏Crossin的编程教室

用 Python 跟自己下棋(续)

上周跟着 AlphaGo vs. 李世乭人机大战的风,写了一个命令行下的 TicTacToe 井字棋。不过,电脑是随机选位置,胡乱走子,所以下赢电脑易如反掌,下...

27510
来自专栏wym

ACM / ICPC 2018亚洲区预选赛北京赛站网络赛 3题签到

选择一个城市开始,必须按照顺时针遍历完所有城市,第一次到这个城市获得ai经费,到下一个城市的代价是bi

583
来自专栏前端知识分享

第66天:闭包的原理和函数传参

在程序语言中,所谓闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。这些外部执行域的非持久型...

754
来自专栏互联网大杂烩

八皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例:在8X8格的国际象棋棋盘上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一...

602
来自专栏企鹅号快讯

C+虚函数实现多态性的思考

相信这篇文字已经被这个晦涩的标题直接给PASS了,但笔者想把这些晦涩的概念说的生动些,C++和Python在编程思想上有很多是一致的,比如面向对象的思想,面向对...

18110
来自专栏祥子的故事

python | 工作笔记 | pandas 常用总结

3549

扫码关注云+社区