一、概述

在开发应用时,当开发者预置图片资源超过一定数量或者大小,由于图片资源的格式需要通过CPU解压缩为纹理格式才能直接被GPU读取,这就增加了CPU的处理时间,可能会引起图片完成时延增长。并且CPU解压缩生成的图片资源会占用较多的内存空间,给内存造成更大的压力,可能会引起卡顿掉帧。此时可以借助纹理压缩技术,将预置图片在构建过程中进行转码和压缩,节省CPU的处理过程,减少占用内存,提升应用性能。

需要注意纹理压缩会在编译构建时提前处理预置图片,可能会提高编译时长并造成包体膨胀的问题。因此如果资源图片占比较大,导致对包体积影响过于显著,则可以对图片资源进行筛选,从而降低纹理压缩的开销影响。

二、实现原理

预置图片在不设置纹理压缩的情况下,图片首先要经过CPU的解码生成PixelMap,并上传给GPU生成纹理进行渲染。解码和上传均比较耗时,开发者可以使用纹理压缩技术将CPU解码和纹理生成的过程提前到编译构建的过程当中,从而减少CPU处理图片的耗时。纹理压缩首先需要在编译文件配置相关属性,然后在构建态时根据相关配置找到对应的预置图片进行纹理转换生成纹理码流,之后进行超压缩编码生成超压缩码流。编译完成进入运行态后进行超压缩解码生成纹理码流,GPU将读取纹理码流后进行渲染显示。

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_纹理压缩

纹理压缩是在编译构建中对预置图片进行处理的,首先需要在编辑器的编译文件中配置纹理压缩的相关参数,hvigor根据配置的参数读取将要纹理压缩的文件资源,然后hvigor构造restool命令开始解析并生成资源文件列表,之后根据文件列表来遍历资源文件,将待转换文件进行转码生成纹理格式的文件,已经转换的资源文件不会再打包到构建产物中,最后将纹理文件和未转换的文件一起构建生成资源产物。

编译构建资源文件开启纹理压缩时序图如下:

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_图片资源_02

三、场景案例

由于图片格式不能直接被GPU渲染,需要等待CPU解码后上传GPU,需要一定耗时,当一定数量的预置图片在一个页面同时进行渲染时,可能会造成图片的完成时延过大。以如下场景为例,设计一个Tab栏切换的示例,当向右滑动切换到tab2的页面时,新页面中通过横列布局加载40张.png格式和40张.jpg格式的预置图片,对比开启和关闭纹理压缩两种情况下,图片完成时延显著不同。未开启纹理超压缩切换过程的效果图如下:

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_图片资源_03

在不使用纹理压缩的情况下,当向右滑动切换到tab2的页面时,由于新页面有较多的预置图片需要加载时,会造成一部分图片的完成时延过大,显示会有白块的情况出现。

(一)工程配置

使用纹理压缩首先需要进行一些基础配置,从而选择要将哪些预置图片进行纹理超压缩。可以在工程级或模块级build-profile.json5配置文件中添加纹理压缩的配置项。纹理超压缩的配置项是在compression对象中添加media和filters两个属性值的内容:

media: 在media中enable属性是控制是否对media图片启用纹理压缩。默认的属性值为false,默认不启用,需要开启纹理压缩时将其属性值改为true。

filters: 在filters属性中可以配置三个属性对象,分别是method、files和exclude。

method中有两个属性值type和blocks,type可以设置转换类型“sut”和“astc”,blocks设置转换类型的扩展参数,当前只支持“4x4”。

files中三个属性path、size和resolution,path指定“按路径匹配”的过滤条件,size指定“按大小匹配”的过滤条件,resolution指定“按分辨率匹配”的过滤条件。

exclude中的属性与files中相同,从files中剔除掉不需要压缩的文件。

纹理压缩配置的示例代码如下:

