Games104学习笔记(2)
GAMES104:引擎架构分层与整体 Pipeline
课程:GAMES104《现代游戏引擎:从入门到实践》
讲次:Lecture 02 / Layered Architecture of Game Engine
讲者:王希(Xi Wang,Booming Tech)
视频时长:约 66 分 51 秒
笔记性质:根据本地视频中的幻灯片与讲解主题整理,不是逐字稿;时间点为复习定位用的近似值。
一句话理解
现代游戏引擎通常采用分层架构:越靠下的层越稳定、越接近硬件,越靠上的层越面向内容和玩法、越容易定制。各层通过清晰的接口协作,把硬件、内存、资源、功能、编辑器和游戏内容连接成一条完整的生产与运行 Pipeline。
游戏项目 / 游戏内容
↑
Tool Layer 编辑器、DCC、资产处理、调试工具
↑
Function Layer 渲染、动画、物理、Gameplay、网络等功能
↑
Resource Layer 文件、资源导入、加载、缓存、生命周期
↑
Core Layer 数据结构、容器、内存、数学和通用基础设施
↑
Platform Layer 操作系统、CPU、GPU、内存、文件和平台 API
旁路:第三方库可以接入多个层,为某一类能力提供专门实现。
本讲学习目标
- 理解为什么大型游戏引擎需要分层,而不能把所有功能写在一起。
- 掌握 Tool、Function、Resource、Core、Platform 五层的职责和边界。
- 理解“资产制作文件”如何经过处理,变成运行时可快速访问的资源。
- 认识资源生命周期、Tick、多线程、容器和内存管理这些跨层问题。
- 知道硬件架构为什么会影响引擎设计。
1. 为什么需要分层架构
1.1 控制复杂性
游戏引擎同时包含图形、物理、动画、音频、AI、网络、资源、工具和平台适配等大量模块。分层可以把复杂系统拆成若干组职责明确的子系统:
- 上层使用下层提供的能力。
- 下层不应该依赖上层具体游戏玩法。
- 每一层可以在相对独立的边界内演进。
- 平台差异不会扩散到所有业务代码。
1.2 稳定性与可定制性的权衡
本讲的核心结论之一是:
越底层的代码越稳定,越上层的代码越容易定制。
例如,内存分配、容器和平台抽象需要被大量系统共同依赖,频繁变化会造成巨大影响;而编辑器、玩法和具体游戏内容则需要不断调整,以适应项目需求。
1.3 通过接口隔离变化
分层并不意味着每一层完全封闭,而是要求依赖方向和接口清晰:
- 上层不直接操作底层硬件细节。
- 下层不应该知道某个具体游戏的关卡规则。
- 系统之间通过数据和接口交互,而不是互相访问内部实现。
- 当底层替换实现时,上层尽量不需要重写。
2. 五层架构总览
| 层 | 主要职责 | 典型内容 | 稳定性 / 定制性 |
|---|---|---|---|
| Tool Layer | 服务内容生产和开发协作 | 编辑器、DCC、资产处理、调试工具 | 定制性最高 |
| Function Layer | 提供游戏运行所需的功能系统 | 渲染、动画、物理、Gameplay、网络 | 面向项目扩展 |
| Resource Layer | 管理数据和运行时资源 | 导入、加载、缓存、依赖、生命周期 | 连接工具与运行时 |
| Core Layer | 提供通用底层基础 | 容器、数据结构、内存、数学、线程基础 | 稳定性较高 |
| Platform Layer | 屏蔽平台和硬件差异 | OS、CPU、GPU、文件、窗口、图形 API | 最底层、最稳定 |
此外,第三方库不一定属于某一个单独层。例如 Havok / PhysX 主要提供物理能力,Wwise / FMOD 主要提供音频能力,TrueSky、SpeedTree、Simplygon 等则提供天空、植被或几何处理能力。引擎需要负责把它们接入自己的资源、线程、内存和平台体系。
3. Platform Layer:平台层
3.1 职责
平台层位于架构底部,处理引擎与具体运行环境之间的差异:
- 操作系统和系统调用。
- 文件系统、窗口和输入设备。
- CPU、GPU、内存和缓存结构。
- 图形 API、音频 API 和线程 API。
- 不同平台的编译、打包和发布方式。
- 硬件资源限制和平台专有能力。
上层应尽量通过平台层提供的统一接口工作,而不是在渲染、资源或 Gameplay 代码中到处写平台特判。
3.2 硬件架构会影响引擎设计
视频以 PlayStation 3 的硬件架构作为例子,展示了:
- PPU 与多个 SPU 的处理器结构。
- SPU 的 Local Store。
- 系统内存与显存。
- DMA 控制器和数据搬运路径。
- RSX GPU。
- CPU、GPU、内存之间的带宽和访问方式。
这个例子说明,游戏引擎不是运行在抽象的“无限快计算机”上。数据应该如何布局、何时搬运、哪个处理器执行、是否适合并行,都与具体硬件有关。
需要区分两类常见内存组织:
- 分离式内存:系统内存和显存相对独立,需要考虑数据上传和同步。
- UMA(Unified Memory Access):CPU 和 GPU 共享或统一访问内存,数据交换方式不同,但仍需考虑带宽、缓存和访问竞争。
3.3 平台层的设计意义
平台层的目标不是抹平所有差异,而是把差异集中管理。这样:
- Function Layer 可以使用统一的渲染、输入和线程接口。
- Resource Layer 可以使用统一的文件和异步 IO 接口。
- 工具和游戏逻辑不必了解每个平台的硬件细节。
- 针对特定平台的优化仍然可以在底层保留。
4. Core Layer:核心层
核心层提供几乎所有上层都会使用的通用基础设施。本讲重点介绍了数据结构 / 容器和内存管理。
4.1 数据结构与容器
引擎中常见的数据组织包括:
- 数组和向量。
- 哈希表、映射和集合。
- 树、图和层级结构。
- 骨骼树。
- 动画帧序列。
- 场景对象、资源依赖和任务依赖。
引擎可能需要自己实现或封装专用容器,原因包括:
- 标准容器的内存行为未必适合实时系统。
- 需要更好地控制容量增长、对齐和分配策略。
- 需要保证数据连续,提高 CPU 缓存命中率。
- 需要支持序列化、反射、调试或跨平台行为。
设计数据结构时,不只要问“查找复杂度是多少”,还要问:
- 数据是否连续?
- 访问模式是否稳定?
- 是否会频繁分配和释放?
- 是否会跨线程访问?
- 是否适合批量处理?
4.2 内存管理
内存管理不只是防止内存不够,也是游戏性能的重要组成部分。常见问题包括:
- 频繁分配和释放带来的开销。
- 内存碎片化。
- 数据分散导致缓存未命中。
- 不同资源的大小和生命周期差异很大。
- CPU 和 GPU 之间的数据存放、上传与同步。
引擎可能使用以下策略:
- 自定义 allocator 和内存池。
- Arena / Linear Allocation 等批量分配方式。
- 按资源类型或生命周期分组分配。
- 尽量让相关数据放在一起,提高局部性。
- 通过内存统计、追踪和调试工具定位泄漏与峰值。
- 使用 PMR(Polymorphic Memory Resource)等机制,将容器与分配策略解耦。
核心思想是:内存布局和访问方式会直接影响实时性能。引擎的内存系统应当让上层能够预测、观察和控制资源使用。
- 相关的放一起
- 顺序访问读写
- 批量同次删除
5. Resource Layer:资源层
资源层负责把各种文件和数据,变成运行时可以高效使用的资源。
5.1 引擎面对的数据与文件
内容团队会产生很多不同类型的源文件,例如:
- 模型:Maya、3ds Max、Blender 等工具产生的模型文件。
- 纹理:PSD、PNG 等图像文件。
- 动画:骨骼、关键帧和动画剪辑数据。
- 音频:MP3、WAV 及其他音频数据。
- 场景与关卡:场景文件、关卡文件和对象布局。
- 脚本与图表:脚本、节点图和游戏逻辑数据。
这些文件的格式、用途和编辑方式不同,不能简单地原样交给运行时处理。
5.2 离线资源导入
常见的资源 Pipeline 是:
DCC / 外部源文件
↓
导入与解析
↓
Asset Conditioning Pipeline
↓
转换、压缩、优化、建立依赖
↓
引擎内部资源 / 运行时资源
↓
加载、缓存和使用
离线处理的好处是把昂贵的解析、转换和优化工作放到运行前完成,避免玩家启动游戏时再解析复杂的制作格式。
运行时资源通常会:
- 使用引擎内部更适合读取的格式。
- 预先建立依赖关系。
- 进行压缩、烘焙或平台特化处理。
- 将多个相关数据组织成适合批量读取的资源。
- 为每个资源生成稳定的唯一标识。
5.3 GUID:稳定地引用资源
资源不能只依赖文件路径来引用。路径可能发生移动、重命名或重新组织,因此引擎通常会为资源分配 GUID(全局唯一标识符)。
GUID 的作用包括:
- 稳定地标识资源。
- 记录资源之间的依赖关系。
- 在资源移动后仍能找到原对象。
- 让编辑器、资源数据库和运行时保持一致。
5.4 资源访问
资源访问通常需要经过资源管理器,而不是由每个系统自行打开文件:
系统请求资源
↓
Resource Manager 根据 GUID 查找
↓
检查缓存 / 依赖 / 当前状态
↓
必要时异步加载和转换
↓
返回运行时资源句柄
这样可以统一处理缓存、引用计数、异步加载、错误报告和资源释放。
5.5 资源生命周期
不同资源的生命周期不同:
- 启动时就需要的核心资源。
- 进入关卡后才需要的场景资源。
- 只在某个特效或事件期间使用的临时资源。
- 可以跨关卡复用的共享资源。
- 占用显存较大的纹理、模型和 GPU 资源。
有限内存要求引擎在适当时机释放不再需要的资源。常见机制包括:
- 按需加载。
- 延迟加载或异步加载。
- 缓存和淘汰。
- 引用计数或句柄管理。
- 分阶段加载和卸载。
- 垃圾回收或其他回收策略。
- 对 CPU 资源和 GPU 资源分别管理。
资源生命周期管理的目标是平衡三件事:
加载速度 ↔ 内存占用 ↔ 运行时访问效率
6. Function Layer:功能层
功能层提供游戏世界运行时需要的具体系统。典型内容包括:
- 渲染。
- 动画。
- 物理。
- 音频。
- AI 和导航。
- Gameplay 和脚本。
- 相机、特效和网络。
本讲重点放在功能系统共同面对的两个问题:Tick 调度和多线程执行。
6.1 Tick:让虚拟世界随时间变化
一个静态数据文件不会自动变成游戏世界。引擎需要不断推进时间、读取输入、更新对象并产生新的输出。这个周期性执行的更新过程通常可以抽象成 Tick。
一帧开始
↓
读取输入和时间
↓
执行各系统 Tick
↓
更新世界状态
↓
准备并执行渲染
↓
提交画面和其他输出
视频最后的 Takeaways 强调:
虚拟世界由一组时钟(clocks)和 Tick 组成。
这意味着不同系统可能有不同的更新节奏和依赖关系:
- 有些系统需要每帧更新。
- 有些系统可以降低更新频率。
- 有些系统需要固定时间步长。
- 有些系统必须等待另一个系统完成。
- 有些工作适合异步或后台处理。
6.2 Tick 的设计问题
设计 Tick 系统时,需要考虑:
- 执行顺序是否正确。
- 数据依赖是否明确。
- 是否存在循环依赖。
- 每个系统是否真的需要每帧运行。
- 更新和渲染之间是否会读写冲突。
- 多线程时是否需要同步。
- 时间步长变化时结果是否稳定。
如果所有对象都简单地在主线程中依次更新,容易理解但扩展性和性能有限;如果完全并行,又会引入依赖、同步和确定性问题。
6.3 多线程演进
视频展示了从简单到高级的几种执行方式:
- 固定入口线程:所有工作集中在一个主线程,结构简单,但难以充分利用多核。
- 主线程 + 线程池:把部分独立工作交给工作线程。
- 任务 / Job System:把工作拆成更细的任务,按依赖关系调度到多个核心(coming in future)。
任务 A ─────┐
├─→ 任务 C ─→ 任务 E
任务 B ─────┘
↑
多核任务调度器
多线程的关键不只是“创建更多线程”,还包括:
- 任务粒度是否合适。
- 工作负载是否均衡。
- 任务之间的依赖如何表达。
- 共享数据如何同步。
- 是否会产生锁竞争和缓存抖动。
- 主线程是否会等待后台任务。
- 结果是否需要保持确定性。
因此,现代引擎的多线程架构通常需要把系统、数据和 Tick 一起设计。
7. Tool Layer:工具层
工具层是引擎面向创作者的一面,负责把底层能力转化为可操作的生产工具。
7.1 DCC 与编辑器
视频展示了两类工具:
- DCC(Digital Content Creation):Maya、3ds Max、Blender、Houdini、FMOD 等外部内容制作工具。
- Engine Editors:关卡、材质、蓝图 / 图表、动画、特效和其他引擎编辑器。
工具层的目标不是让所有人都直接阅读引擎源代码,而是让不同角色用各自熟悉的方式创作内容。
7.2 Asset Conditioning Pipeline
DCC 和引擎之间需要一个资产处理流程:
- 识别和解析外部格式。
- 检查资源合法性。
- 转换坐标系、单位、材质或动画数据。
- 生成引擎内部格式。
- 压缩和优化资源。
- 建立 GUID 和依赖关系。
- 针对目标平台生成不同版本。
- 在资源发生变化时重新处理相关内容。
这条 Pipeline 是“内容制作”和“运行时资源”之间的桥梁。
7.3 游戏内容与引擎的关系
视频中的结构示意可以概括为:
Game
↑ 使用
Game Engine
↑ 由工具和资源 Pipeline 服务
Editors + DCC + Asset Conditioning Pipeline
游戏项目不应该把引擎核心改得面目全非;更理想的方式是使用引擎提供的接口、资源格式、编辑器和扩展机制,构建自己的游戏内容。
8. Third-party Libraries:第三方库与中间件
游戏引擎可以复用成熟的第三方库,把有限的工程资源集中到自己的核心竞争力上。本讲展示的例子包括:
| 方向 | 例子 |
|---|---|
| 物理 | Havok、PhysX |
| 音频 | Wwise、FMOD |
| 天空与环境 | TrueSky |
| 植被 | SpeedTree |
| 光照或渲染辅助 | Enlighten |
| 几何处理 | Simplygon |
引入第三方库时,不能只看“功能能不能用”,还要考虑:
- 数据格式是否兼容。
- 内存分配是否统一。
- 线程模型是否冲突。
- 资源生命周期如何接入。
- 平台是否都支持。
- 调试、授权和版本升级如何管理。
第三方库是引擎生态的一部分,但整合责任仍然在引擎架构者手中。
9. 本讲最重要的结论
9.1 分层不是形式主义
分层的价值在于管理变化:平台和核心基础变化慢,功能和资源变化适中,工具和游戏内容变化快。把它们混在一起,会让任何修改都变得昂贵。
9.2 资源 Pipeline 是引擎的生命线
美术工具中的文件不能直接等同于运行时资源。导入、转换、压缩、依赖、GUID、缓存和释放,决定了内容能否稳定、高效地进入游戏。
9.3 数据布局和内存访问是性能问题
实时引擎的性能不仅由算法复杂度决定,也由数据是否连续、缓存是否命中、分配是否频繁、CPU / GPU 之间是否需要搬运等因素决定。
9.4 Tick 是世界时间的组织方式
虚拟世界不是一次性计算完成的,而是由许多时钟和 Tick 持续推进。Tick 的顺序、频率、依赖和线程安排,是功能层设计的基础。
9.5 多线程必须和数据依赖一起考虑
真正的并行化不是把函数随意放进线程,而是把工作拆成可调度任务,并明确数据所有权、依赖关系、同步方式和负载均衡。
9.6 工具层决定引擎能否被团队使用
一个引擎如果只有底层功能,没有好的编辑器、资源导入和协作流程,就很难转化为团队生产力。
10. 复习用速记
五层:Tool → Function → Resource → Core → Platform
下层:更稳定、更通用、更接近硬件
上层:更灵活、更面向内容、更容易定制
资源 Pipeline:
DCC / 源文件 → 导入处理 → 内部资源 → 运行时加载 → 缓存 / 释放
性能关键词:
数据局部性、缓存命中、内存池、资源生命周期、批处理、任务依赖
时间关键词:
Clock、Tick、更新顺序、更新频率、固定步长、同步
工具关键词:
Editor、DCC、Asset Conditioning Pipeline、GUID、Schema、调试
11. 自测题
- 为什么越底层的层通常越稳定,越上层的层通常越容易定制?
- Platform Layer 和 Core Layer 的边界分别是什么?
- 为什么不能让运行时直接读取所有 Maya、PSD 或其他 DCC 源文件?
- GUID 解决了单纯使用文件路径引用资源时的什么问题?
- 资源管理器为什么需要同时处理加载、缓存、依赖和释放?
- Tick 系统需要考虑哪些顺序、频率和依赖问题?
- 从主线程到线程池,再到 Job System,演进的主要动机是什么?
- 多线程为什么会引入锁竞争、数据竞争和确定性问题?
- 为什么引擎需要关注容器的数据连续性和缓存命中率?
- 第三方库接入引擎时,除了功能,还要检查哪些工程问题?
- DCC、Editor 和 Asset Conditioning Pipeline 分别扮演什么角色?
- 你在 Pilot Engine 中会按什么顺序寻找主循环和各层代码?
12. 术语表
| 术语 | 含义 |
|---|---|
| Layered Architecture | 分层架构,将系统按职责和依赖关系拆成多层 |
| Pipeline | 数据或工作经过多个阶段逐步处理的流程 |
| Platform Layer | 屏蔽操作系统和硬件差异的底层平台层 |
| Core Layer | 提供容器、内存、数学等公共基础的核心层 |
| Resource Layer | 负责资源导入、访问、缓存和生命周期的资源层 |
| Function Layer | 提供渲染、动画、物理和 Gameplay 等运行时功能的层 |
| Tool Layer | 面向内容生产、编辑和调试的工具层 |
| DCC | Digital Content Creation,数字内容创建工具 |
| Asset Conditioning | 将外部资产转换为引擎内部运行时格式的处理流程 |
| GUID | Global Unique Identifier,全局唯一资源标识 |
| Tick | 系统推进一小段时间并更新状态的过程 |
| Job System | 将工作拆成任务并按依赖关系调度到多核执行的系统 |
| Cache Locality | 数据访问的局部性,常用于提高 CPU 缓存命中率 |
| Memory Pool | 预先分配并重复利用的一组内存区域 |
| PMR | Polymorphic Memory Resource,多态内存资源机制 |
| UMA | Unified Memory Access,统一内存访问 |
| Middleware | 为引擎提供专门能力的第三方中间件或库 |
13. 参考资料
- GAMES104 官方课程主页
- GAMES104 官方课程 PPT 与视频页面
- 推荐阅读:Jason Gregory,《Game Engine Architecture / 游戏引擎架构》