游戏引擎架构 第六章
涉及载入、管理多种资源 因此涉及到 资源管理器
封装引擎私有API的原因:
1.跨平台需求时 对文件系统API的隔离 隐藏不同目标平台间的区别。
2.提供一切所需但文件系统未提供的功能 例如streaming 等等。
路径:一般是由卷盘指示符和路径成分组成 其之间以分隔符分隔 不同操作系统之间的分隔符使用是有差异的。
C:\Program Files\Unity\Hub\Editor
例如UNIX使用‘/’ DOS和早期Windows采用‘\’ 后期的Windows支持了正反斜线符来做分隔路径。
UNIX系不支持以卷盘分开目录层次。整个文件系统都是以单一庞大的层次所组成的。像是一棵树行关系,所以UNIX路径不会出现卷盘指示符的。
所有路径都对应文件系统中的某个位置
当路径相对于根目录,称其为绝对路径
当路径相对于文件系统中的其他目录时,称其为相对路径
UNIX的绝对路径是以分隔符/起始
搜寻路径是包含一串路径的字符串,各路径间以特殊符号分隔 在找文件时会从这些路径进行搜寻。PATH环境变量
常见的如 路径分离目录/文件名/扩展名、路径规范化、绝对路径与相对路径间转换、各平台间目录差异包装等
standard C library 提供两组API以开启、读取和写入文件内容。
其中一组有缓冲buffered 另一组无缓冲unbuffered。
每次调用输入输出时 都需要成为缓冲区的数据区块 来提供程序和磁盘之间传送来源或目的字节。
对于有I/O缓冲功能的函数有时候会成为Stream I/O 这时会把磁盘文件抽象成字节流。
程序发出IO请求后 必须等待读写数据完毕后 程序再继续运行。
streaming
利用另一线程处理I/O 请求。主线程调用异步函数时,会把请求放入一个队列,并且立即传回。
同时,I/O线程从队列中取出请求,并阻塞I/O函数如read()处理这些请求。
在请求工作完成后,就会调用主线程之前提供的回调函数,来通知操作完成。
若主线程选择等待完成I/O请求,会使用信号量 semaphore 来同步状态
(每个请求对弈一个semaphore 主线程把自身处于休眠,等待IO线程完成工作后的新号通知)。
asynchronous 通过回调函数 处理读写完成后的连续操作
需要注意异步加载时 资源加载的优先权 对于优先级更高的资源做优先加载处理 可能情况还需要暂停较低优先权的请求。
两个部分组成
1.负责管理离线工具链,用来创建资产以及把他们转换成引擎可用的形式。
2.在执行期间管理资源,确保资源在使用之前已载入内存,并在不需要时从内存卸下。
版本控制工具 资产 数据量
对于大部分资产来说 引擎并不会直接使用期原本的格式。会对资产做相应的调节转换为引擎所需格式 及 资产调节通道 asset conditioning pipeline。
此时 每个资源都需要有元数据metadata描述如何对资源进行处理。
例如 压缩纹理的压缩类型 动画clip的帧信息处理 模型软件导出的网格信息处理
所以需要这种由各种元数据组成的资源数据库 或者是资源处理配置。记录着如何处理对应的资源。也可以理解为是一种描述文件。
无论资源数据库采用什么形式 必须提供以下功能:
能处理多种类型资源。
能创建新资源。
能删除资源。
能查看及修改现存资源。
能把修改资源的目录位置。
能让资源交叉引用其他资源。交叉引用通常同时驱动资源管理生成过程及运行时的载入过程。
能维持数据库内所有交叉引用的引用完整性。执行所有常见操作后,如删除或移动资源后,仍能保持引用完整性。
能保存版本历史,并含完整日志记录改动人员和事由。
支持不同形式的搜寻和查询。例如资源建引用关系。
需要考虑的常见问题:
对同一时间相同资产的修改,存在合并问题和上锁问题。以及资源的粒度划分。
源文件映射,让用户很容易得出该资源是由那些资产而来。
asset conditioning pipeline ACP
resource conditioning pipeline RCP
tool chain
每个资源管道的开端都是DCC原生格式的源文件(如Maya .ma PS .psd)
这些资产通常会经过3个处理阶段 再到达游戏引擎。
1.导出器 exporter
常是为DCC撰写自定义插件, 功能是把DCC工具里的数据导出为某种中间格式,供后续阶段使用。
2.资源编译器 resource compiler
对上一阶段的数据做一下格式处理。 例如把网格三角形重新排列成三角形带门或是要压缩纹理 。 目的是让数据在导出后可直接被引擎所使用。
3.资源链接器 resource linker
有时 多个资源需要先结合成单个有用的包再载入到引擎(存在依赖关系)。 例如模型 需要把网格材质骨骼贴图动画等等整合好后结合为模型资源。
叫做资源链接 但也是并非素有资源都需要链接 单一素材则不需要了(无依赖)。
每个ACP都需要一组规则来描述资产之间的依赖关系。当某资产作出改动后,有些生成工具可以利用这些依赖关系信息,确保以正确次序为自查进行生成。
功能责任
1.确保任何时候,同一资源在内存中只有一份副本。
维护一个资源注册表,来确保每个资源只有一份副本。可以用字典键值对来记录加载过的资源 guid,内存中资源指针。
2.管理每个资源的生命周期,载入需要的资源,不需要时卸载。
引用计数
3.处理复合类型资源,复合资源是由多个资源组成的资源。
4.维护引用完整性,正确维护资源依赖关系。包括内部引用完整性(单个资源内的交叉引用)及外部引用完整性(资源间的交叉引用)。
有向图 全局资源查找表 指针修正 指针偏移表
5.管理资源载入后的内存用量,确保资源储存在内存中合适的地方。
6.允许按资源类型,载入资源后执行自定义处理。资源载入初始化。
7.常常提供单一同一接口管理。且应该易扩展。
8.支持异步加载 streaming
9.游戏中所有资源都必须有某种全局唯一标识符 GUID 文件路径或是文件把资源层次结构组织起来。
忽略内存碎片问题,仅使用通用的对分配器分配资源所需内存。由系统自行做内存碎片整理和内存分配。
堆栈分配器不会有内存碎片问题,因为内存是连续分配的。释放内存时则根据分配的反方向进行。
需要资源加载是线性且可控。
游戏启动后 ,先分配给常驻留资源。标记栈顶位置,之后便可以通过该标志位做资源释放。载入关卡时在栈顶分配内存,关卡完成后,移回栈顶位置到原标记位。
两个栈定义在一大块内存里。其中一个由内存底端向上 一个从顶端向下。例如:顶端栈做临时分配内存 底端栈做持久数据。
池分配器,把资源数据以同等大小的块载入,因为全部大小相同,资源卸下时不会造成内存碎片。
资源切块 同等大小 一般会存在空间浪费问题。通常不能充分利用资源最后一个块。
某些资源在载入后 需要执行某些特殊操作 以及注意销毁时做特定动作。 多态 构造 析构
载入时同时包含相关和不想管数据的资源时,相关数据会复制到内存最终目的地,不相关数据进行弃置。避免浪费内存。
直接载入到最终内存位置 或者载入到临时内存区域。