"buildOption": {
  "resOptions": {
    "compression": {
      "media": {
        "enable": true // 是否对media图片启用纹理压缩
      },
      // 纹理压缩文件过滤,非必填,不填会压缩资源目录中的所有图片
      "filters": [
        {
          "method": {
            "type": "sut", // 转换类型
            "blocks": "4x4" // 转换类型的扩展参数
          },
          // 指定用来参与压缩的文件,需要满足所有条件且不被exclude过滤的文件才会参与压缩
          "files": {
            "path": ["./**/*"], // 指定资源目录中的所有文件
            "size": [[0, '1000k']], // 指定大小1000k以下的文件
            // 指定分辨率小于3000*3000的图片
            "resolution": [
              [
                { "width": 0, "height": 0 }, // 最小宽高
                { "width": 3000, "height": 3000 } // 最大宽高
              ]
            ]
          },
          // 从files中剔除掉不需要压缩的文件,需要满足所有过滤条件的文件才会被剔除
          "exclude": {
            "path": ["./**/*.webp"], // 过滤所有webp文件
            "size": [[0, '1k']], // 过滤大小1k以下的文件
            // 过滤分辨率小于1024*1024的图片
            "resolution": [
              [
                { "width": 0, "height": 0 }, // 最小宽高
                { "width": 1024, "height": 1024 } // 最大宽高
              ]
            ]
          }
        }
      ]
    }
  }},
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

配置项注意点:
文件过滤配置参数filters:当工程级和模块级同时配置时,先按照模块级的过滤条件匹配,一旦匹配成功,则忽略工程级的过滤条件;如果模块级的没有匹配成功,继续按工程级的条件进行匹配。
转换类型type:
astc(Adaptive Scalable Texture Compression):自适应可变纹理压缩,一种对GPU友好的纹理格式,可在设备侧更快地显示,有更少的内存占用。
sut(SUper compression for Texture):纹理超压缩,可在设备侧更快地显示,有更少的内存占用,相比astc具备更大压缩率和更少ROM占用。
size(按大小匹配)和resolution(按分辨率匹配):注意size一维数组取值和resolution二维数组取值的区别。
按大小匹配是一维数组,因此按大小匹配[0-1k,1k-2k]与按大小匹配[0-2k]的取值范围是相同的。
按分辨率匹配时,匹配分辨率的宽高值是二维数组,下图左侧代表分辨率小于20482048的所有图片,右侧代表分辨率小于10241024的图片和分辨率大于10241024且小于20482048的图片。两个写法看似相同,但其取值范围并不一样。

(二)编译执行
在配置完相关的参数后,执行项目进行编译构建。在编译过程中,hvigor会根据配置的参数获取需要转换的预置图片,通过转码部件纹理压缩后进行打包。使用纹理压缩后进行Tab栏切换的效果图如下:

通过效果图可以看出,使用纹理压缩的情况下,向右滑动切换tab2的页面后图片直接出来显示,不会出现图片显示延迟的状况,没有出现显示白块的情况。
(三)收益和开销
使用纹理压缩进行预置图片资源转换时,要注意覆盖的资源文件的多少,要考虑在获取更大收益的同时减少其开销造成的影响。因此纹理超压缩的性能提升要从收益和开销两部分进行分析:
1.收益
纹理压缩的主要收益是在编译过程中将预置图片转换为纹理格式,能直接被GPU读取进行渲染,降低了CPU和DDR的负载,能更快的加载图片。在Tab栏切换示例中将预置图片分别以原图(.png)、纹理超压缩(.sut)和自适应可变纹理压缩(.astc)三种方式为例,测试图片的读取耗时如下图所示:

统计以上H:CreateImagePixelMap的耗时得到下表:配置项注意点:

文件过滤配置参数filters:当工程级和模块级同时配置时,先按照模块级的过滤条件匹配,一旦匹配成功,则忽略工程级的过滤条件;如果模块级的没有匹配成功,继续按工程级的条件进行匹配。

转换类型type:

astc(Adaptive Scalable Texture Compression):自适应可变纹理压缩,一种对GPU友好的纹理格式,可在设备侧更快地显示,有更少的内存占用。

sut(SUper compression for Texture):纹理超压缩,可在设备侧更快地显示,有更少的内存占用,相比astc具备更大压缩率和更少ROM占用。

size(按大小匹配)和resolution(按分辨率匹配):注意size一维数组取值和resolution二维数组取值的区别。

按大小匹配是一维数组,因此按大小匹配[0-1k,1k-2k]与按大小匹配[0-2k]的取值范围是相同的。

按分辨率匹配时,匹配分辨率的宽高值是二维数组,下图左侧代表分辨率小于20482048的所有图片,右侧代表分辨率小于10241024的图片和分辨率大于10241024且小于20482048的图片。两个写法看似相同,但其取值范围并不一样。

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_编译过程_04

