07. WebApp2.0时代启程:倒立者赢,从CPU到GPU,一张图片的旅行

简介: 紧接上文,终端开发使用的WindVane、wax、ReactNative等已经是一种跨平台的技术,我们称之为上层跨平台,Cocos2d-x这种直接使用C/C++,我们成为底层跨平台。上层跨平台,提升开发效率;下层跨平台,提升程序性能。 ####1. 为什么Cocos2d-x性能比Native开发要好? 因为Cocos2d-X是游戏引擎呗,人家是专业做游戏特效的好不好,直接调用GPU的Ope

紧接上文,终端开发使用的WindVane、wax、ReactNative等已经是一种跨平台的技术,我们称之为上层跨平台,Cocos2d-x这种直接使用C/C++,我们成为底层跨平台。上层跨平台,提升开发效率;下层跨平台,提升程序性能。

1. 为什么Cocos2d-x性能比Native开发要好?

因为Cocos2d-X是游戏引擎呗,人家是专业做游戏特效的好不好,直接调用GPU的OpenGL绘图的好不好。打开Cocos2d-X代码,感触最深的不是CCNode这些游戏节点,cocos2d-x已经开始为App开发做准备了!
screenshot
大家看到了什么?UIImageView?UIScrollView?OMG!当我们还在纠结要不要学习一下游戏开发的时候,人家都已经开始做UI系统了,有木有!!

很长一段时间,我们以为Game和App之间有一种天然的鸿沟,长久以来,这两种技术之间没有直接的联系,App可以做各种UI控件,游戏可以做各种UI特效,当游戏开发者想NativeApp大步的走过来的时候,我们还在学习如何用UITableView去优化图片的显示,岂不知在游戏世界里,早已实现了图片的异步加载、解码、缓存了!

别忘了,从iOS7.0开始,苹果已经默认集成了GameKit.framework,颤抖吧!游戏和App即将面临傻傻分不清楚的时候了。

2. 一张图片在OpenGL的世界里,是如何从SDCard显示到屏幕

这一次,我们不是讲UIImage如何加载一个图片,而是自己读取图片文件,解码图像,上传纹理,推送顶点缓冲,刷新帧缓冲,一步步显示图片到屏幕上。

上图之前,我们先了解几个概念:
2.1 帧缓冲:
我们要显示的视图绘制到屏幕上,本质是一个int32 buffer[width x height x 4]的一个数组,不管2D还是3D,我们都要吧GPU的对象投影到这个buffer中,一个像素4字节表示[R/G/B/A]。

2.2 顶点缓冲:
终端设备,只支持精简版的OpenGL,称之为OpenGL ES,这里我们只用2.0版本。在这个版本中,我们只能画三角形,一个正方形的图片纹理,需要两个三角形才可以描述:
screenshot
我们要画图片之前,就要告诉OpenGLES图片顶点的位置,先忽略三角形的顺序问题,就这样告诉GPU:【ABDBDC】,同时设置当前的激活的纹理,就可以绘制图片了。

2.3 上传纹理:
现在绝大多数手机,都有独立的显卡芯片,每个显卡芯片都有独立的显存,这么就清楚了:我们常说的内存是CPU直接操控的内存条容量,算上显存,就有两个缓存空间了,程序员可以发挥的空间又多了,是不是有点小激动?

2.4 图片解码:
常见的jpg、png等图片文件,都是压缩后的文件,如果不压缩,一张1024x1024的图片,至少需要4M空间。一张图片文件解码到内存,需要解码器来解码,不幸的是,Android很多机型没有集成硬件解码,庆幸的是iOS设备内部支持硬件解码。如果一张图片文件可以瞬间在底层硬件解码完成,要比上层代码优化,更加直接!这就是我们为什么一直追求的是底层跨平台的原因。

2.5 讲了很多概念了,我们上图吧
screenshot

3. 原来图片可以不占用内存!

我们用C的API来读取一张图片,用libpng来解码一张png图片,调用OpenGL ES2.0API上传图片到GPU显存,指定绘图坐标,显示一张图片,我们上代码吧:

一)读取文件:


AEFileData AEFileDataWithPathfile(std::string& pathfile) {
    AEFileData data;
    FILE* file = fopen(pathfile.c_str(), "rb");
    if (file == NULL) {
        return data;
    }
    fseek(file, 0, SEEK_END);
    data.size = (GLuint)ftell(file);
    fseek(file, 0, SEEK_SET);
    data.bytes = (GLchar*)malloc(sizeof(GLchar) * data.size);
    fread(data.bytes, sizeof(GLchar), data.size, file);
    fclose(file);
    return data;
}

