« July 2023 | Main | September 2023 »

August 14, 2023

疑似 covid-19 二次感染

第一次感染 covid-19 是在去年底,距今已有 8 个月了。

最近似乎又中招过一次,特此记录一下。

上周四( 8 月 10 日)午饭过后,感觉有点困,就靠在办公椅上打了个盹。我平常没有固定午休的习惯,觉得自己是头天没睡好的缘故。14 点多醒了,感觉依旧精神不好。起身去上厕所时发现有些头晕,不过没太在意。

18 点下班时,感觉头晕没有缓和,似乎更严重了。去食堂的路上走路有点飘。没什么胃口,快速吃完,觉得自己的体力不足以步行回家,就搭了个同事的顺风车赶紧回家睡觉。

这时有点怀疑自己二阳了。但检测体温 36.3 ,没有发烧的症状,也没有类似感冒的其它症状,就只有头晕。

之前一周,坐我对面的同事说他确定二次感染了 covid-19 (自测抗原)。发烧了一晚,恰逢周末(8 月 5/6 日),休息了两天后恢复。而我没有发烧症状,不确定是不是。

家里没有抗原检测,也懒得出去买。没有吃药,就只多喝了几杯开水,20 点就上床睡了,一直睡到周五早上 7 点,感觉还是没有完全恢复。和公司同事打了个招呼,说上午不去公司了。

周五下午坐公交去了办公室。和同事聊了一下工作进展,没有什么心情写程序。到 18 点下班去食堂吃完饭就坐公交回家了。头还是有点晕,但比周四要好一些。

周末感觉体力不行,没有去攀岩。周六和周日一直在家睡觉。到周日下午觉得精神好了不少,就看了半本小说。因为头晕,不想打博德之门 3 ,拿出之前没打完的《银河战士:生存恐惧》继续。晚上玩了盘桌游卡坦岛,很早就睡觉了。睡觉前身体还是略微不适(主要是头晕)。整个周末体温都没有超过 36.5 。

云豆:爸爸,你的病是不是还没有好呀?

我说:没完全好。

云豆:爸爸,我想问你个问题,但是我不敢说。

我说:我不会死的。

睡觉时一直在想,如果身体不舒服,确实很多事情,尤其是用脑的事情都没法做。所以,身体健康的确是最重要的。

周一醒来时,已经完全恢复了。


中午吃饭时和同事交流了一下。同组的同事有周三就开始头晕的,也没有发烧,但连着几天腹泻。

周四送我回家的同事周末也在家躺着,感觉特别嗜睡。

到最后也没确诊,不知道是不是 covid-19 ,但看起来至少是种有传染性的病毒。症状很轻微,自愈周期甚至比感冒还短。

August 07, 2023

手机游戏引擎的优化

我们的手机游戏引擎一直在跟随着游戏项目的进程不断优化。一开始是因为游戏引擎在手机上帧数达不到要求。得益于 ECS 框架,我们把初期用 Lua 快速开发出来的几个核心 system 用 C 重写后,得到了质的飞跃。

其实这些核心代码总量并不算大。例如在 profile 中表现出来的非常消耗 CPU 的一个场景树更新系统,用 C 重写了也才 200 行代码 ,但在优化前 Lua 版本会消耗超过 1ms 的时间,而用 C 重写后,时间已可以忽略不计。

另外,我们采用了类似 skynet 的 ltask 做多线程框架,把业务尽量拆分到多线程中并行处理,这也极大的减少了每帧的耗时。除了主业务逻辑外,UI 、粒子系统、IO 被分为几个并行线程。且渲染底层的 bgfx 也是按多线程渲染设计的。这些并行流程间只通过少量的消息通讯,所以,并行的总工作量并没有比单线程模型更多。ltask 也可以很方便的调节工作线程的个数,用来更好的适配手机的 CPU 。

从xcode 的调试信息看,在游戏场景丰富时,大约会占用 280% 的 cpu 。换句话说,如果我们采用的是单线程架构,在不删减特性的前提下,做到流畅是相当困难的。

我的开发用手机为 iPhone 12 mini 。目前,游戏锁定在 30fps ,而每帧实际时间开销为平均 10ms 左右 (7ms ~ 15 ms 之间)。所以理论上锁定 60fps 也是完全做得到的,但因为不是动作类游戏,所以无此必要。

