这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《游戏引擎实现原理》3.2 GC、内存预算与分配追踪
帧时间稳住了,GC 的一次 Stop-The-World 照样能把它抖起来。本节用 process.memoryUsage() 与 --trace-gc 把内存账本读出来,按子系统定位分配热点,并在独立子进程里实测对象池对 GC 次数、停顿与吞吐的真实收益。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
帧时间稳住了,GC 的一次 Stop-The-World 照样能把它抖起来。本节用 process.memoryUsage() 与 --trace-gc 把内存账本读出来,按子系统定位分配热点,并在独立子进程里实测对象池对 GC 次数、停顿与吞吐的真实收益。
帧预算是性能工程的第一把尺。本节把一帧拆成 logic/submit/present 三阶段,从零实现可注入时钟的 FrameProfiler,在 Node 里真跑 600 帧粒子负载,读出中位、p95、p99 与超预算比例,再用 CPU/GPU 流水线模型说清一帧里谁在等谁。
黄金图像抓画面,回放抓逻辑。本节把「一局游戏」定义为「一个种子 + 一串输入帧」,用全整数模拟保证跨平台逐位一致,再把每帧状态折成哈希:两遍回放逐帧相同,一次改动被精确定位到分叉帧。本机实测同一份 120 帧回放两遍哈希全同,改一个摩擦常数后在第 1 帧即分叉。
NullRHI 只能告诉你「调用了 draw」,画出来长什么样必须真的上 GPU。本节把测试搬进 headless Chromium,用 WebGL2 渲染一个固定场景、落 PNG 存档,再用逐像素比对与容差判定抓住回归。本机实测:同一场景两次渲染零差异,一个 3px 的位移被抓出 622 个差异像素、最大通道差 204。
引擎测试的难点不在断言,而在把外部世界换掉:时间、图形 API、磁盘与随机数。本节用 FakeClock、NullRHI、内存文件系统与种子化 PRNG 四类假对象,拼出一个无 GPU、无磁盘、无真实时钟的内核测试台,让同一关卡每次跑出逐位一致的结果。全部代码在 Node 直跑,17 条用例锁住行为。
全书的最后一节:给出三条能真正走下去的路——去读 three.js、Bevy、Godot、Filament 这些真实引擎的源码该从哪个文件入手,怎么给开源项目提第一个 PR,以及如何按本书四卷的顺序做自己的引擎并用一个自检脚本量进度。同时明确本书不覆盖的边界:地形流式、网络同步、光追与全局光照、体积云与水体,都指向站内既有文章。
全书收官的第一节,把四卷 60 章拉成一张「一个引擎由哪些原理构成」的全景图:内核的内存、句柄、容器、数学、数据导向、ECS、任务与反射,渲染的 RHI、剔除、材质、光照、帧图与后处理,子系统的资源、动画、物理、音频、输入与编辑器,工程端的构建、测试、性能、跨端与裁剪。每一章都给出直达代表节的链接,并用本机实跑的小工具把 helio 的真实体量盘出来。
《微光岛》不可能把整包都塞进首屏。本节写一个分包器:入口 chunk 只装从启动入口静态可达、且不穿过懒加载点的模块,其余按懒加载根切成独立 chunk。实测首包 19517 字节(gzip 6558)、整包 29212 字节;不上提共享依赖则整包膨胀到 47439 字节。
同一张 256×256 地形贴图,在显存里是 262144 字节的裸 RGBA,无损 PNG 压到 64992 字节,BC1 块压缩只留 32768 字节但 PSNR 掉到 37 dB。本节用 Node 从零实现 PNG 编码器与 BC1 编解码,量出三种产物的真实体积、压缩率与解码耗时,全部本机实跑。
卷四开篇,先把前三卷的 helio 引擎变成一个可分析的产物:用 module-graph 扫出 187 个模块的依赖图,量出「源码 → 打包 → 压缩」三段真实体积,再用符号级 tree-shaking 删掉入口用不到的顶层导出。全部代码在 Node 直跑,拼出来的 bundle 与原始 demo 逐位相同。
公平与手感之间的取舍。本节讲延迟补偿的常见做法与服务端回滚判定所需的客户端配合,把「双人竞速各跑各的」讲透——纯计时不需要补偿,只有碰撞干扰才需要,并给出带死区与上限的 L1 补偿决策函数、配套单测,以及一张把零补偿到无上限动态补偿逐项对比的公平与手感权衡表。
延迟、掉线、重连该怎么告诉玩家,以及哪些网络数据不该给玩家看。本节给出网络质量档位到图标与文案的映射表、提示的时机与频率控制、无障碍与本地化考虑,并落到一个订阅会话状态与诊断数据的 L2 UI 组件,以及一个可单测、覆盖多档位边界的 L1 档位映射函数。
前几章把插值与预测写成了可测的纯逻辑,本节把它接到 Cocos 节点与帧循环:讲清逻辑 60 Hz 定点与渲染浮动帧率的分工,远端玩家如何用插值位置驱动 setPosition、用插值速度切换动画,为什么特效必须由服务端事件触发而非状态推断,并给出 L1 纯函数插值器与 L2 组件脚本。
客户端侧的加密配合与它的边界:wss 传输层加密在 H5、微信小游戏、原生三端的支持情况与证书要求,消息校验和与 HMAC 能防网络中间人篡改、却防不了本地改客户端,客户端该做与不该做的一整张职责清单,最后回到「真正的裁决在服务端」这条底线。
客户端不可信,所以这一节校验的是服务端下发的快照:字段范围、时序合理性、序号连续性、与本地预测的偏差四类检查,配上接受、平滑修正、丢弃、上报四种处理策略,并明确客户端校验只保可用性与健壮性、不阻止作弊,最后给出可跑的快照校验器、假传输层与 vitest 单测。
把《方块冲刺》的每个游戏事实逐一归类到权威方:位置、速度、输入、计时、得分、掉落、胜负各自归谁管、客户端被改会怎样,给出「客户端只发意图、不发结果」的原则,讲清客户端预测为什么不是客户端权威,再用一张权威边界表与可跑的单测把边界钉进协议与代码。
线上卡顿与不同步不能靠猜。本节先给出一张从症状到原因的排查表(橡皮筋、远端瞬移、逐渐漂移、周期性卡、特定网络必现),再说明客户端该落哪些日志、怎么采样控制体积,并用纯 TS 写出固定容量的环形日志缓冲与按帧号关联输入和快照的追踪,最后把线上抓到的网络序列喂回 7.1 的模拟器复现。
机器人压测不是拿真玩家去撞服务端,而是把客户端逻辑层抽出来,在 Node 里同时驱动成百上千个无渲染的机器人,用它们压自己这套模拟与消息处理。本节给出机器人客户端与脚本化行为的纯 TS 实现、只回最小快照的假服务端测试替身、用 vitest 断言规模下的单帧耗时预算,并说明压测结论该怎么写才可信。
本地复现线上卡顿的前提,是能造出一模一样的弱网。本节把弱网拆成延迟、抖动、丢包、乱序、带宽与突发中断六种病,给出一个可配置、可脚本化的弱网 Transport 装饰器,用固定随机种子保证可复现,并用 vitest 断言丢包率与延迟分布落在设定区间,最后列出诊断面板该采集的指标。
观战与回放都不产生输入,只把同一场比赛在本地重演。本节对比观战与参战的数据流差异,讲观战为何只需要插值、不需要预测,说明回放该录输入流与随机种子而非状态,用纯 TS 写出可单测的回放器(记录加重演、关键帧快照支持拖动进度),并估算一局录像的体积。