二)解码图片:
为了跨平台通用,我们用libpng来解码图片,获取png的format和rgba数组,【代码太多,就不贴了】

三)上传GPU:


glGenTextures(1, &_textureId);
glBindTexture(type, _textureId);
glTexImage2D(type, 0, _format, _size.width, _size.height, 0, _format, GL_UNSIGNED_BYTE, pixels);

四)释放内存


AEFileData file = AEFileDataWithPathfile(pathfile);
this->initPNG((GLuchar*)file.bytes, file.size);
__FREE(file.bytes);

五)指定顶点索引,并绘制


vb[0] = {d11, t11, color};
vb[1] = {d21, t21, color};
vb[2] = {d12, t12, color};
vb[3] = {d21, t21, color};
vb[4] = {d12, t12, color};
vb[5] = {d22, t22, color};
glDrawArrays(GL_TRIANGLES, 0, _vertexIndex);

4. 对比iOS与Android,图片的渲染和展示做了什么?

screenshot
Android的技术框架更偏于上层,看不到底层C/C++代码实现,优化的空间有限;Objective-C更靠近C语言,优化的空间明显。下面我们以iOS为例:

5. 主流框架的图片优化思路

要读懂图片的展示与优化,就要看看当前主流的图片优化框架,他们是如何做优化的?主流的ImageCache框架:SDWebImage/FastImageCache/TBImageCache

screenshot

左边的是SDWebImage,中间是FastImageCache,右边是TBImageCache

SDWebImage:
将解码后的图片分别保存到内存和SDCard,将复杂的操作放到了子线程中;

FastImageCache:
直接将文件读取到mmap映射的文件中,减少了一次从系统内核到用户空间的一次拷贝【然并卵】

TBImageCache:
在首次加载图片时,根据用户需要,对图片增加圆角阴影处理,增加了异步线程的处理的工作量,同时保存到sdcard中,消耗了额外的资源,换取的是更好的图片性能【解决了圆角阴影在终端的痛点】

图片缓存框架,只是让CPU负载曲线更均衡,有没有可能再进一步优化?

6. 游戏世界与App世界对比

CPU与GPU是终端的双驾马车,缺一不可,CPU load过高,导致终端卡顿,同理GPU也是如此。iOS的UIKit框架封装的太好了,也是优化做的最好的,让我们看不到底层的原理,看不到iOS是如何平衡CPU、GPU、RAM、GPU RAM的4者之间的关系,让我们大胆大猜测一下吧:

screenshot

一)UIImageView管理者UIImage对象
UIImage就是内存中得bitmap数组,当我们需要绘制一张图片的时候,CPU发出指令,上传BitMap到GPU的显存中,GPU返回生成的纹理ID给CPU。

二)为了提高iOS的性能,苹果做出了一个优化策略,不释放CPU的bitmap
当GPU显存空间不足的时候,iOS会释放一部分图片纹理,以节省显存空间,但不会释放内存的bitmap。当一张图片重新需要绘制的时候,iO快速从内存bitmap快速上传到显存,完成绘制过程。

三)当内存的bitmap释放的时候,同步释放显存的纹理对象
本质意义上说,bitmap在内存中得存储,不是真正显示的那个缓存,当bitmap上传到GPU的时候,GPU会在显存中申请同样大小的空间,来存放bitmap,并返回纹理ID,每当CPU需要显示某张图片的时候,只需要高速GPU要绘制那个纹理ID就可以了。

四)上传GPU后,我们完全可以释放内存bitmap
释放内存的bitmap很简单: free(bitmap);
这样内存里只剩下了对应的GPU纹理的ID,当GPU内存紧张的时候,程序要要自己释放不需要的纹理ID,如果这个ID在某个时候有需要使用了,怎么办?那要把之前的读取文件到上传GPU重走一遍,太耗性能了,我想苹果也是不想看到这个原因,才不会轻易释放bitmap的

总结:游戏的架构模式,需要开发者自己优化所有场景

游戏开发者不仅需要优化CPU内存,也要优化GPU内存,通过将所有图片合成一张大的纹理图片,来统一管理GPU内存,需要把大量浮点数运算根据不同的场景,或者交给CPU或者交给GPU,让CPU和GPU的负载达到均衡,各司其职。
这一些优化,对于我们做系统上层应用的无线开发,是很难想象有多少坑等着我们,终端App的开发,还是使用上层框架好,图形图像的世界水很深,有想法的同学,可以继续关注下一章分享,想了解我们的同学,可以用Android下载安装我们的Demo:
screenshot

