在 UE5 中 PAK 打包时发现了每个 PrimaryAssetLabel 都会生成 3 个文件:.pak,.utoc,.ucas。
这对 Chunkloader 测试造成了影响,在 Mount 的时候将无法正确挂载,从存储服务器下载的 .pak 需要依赖 .utoc 及 .ucas 文件,否则我们需要将 .utoc 及 .ucas 文件上传到 OSS 进行完整下载和校验。
在 “Project Settings>Packaging” 有一个选项 “ Use Io Store” 导致了 .utoc 及 .ucas 文件的生成。
该选项到底有什么作用呢,引擎提示说是 “将所有包放入一个或多个容器文件中”。容器文件是谁,似乎没有解释。但是启用该选项会减少大量加载资产时间。PAK 包也会变的更大。
实际这是 UE4.25.4 推出的新的加载系统中处理高速加载的新特性。 IO Store 仍然是一个实验性功能,所以未来配置和细节可能会发生变化。
【IOStore 概述】
- 从 UE4.25 实现的新加载系统的一部分
- 能够存储 IO 信息
- 提高 IO 性能和加速加载
- IO Store 对启用了功能的打包应用程序有效
Unreal 的加载系统路线中,在 UE4.25 Experimental 版本之前采用了 AsyncLoader 的传统加载方式,之后采用 AsyncLoader2 新的加载方式。
新的加载方式中如果是编辑器行为,默认将不启用 IO Store 编译构建项目,否则默认采用 AsyncLoader2 新的加载方式,启用 IO Store 构建项目。
什么是 AsyncLoader2 呢?在 UE4.26 预览版中有如下描述:
Memory Insights (Beta). For users wanting an insight into memory usage, we are adding Memory Insights to help developers better understand how their work impacts performance and engine behavior.
Garbage Collection. We are improving garbage collector performance by reducing FArchive usage in favor of fast reference collection.
Zen Loader (Beta). Next-generation consoles will have improved load times, a new input/output mechanism that offers a low-overhead interface for data access. The APIs will interface with next-generation storage APIs that will enable data to be read directly into target memory with minimal CPU overhead.
Zen Loader 是新的下一代运行时加载器。作为大幅缩短下一代游戏机加载时间并更好地利用新的更快硬件的计划的一部分,它基于 EDL 加载器,但经过重构以减少开销,并具有新的 IO 机制,为数据访问提供低开销接口。
这是一种新的输入/输出机制,可为数据访问提供低开销接口,改进加载时间。这些 API 将与下一代存储 API 接口,使数据能够以最小的 CPU 开销直接读入目标内存。
那么 IO Store 的作用是怎样呢?
在 UE4 包的创建流程( IOStore = OFF时)Build ->Cook ->Stage ->Package->Archive 中 ,Stage 阶段负责将多个 .uasset 合并成一个文件进行加密和压缩(.pak)。
(IOStore=ON) 时,Stage 阶段负责将多个 .uasset 合并成 .pak .utoc .ucas 三个文件进行加密和压缩,并且提供了 Zen Loader 资产加速。
如何使用 IO Store 进行 Zen Loader 资产加速呢?
创建一个支持 IO Store 的 PAK 包,需要先在 “Project Settings>Packaging”中启用 “IO Store”,然后为项目创建 PAK 包。
有如下 3 种方式,可选择其中任何一种:
- 在项目设置中启用 “Use PakFile” 和 “Use Io Store”
- 在 Project Launcher 项目启动器 Profile 的 Cook 中启用 UnrealPak 和 I/OStore,包括启用 “Store all content in a single file(UnrealPak)” 将所有 Cook 的资源内容放到一个 .pak 文件中,移动平台不管是否勾选该选项都会将资源文件放到 .pak 文件。以及启用 “use container files for optimized loading (i/o store)” 。注 UE5 该方式可忽略。
- 通过 UAT 编译的 “BuildCookRun” 在运行时指定 “-iostore” 和 “-pak”
“IO Store = OFF” 或 “IO Store = ON” 包创建后文件结构的差异?
“IO Store” 关闭时会在构建路径的 Paks 目录中生成 .pak 文件;
“IO Store” 开启时除了 .pak 文件之外,还会在构建路径的 Paks 目录中添加了 .ucas 和 .utoc 文件,以及 global.ucas, global.utoc 文件;
其中 .pak 是打包文件, .ucas 是容器文件, .utoc 是目录文件。
.pak:打包文件
- IOStore = OFF 时存储所有资产文件
- IOStore = ON 时存储不常访问的文件,包括 .ini, .ushaderbytecode, .uproject 等的保存,并将之前保存的资产文件移动到 .ucas
.ucas:文件资产容器,存储资产文件(.uasset、.umap、.ubulk、.uptnl、uexp),作用有以下:
- 资产信息 (FIoBuffer) 中 16Byte 对齐顺序的性能提升
- 通过有效排列资产数据来加速文件访问
.utoc:目录文件,存储目录(TOC:目录)信息,作用有以下:
- 链接 .ucas 和包的目录信息
- 包含标头信息 (FIoStoreTocHeader):Magic, Header Size, Entry Count, Entry Size, Padding, 以及资产信息 (FIoStoreTocEntry): ChunkId, OffsetAndLength。
启用 IO Store 时,会在项目文件结构之外单独添加一个全局的 .ucas / .utoc 文件。
global.ucas:全局容器文件,包含从容器加载包所需的信息。IoChunkType 和 Contents 如下:
- LoaderGlobalMeta 全局元信息表,存储关于加载包的信息。
- LoaderInitialLoadMeta 初始加载元信息表,将包与包关联的信息。
- LoaderGlobalNames 全局名称表,加载时将 bundle 与对象名称关联的名称表。
- LoaderGlobalNameHashes 全局名称散列,加载时将捆绑包与对象名称关联的散列表。
global.utoc:全局目录文件,这只是一个未使用的头信息。
“IO Store” 能优化加载速度到一个什么样的程度,效果如何?
采用官方的 Elemental Demo, Infiltrator Demo, 及 Kite Mode 分别进行 “ IO Store = OFF” 和 “ IO Store = ON” 的测试,测试条件如下:
- 使用样本测量启动级加载时间
- Win64 / 测试构建包,使用 Unreal Insights
示例的验证结果中,启用 "IO Store" 改善加载时间分别为 28%(OFF 时为 1.23,ON 时为 0.88),28%(OFF 时为 2.62,ON 时为 1.88), 5%(OFF 时为 6.30,ON 时为 5.98) 。
【IO Store 和 NewLoader】
传统的 AsyncLoader 的加载流程:
Game Thread 在资产处理中会向 AsyncLoading Thread 异步加载线程请求 Load 资产并等候,
传统的 AsyncLoading Thread 会控制后台负载,向 PoolThread 线程池请求加载资产数据并等候,
Pool Thread 主要负责文件读取控制, 对 .pak 进行查找 & 读取,当 PoolThread 查找并读取到 .pak 数据时,会向 AsyncLoading Thread 发出通知并返回加载资产数据,
AsyncLoading Thread 拿到加载的资产数据通过 Complete 回调给 Game Thread ,Game Thread 异步加载线程接收到返回的数据并通过 Complete 回调通知游戏 .pak 数据加载完成。
使用 AsyncLoader 加载和文件访问:
GameThread 向 AsyncLoading 发出 LoadPackage 加载和文件访问请求,LoadPackage 为一个加载包线程。
AsyncLoading 会创建 QueuePackage 加载包队列(包含多个 AsyncPackage 异步加载包线程),然后加入到 EventQueue 线程事件队列(包含多个 AsyncPackage)中进行等候,同时创建一个 CreateLinker 链接器到 AsyncArchive 异步归档线程进行异步归档,AsyncArchive 会启动线程池中的 PAK 查找和读取的线程进行加载。
AsyncLoader Thread 是一个异步加载线程 ,负责在游戏线程后台加载资产 ;
PoolThread 是一个池线程 ,执行 FileIO API 从 .pak 文件加载资产;
新的 AsyncLoader2 的加载流程:
Game Thread 在资产处理中会向新的 AsyncLoading Thread2 异步加载线程请求 Load 资产并等候,
新的 AsyncLoading Thread2 会控制后台负载,向 IO Service 发出请求文件读取控制,
Pool Thread 主要负责文件读取控制, 对 .ucas 进行查找 & 读取,当 PoolThread 查找并读取到 .ucas 文件资产容器数据时,会向 AsyncLoading Thread2 发出通知并返回文件资产容器数据,
AsyncLoading Thread2 拿到加载的文件资产容器数据通过 Complete 回调给 Game Thread ,Game Thread 异步加载线程接收到返回的数据并通过 Complete 回调通知游戏 .ucas 数据读取完成。
使用 AsyncLoader2 加载和文件访问:
GameThread 向 AsyncLoadingThread2 发出 LoadPackage 加载和文件访问请求,LoadPackage 为一个加载包线程。
AsyncLoadingThread2 会创建 QueuePackage 加载包队列(包含多个 AsyncPackage2 异步加载包线程),然后加入到 EventQueue 线程事件队列(包含多个 AsyncPackage2)中进行等候,同时创建一个 ReadWithCallback 回调到 IO Dispatcher 调度器, IO Dispatcher 调度器会通过 ReadFromBlockFile 向 IO Service 发出请求文件读取控制, IO Service 查找和读取数据。
AsyncLoader2:
- 包含 AsyncLoadingThread2 新的异步加载线程;它利用 IO Store 进行更改;以有效的方式请求 IO 以减少 CPU 开销;
- IoDispatcher IO调度器:中继异步加载线程和 Io Service 服务;
- Io Service 服务运行 FileIO API 以从 .ucas 文件加载资产;
相关推荐
1.项目中数据请求用到了fly.io,封装成request.js如下: import wx from 'wx' import Fly from 'flyio' import store from '../store/index' const fly = new Fly() fly.config.baseURL = process.env.BASE_URL fly...
在Vue.js应用中,随着项目的复杂度增加,单个Vuex Store可能会变得庞大且难以管理。为了解决这个问题,Vuex 提供了多模块(multi-module)的功能,允许我们将Store拆分为多个独立的模块,每个模块都有自己的状态、...
const store = new Vuex.Store({ state: { students: [...] }, mutations: { addStudent(state, stu) { state.students.push(stu); } } }); ``` 通过这种方式,我们可以向`mutations`传递任意复杂的数据...
在vue项目中组件间相互传值或者后台获取的数据需要供多个组件使用的情况很多的话,有必要考虑引入vuex来管理这些凌乱的状态,今天这边博文用来记录这一整个的过程,后台api接口是使用webpack-server模拟的接口,这...
在 Vuex 中 store 数据改变的唯一方法就是提交 mutations。mutations里面装着一些改变数据方法的集合,这是Vuex 设计很重要的一点,就是把处理数据逻辑方法全部放在 mutations 里面,使得数据和视图分离。 Mutations...
export const store = new Vuex.Store({ state: { isShow: false } }); ``` 在需要显示loading的组件中,可以使用`v-if="this.$store.state.isShow"`绑定到`isShow`状态。 接下来,我们要利用axios的拦截器功能...
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。 Vuex 中的 mutation 非常类似于事件: 每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态...
storage.setItem('shiguoqing0', [1, 2, 3, 4, 5, 6], { type: 'session' }); ``` 此外,还可以扩展这个工具类,添加超时时间设置、数据加密解密等功能,以满足更复杂的应用场景。 通过封装和规范`localStorage`和...
将以上内容整合到 Vuex 中,通常会在 `src` 目录下创建一个 `store` 文件夹,并在其中的 `index.js` 文件中定义 Store。例如: ```javascript import Vue from 'vue' import Vuex from 'vue' Vue.use(Vuex) ...
1. **初始化Vuex Store**:在`store.js`文件中,定义一个状态`apiCount`来跟踪当前的请求数量,以及一个`loadingInstance`来存储loading组件的实例。然后定义对应的mutation,如`startLoading`(打开loading)、`...
`mutations`部分是Vue的Vuex状态管理库中的一个关键概念,它允许我们改变 Vuex store 的状态。在这个例子中,`get_product`方法用于更新商品列表。当商品的`image`属性不为空时,代码会尝试处理图片的本地缓存。注意...
make 出现的/common.c:1088:42: error: request for member ‘path’ in something not a structure or union pStorePaths->paths[store_path_index].path, 解决办法 需要将FastDFS升级到v6.03及以上版本,建议使用...