《游戏引擎实现原理》7.1 场景图与变换层级(ECS 之上的一层视图)
把 ECS 的扁平组件数组解释成一棵场景树:给每个实体补一个父索引,按拓扑序算出 world 矩阵,再展平成渲染器直接消费的实例列表,并在 headless Chromium 里用一次 instanced draw 画出全部实例、回读像素核对位置。
category
把 ECS 的扁平组件数组解释成一棵场景树:给每个实体补一个父索引,按拓扑序算出 world 矩阵,再展平成渲染器直接消费的实例列表,并在 headless Chromium 里用一次 instanced draw 画出全部实例、回读像素核对位置。
采样器不占显存,却决定纹理越界、缩放、倾斜时怎么被读:clamp / repeat / mirror 三种寻址、nearest / linear 两种过滤、以及各向异性的收益与代价;并用同一张 4x1 纹理实测三种寻址模式下的边缘像素回读。
把纹理的显存账算清楚:颜色 / sRGB / 深度格式各占多少字节、BC / ETC2 / ASTC 三族块压缩如何取舍、mipmap 链为什么让显存多出约三分之一;并实测 WebGPU 上哪些压缩格式必须靠扩展才能创建、哪些属于核心格式开箱即用。
把顶点描述成「格式 → 偏移 → arrayStride → shaderLocation」这条可运行的数据链:写出 helio 的顶点布局模块,用它程序化生成《微光岛》一块低多边形地形,并在 headless Chromium 里上传、回读、重算包围盒。
NullRHI 是 helio 的第三个后端:它不接触 GPU,把每一次 RHI 调用按顺序记进日志。于是渲染代码的调用序列变成可在 Node 里断言的纯数据,单测与 CI 不需要显卡、也不会因驱动差异而抖动。本节用同一段渲染代码分别跑 WebGPU 与 NullRHI,断言后者产出的调用日志。
同一套 RHI 接口的 WebGL2 实现:把 bind group 映射成 uniform block 与 texture unit、render pass 映射成 framebuffer object、WGSL 换成 GLSL ES 3.0。更关键的是能力降级——WebGPU 有而 WebGL2 没有的特性如何优雅退化,并用 clearColor 加 readPixels 真跑出像素断言。
helio 的 RHI 抽象在 WebGPU 上的落地:把资源、管线、命令、绑定四组接口逐条翻译成 WebGPU 调用,用自增整数句柄隔离后端细节,并真跑一次创建资源、录制渲染通道、提交队列、回读像素的完整流程,断言中心像素与清屏角像素,作为后端正确性的最小证据。
资源能传上去之后,一帧里反复创建销毁 buffer/texture 会把驱动拖垮、把显存切碎。本节复用卷一的大小类思路做一个 GPU 内存池,配显式预算与超限策略,统计复用率与峰值占用,并在真实 WebGPU 设备上跑三帧验证第 2/3 帧确实复用了第 1 帧创建的 buffer。
上一节把资源绑进了管线,但资源本身还没进 GPU。本节讲 writeTexture 与 copyBufferToTexture 两条上传路径,撞上 WebGPU 最著名的 bytesPerRow 必须对齐 256 字节的坑并抓出真实报错,再用一条 textureLoad 的 box filter 通道逐级生成 mipmap 并回读断言。
着色器怎么知道去哪拿 uniform、贴图和采样器?本节讲 BindGroupLayout 与 BindGroup 如何和 WGSL 里的 @group/@binding 一一对应,先写一个从着色器注解反射出布局的纯 TS 模块,再在真实 WebGPU 上跑通「uniform + texture + sampler」三合一绑定并回读像素验证。
GPU 可能还在读帧 N 的 uniform,而 CPU 已经在写帧 N+1——直接复用同一块 buffer 会画错。本节解释为什么需要 2~3 份 uniform buffer 与 bind group,并用纯 TypeScript 实现一个环形分配的三缓冲池,在复用仍在飞的槽位时立刻报错,把竞态变成可断言的异常。
queue.submit 只是把命令缓冲排进 GPU 的队列,真正「做完了」要靠 fence 通知 CPU。本节说清 GPU 与 CPU 的同步点在哪,用纯 TypeScript 实现一个记录在飞帧的 FrameRing,并写出「每帧 await」与「流水线」两种策略的确定性时间线,实测出两者的吞吐差异。
命令编码器把一帧的 GPU 工作录成命令缓冲:beginRenderPass 开通道、setPipeline 绑管线、draw 提交绘制、end 收尾。本节先用纯 TypeScript 把这套顺序约束做成可断言的录制器状态机,再在真实 WebGPU 设备上录一条渲染通道,清屏后画三角形并把像素回读出来验证。
一条管线状态对象把顶点布局、图元拓扑、深度与混合状态、绑定布局全部钉死;着色器模块则是它的 vertex / fragment 两半。本节给出 helio 的 PSO 描述符,然后在真实 WebGPU 上真编译一份 WGSL,断言 getCompilationInfo() 零错误,再故意写错一处,验证诊断能定位到具体行列。
渲染层的一切最终都是三类 GPU 资源:缓冲、纹理、采样器。本节给出 helio 的资源描述符与使用标志(与 WebGPU 逐位一致),用纯逻辑校验行距对齐与 mip 链长度,再在真实 WebGPU 上创建一块 buffer、写入、读回,逐字节断言两条上传路径的结果一致。
从零实现渲染器的第一件事是划一条硬件抽象线。本节讲清 WebGPU、WebGL2 与 NullRHI 三个后端在特性、格式与上限上的真实差异,给出 helio 的 RHI 统一词汇(仅类型接口),并用一张能力查询表把「能不能做 X」变成一次查表,最后在 headless 浏览器里真探测一次设备的 limits。
本卷和站内 graphics 专题到底谁写什么?本节用一张逐条对照的三列表,把 graphics 的 16 篇文章各自负责的算法与原理,和本卷只做的「在 helio RHI 上实现并跑出真实产物」一一对齐,并把这张表编码成一个可运行的路由函数,让「越界」变成一条可断言的失败用例,而不是一句口号。
上一节看到 CPU 与 GPU 是两条流水线,那渲染器该怎样分层来管好它们?本节把渲染器拆成 RHI(硬件接口)、渲染器(帧组织)、场景(数据)三层,给出 helio 的目录与接口草图,并用一个可运行的依赖方向守卫,把「只能向下、不能成环」这条铁律钉成编译器与测试都能把关的断言。
渲染器的第一课不是画三角形,而是搞清楚一帧里 CPU 和 GPU 谁在等谁。本节用离散事件模型模拟 CPU 提交与 GPU 执行的流水线,算出单/双/三缓冲下的稳态帧率与延迟,再在真实 WebGPU 设备上量出 CPU 提交与 GPU 排空的耗时,用数字说明为什么「CPU 帧」与「GPU 帧」不是同一件事。
《游戏引擎实现原理》卷一附录 C:汇总全书性能基准的原始数字与测量方法。给出 SoA vs AoS、连续遍历 vs 指针追逐、对象池 vs new 三组实测数据(含中位数、堆增量与 IQR),讲清进程隔离、预热、hrtime 计时与 21 样本中位数统计这套方法论,并诚实声明这些是同机相对量级、绝对数值不可移植。