目前遇到最大的问题是游戏的能耗。优化游戏引擎让它尽量减少能耗,是过去在 PC 上做开发所没有过的经历。

让玩家的续航时间更长是一方面的原因,更重要的原因是:一旦让手机长时间工作在高能耗状态下,超过了手机的散热设计功耗 (TDP),必然会逐步发烫。最终会导致手机 CPU 自己降频,帧率也达不到了。

以前以帧率为唯一衡量标准放到手机上就行不通了。如果只为帧率达标的话,一般是在达标的基础上尽可能的提升画面质量,或是采用更简单易维护的算法。例如,有些算法为了简单,就直接每帧重复计算。而 cache 计算结果尽量避免重复计算,通常会增加代码的复杂度,却能减少 CPU 的使用。

现在,优化变得没有上限。只要能减少 CPU 开销都值得做。减少的开销全部能兑换成更长的续航时间和更高的散热效率。从散热角度看,手机真不是个好的游戏设备,switch 这种带风扇的设备要好得多。GPU 的开销更是如此,虽然 iphone 的旗舰级的 GPU 性能开起来远超 switch (从字面上的数字看,超过了 ps4 ),但实际上峰值性能除了给人几秒的惊艳外,远远达不到玩家的需求。iphone 12 只有 6W 的 TDP ,而 switch 的 TDP 达到了 15 W ,是它的两倍半。我们在手机上设计游戏,需要适当裁剪效果,把能耗控制下来才行。

我们的引擎使用了基于物理的渲染 ( PBR )。材质、光照都比较复杂。技术虽然不算新,但现在看起来在手机上运行还是比较勉强的。我发现像素着色器(fragment shader)开销比较大。而这恰巧是最容易优化的:改一行设置,直接降分辨率即可。iphone 12 mini 的视网膜屏有 476 ppi ,如果按最高分辨率的像素绘制 18x6 的点阵字母会小到我的眼睛几乎分辨不清。按一半分辨率渲染 1170 x 560 给我的感受,画面质量只下降了一丁点。但能耗居然可以下降到全分辨率的一半左右。(而每帧的时间开销并无太大区别)

为了得到准确的能耗情况,我没有采纳 xcode 给我的数据,而使用了更苛刻的测试方法:

每次测试前把电量充到 97% ,然后静置到刚好变成 96% 的那一刻开始启动游戏,加载足够复杂的场景,连续玩 16 分钟。以这样相同的测试条件,分别对全分辨率和半分辨率各测试两次(共四次),我发现全分辨率下的耗电几乎是半分辨率的一倍。

而考虑到能耗开销由 CPU 和 GPU 共同承担,修改分辨率丝毫不影响 CPU 的使用,那么 GPU 开销实际不只翻倍。或者说,我们引擎的 CPU 部分优化已经足够了,接下来需要重点考虑的是 GPU 开销。

btw, 我们游戏引擎几乎是用 Lua 编写,只在最近一年把 profile 后发现的少量性能瓶颈系统平替为 C/C++ 版本。用占比不大的 C/C++ 代码解决了 Lua 框架的性能问题。而 Lua 部分易于开发维护,(多虚拟机)还可以更好的适应多线程框架。


另一个能耗大头是特效系统,它主要是 CPU 方面的能耗很大。这块使用的 effekseer ,美术一开始只想追求好的效果,使用的比较粗犷。一开始发现它吃 CPU 比较多后,就将其移到了独立服务(可并行的线程)中,每帧耗时(实际帧率)倒是立竿见影的降低下来了,但能耗并无改善。毕竟,做的工作并无减少。

我想到的折衷方案是在镜头之外的粒子发射器就停止工作。这样从画面上来说是不准确的。设想一下一个火焰或烟雾效果,粒子片是逐步弥散开的。如果粒子发射器一旦离开镜头就删掉,重新进入镜头再重新发射,画面上会有些奇怪。但这样的画面 “bug” 我认为是为了性能而必要牺牲的准确性,可以接受。


我觉得只优化引擎代码是不够的。接下来的时间,还需要仔细优化美术资源:尺寸更小的贴图,更低面的模型更简单的动画,应该可以带来更好的性能。