(二)编译执行

在配置完相关的参数后,执行项目进行编译构建。在编译过程中,hvigor会根据配置的参数获取需要转换的预置图片,通过转码部件纹理压缩后进行打包。使用纹理压缩后进行Tab栏切换的效果图如下:

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_图片资源_05

通过效果图可以看出,使用纹理压缩的情况下,向右滑动切换tab2的页面后图片直接出来显示,不会出现图片显示延迟的状况,没有出现显示白块的情况。

(三)收益和开销

使用纹理压缩进行预置图片资源转换时,要注意覆盖的资源文件的多少,要考虑在获取更大收益的同时减少其开销造成的影响。因此纹理超压缩的性能提升要从收益和开销两部分进行分析:

1.收益

纹理压缩的主要收益是在编译过程中将预置图片转换为纹理格式,能直接被GPU读取进行渲染,降低了CPU和DDR的负载,能更快的加载图片。在Tab栏切换示例中将预置图片分别以原图(.png)、纹理超压缩(.sut)和自适应可变纹理压缩(.astc)三种方式为例,测试图片的读取耗时如下图所示:

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_图片资源_06

统计以上H:CreateImagePixelMap的耗时得到下表:

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_图片资源_07

通过上表可以看到,使用原图(.png)格式的图片加载耗时是纹理格式的加载耗时4倍左右,纹理超压缩和自适应可变纹理压缩两种图片加载耗时2倍左右。在开启纹理超压缩或自适应可变纹理压缩的情况下可以有效地提高应用中预置图片的加载速度。

在对比完加载图片的耗时后,使用Tab栏切换示例做内存大小的测试,查看使用纹理压缩前后的内存占用情况,得到相关数据如下图所示:

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_图片资源_08

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_纹理压缩_09

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_纹理压缩_10

统计纹理压缩开启前后的内存占用大小数据如下表:

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_纹理压缩_11

通过表中数据可以知道,开启纹理压缩后内存的占用从598965KB下降到了165015KB~167723KB,图片加载占用内存的大小降低。

2.开销

使用纹理压缩时,由于在编译过程中进行预置图片的转换,因此会增加编译的时长。并且在将预置图片转为纹理格式时,根据预置图片的格式的不同,转换后的大小也不相同,会让包体膨胀或收缩。

编译时间长的问题是因为在编译过程中增加了纹理压缩的过程,可以各准备87张png/webp/jpg预置图片分别以全量编译、修改按分辨率过滤参数和增加1~100张图片三种情况进行编译打包,对比三种情况下纹理压缩打开和关闭的编译时长,得到相关数据如下表所示:

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_图片资源_12

从上表可以看出开启纹理压缩后全量编译耗时较大,但是按分辨率过滤预置图片后再次纹理压缩,能够有效减少编译时长。

在对比完编译时长的问题后,对若干个示例应用进行侧量,其中.jpg和.webp格式图片的膨胀率为2~3倍左右。 具体开启纹理超压缩后体积的膨胀率得到相关数据如下表所示:

HarmonyOS优化应用预置图片资源加载耗时问题性能优化_纹理压缩_13

说明

具体工程应用会因为实际工程内资源大小、格式、分辨率和数量等因素的不同产生不同的包体膨胀率,以上数据仅供开发者参考。

可以看到.png格式的图片纹理压缩后,没有膨胀,但是.jpg格式和.webp格式的图片包体积膨胀较大。因此结合编译时长和打包体积综合来看,为了使用纹理超压缩获得更好的性能,在对包体积敏感的场景下,可以采用对.png格式图片全转;对.jpg、.webp格式的图片挑选其中被高频使用或者对关键帧有关键影响的部分图片进行转换的策略。

四、总结

在开发应用时,如果在应用中预置一定数量的图片,开发者可以考虑使用纹理压缩的方法来转换并压缩图片资源,提升应用性能。开发者可以在项目的工程级或模块级build-profile.json5配置文件中添加纹理压缩的配置项,编译构建时将设置的图片资源转化并压缩,生成资源包。这些转化后的图片资源可以被GPU直接使用,省略了CPU的处理过程,提高图片的显示速度,减少内存的占用,从而实现内存的提升。

本文主要引用参考HarmonyOS官方文档