下一章,我们一起见证所及即所得的开发方式,WebApp2.0初体验

相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
15天前
|
弹性计算 人工智能 Serverless
阿里云ACK One:注册集群云上节点池(CPU/GPU)自动弹性伸缩,助力企业业务高效扩展
在当今数字化时代,企业业务的快速增长对IT基础设施提出了更高要求。然而,传统IDC数据中心却在业务存在扩容慢、缩容难等问题。为此,阿里云推出ACK One注册集群架构,通过云上节点池(CPU/GPU)自动弹性伸缩等特性,为企业带来全新突破。
|
1月前
|
监控 异构计算
Jetson 学习笔记(八):htop查看CPU占用情况和jtop监控CPU和GPU
在NVIDIA Jetson平台上使用htop和jtop工具来监控CPU、GPU和内存的使用情况,并提供了安装和使用这些工具的具体命令。
111 0
|
22天前
|
机器学习/深度学习 人工智能 并行计算
CPU和GPU的区别
【10月更文挑战第14天】
|
22天前
|
机器学习/深度学习 人工智能 缓存
GPU加速和CPU有什么不同
【10月更文挑战第20天】GPU加速和CPU有什么不同
42 1
|
2月前
|
人工智能 自然语言处理 文字识别
MinerU-大语言语料处理神器,CPU/GPU均可跑,开源免费“敲”好用
在7月4日举行的WAIC 2024科学前沿主论坛上,书生·浦语2.5正式发布,面向大模型研发与应用的全链条工具体系同时迎来升级。
MinerU-大语言语料处理神器,CPU/GPU均可跑,开源免费“敲”好用
|
3月前
|
机器学习/深度学习 人工智能 并行计算
【人工智能】CPU、GPU与TPU:人工智能领域的核心处理器概述
在人工智能和计算技术的快速发展中,CPU(中央处理器)、GPU(图形处理器)和TPU(张量处理器)作为核心处理器,各自扮演着不可或缺的角色。它们不仅在性能上各有千秋,还在不同的应用场景中发挥着重要作用
201 2
|
4月前
|
并行计算 API 数据处理
GPU(图形处理单元)因其强大的并行计算能力而备受关注。与传统的CPU相比,GPU在处理大规模数据密集型任务时具有显著的优势。
GPU(图形处理单元)因其强大的并行计算能力而备受关注。与传统的CPU相比,GPU在处理大规模数据密集型任务时具有显著的优势。
|
3月前
|
机器学习/深度学习 编解码 人工智能
阿里云gpu云服务器租用价格:最新收费标准与活动价格及热门实例解析
随着人工智能、大数据和深度学习等领域的快速发展,GPU服务器的需求日益增长。阿里云的GPU服务器凭借强大的计算能力和灵活的资源配置,成为众多用户的首选。很多用户比较关心gpu云服务器的收费标准与活动价格情况,目前计算型gn6v实例云服务器一周价格为2138.27元/1周起,月付价格为3830.00元/1个月起;计算型gn7i实例云服务器一周价格为1793.30元/1周起,月付价格为3213.99元/1个月起;计算型 gn6i实例云服务器一周价格为942.11元/1周起,月付价格为1694.00元/1个月起。本文为大家整理汇总了gpu云服务器的最新收费标准与活动价格情况,以供参考。
阿里云gpu云服务器租用价格:最新收费标准与活动价格及热门实例解析
|
5天前
|
机器学习/深度学习 人工智能 弹性计算
什么是阿里云GPU云服务器?GPU服务器优势、使用和租赁费用整理
阿里云GPU云服务器提供强大的GPU算力,适用于深度学习、科学计算、图形可视化和视频处理等多种场景。作为亚太领先的云服务提供商,阿里云的GPU云服务器具备灵活的资源配置、高安全性和易用性,支持多种计费模式,帮助企业高效应对计算密集型任务。
|
5天前
|
机器学习/深度学习 人工智能 弹性计算
阿里云GPU服务器全解析_GPU价格收费标准_GPU优势和使用说明
阿里云GPU云服务器提供强大的GPU算力,适用于深度学习、科学计算、图形可视化和视频处理等场景。作为亚太领先的云服务商,阿里云GPU云服务器具备高灵活性、易用性、容灾备份、安全性和成本效益,支持多种实例规格,满足不同业务需求。