中国领先的IT技术网站
|
|
创建专栏

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

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

作者:柯灵杰|2017-08-09 13:48

开发者大赛路演 | 12月16日,技术创新,北京不见不散


导语

最近一段时间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位的数据块,也是有损压缩。

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

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

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

3.3 PVRTC

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

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

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

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

支持的GPU

3.4 ASTC

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

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

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

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

4 主要优缺点

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

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

4.1 主要优点

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

无额外性能开销

使用方便,只需少量代码

4.2 主要缺点

硬件相关,要考虑兼容性

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

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

5 如何使用压缩纹理

5.1 保存格式

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

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

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

5.2 文件结构

  1. Byte[12] identifier 
  2. UInt32 endianness 
  3. UInt32 glType 
  4. UInt32 glTypeSize 
  5. UInt32 glFormat 
  6. Uint32 glInternalFormat 
  7. Uint32 glBaseInternalFormat 
  8. UInt32 pixelWidth 
  9. UInt32 pixelHeight 
  10. UInt32 pixelDepth 
  11. UInt32 numberOfArrayElements 
  12. UInt32 numberOfFaces 
  13. UInt32 numberOfMipmapLevels 
  14. UInt32 bytesOfKeyValueData 
  15.  
  16. for each keyValuePair that fits in bytesOfKeyValueData 
  17.     UInt32   keyAndValueByteSize 
  18.     Byte     keyAndValue[keyAndValueByteSize] 
  19.     Byte     valuePadding[3 - ((keyAndValueByteSize + 3) % 4)] 
  20. end 
  21.  
  22. for each mipmap_level in numberOfMipmapLevels* 
  23.     UInt32 imageSize;  
  24.     for each array_element in numberOfArrayElements* 
  25.        for each face in numberOfFaces 
  26.            for each z_slice in pixelDepth* 
  27.                for each row or row_of_blocks in pixelHeight* 
  28.                    for each pixel or block_of_pixels in pixelWidth 
  29.                        Byte data[format-specific-number-of-bytes]** 
  30.                    end 
  31. end 
  32.            end 
  33.            Byte cubePadding[0-3] 
  34.        end 
  35.     end 
  36.     Byte mipPadding[3 - ((imageSize + 3) % 4)] 
  37. end 

5.3 使用KTX格式

  1. typedef struct __attribute__((packed)) 
  2.     uint8_t identifier[12]; 
  3.     uint32_t endianness; 
  4.     uint32_t glType; 
  5.     uint32_t glTypeSize; 
  6.     uint32_t glFormat; 
  7.     uint32_t glInternalFormat; 
  8.     uint32_t glBaseInternalFormat; 
  9.     uint32_t width; 
  10.     uint32_t height; 
  11.     uint32_t depth; 
  12.     uint32_t arrayElementCount; 
  13.     uint32_t faceCount; 
  14.     uint32_t mipmapCount; 
  15.     uint32_t keyValueDataLength; 
  16. } KTXHeader; 
  17.  
  18.  
  19.  
  20.  
  21. KTXHeader *header = (KTXHeader *)[data bytes]; 
  22.  
  23. BOOL endianSwap = (header->endianness == 0x01020304); 
  24.  
  25. self.width = endianSwap ? CFSwapInt32(header->width) : header->width; 
  26. self.height = endianSwap ? CFSwapInt32(header->height) : header->height; 
  27. self.internalFormat = endianSwap ? CFSwapInt32(header->glInternalFormat) : header->glInternalFormat; 
  28.  
  29. uint32_t mipCount = endianSwap ? CFSwapInt32(header->mipmapCount) : header->mipmapCount; 
  30. uint32_t keyValueDataLength = endianSwap ? CFSwapInt32(header->keyValueDataLength) : header->keyValueDataLength; 
  31.  
  32. const uint8_t *bytes = [data bytes] + sizeof(KTXHeader) + keyValueDataLength; 
  33. constsize_tdataLength = [data length] - (sizeof(KTXHeader) + keyValueDataLength); 
  34.  
  35. NSMutableArray *levelDatas = [NSMutableArrayarrayWithCapacity:MAX(mipCount, 1)]; 
  36.  
  37. const uint32_t blockSize = 16; 
  38. uint32_t dataOffset = 0; 
  39. uint32_t levelWidth = self.width, levelHeight = self.height; 
  40. while (dataOffset<dataLength) 
  41.     uint32_t levelSize = *(uint32_t *)(bytes + dataOffset); 
  42.     dataOffset += sizeof(uint32_t); 
  43.  
  44.     NSData *mipData = [NSDatadataWithBytes:bytes + dataOffsetlength:levelSize]; 
  45.     [levelDatasaddObject:mipData]; 
  46.  
  47.     dataOffset += levelSize; 
  48.  
  49.     levelWidth = MAX(levelWidth / 2, 1); 
  50.     levelHeight = MAX(levelHeight / 2, 1); 

原文链接:https://www.qcloud.com/community/article/897529,作者:柯灵杰

【本文是51CTO专栏作者“腾讯云技术社区”的原创稿件,转载请通过51CTO联系原作者获取授权】

戳这里,看该作者更多好文

【编辑推荐】

  1. 智能时代的移动应用
  2. 外媒速递:移动应用开发者值得一试的十款出色登录页面方案
  3. 从环境设置到内存分析:Python代码优化指南
  4. 小心你的手机和移动网络
  5. 外媒速递:十种方式确保你的网站面向移动设备实现优化
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

热门职位+更多