651 lines
21 KiB
Markdown
651 lines
21 KiB
Markdown
# NanoVDB 体积云场景首帧阻塞根因修复计划
|
||
|
||
日期:2026-04-10
|
||
|
||
## 1. 文档定位
|
||
|
||
这份计划只解决一个具体问题:
|
||
|
||
- 在 `project` 项目中打开带有 `cloud.nvdb` 体积云对象的 `Main.xc` 场景时,Editor 在前期可交互,但在约 8 秒后出现一次明显的整窗卡死,随后再经过十多秒体积云才真正显示出来。
|
||
|
||
这份计划不讨论以下内容:
|
||
|
||
- 不讨论 build/package/runtime 发布格式。
|
||
- 不讨论 NanoVDB 渲染算法本身是否继续升级。
|
||
- 不讨论多后端 rollout。
|
||
- 不讨论重新设计整个 `Library` 体系。
|
||
|
||
这份计划关注的是:
|
||
|
||
- 为什么当前 `Library` 已经存在,但含 `.nvdb` 的场景打开仍然会在首帧附近严重阻塞。
|
||
- 为什么 `mvs/VolumeRenderer` 用同一份 `cloud.nvdb` 可以约 3 秒完成显示,而 Editor 要慢很多。
|
||
- 应该如何把当前这条链路收口成一套真正可用的正式方案。
|
||
|
||
---
|
||
|
||
## 2. 问题现象
|
||
|
||
当前复现现象已经很明确:
|
||
|
||
1. 直接打开 `project` 项目。
|
||
2. 打开 `project/Assets/Scenes/Main.xc`。
|
||
3. 界面会显示 `Runtime streaming scene assets...`,这段时间窗口还能继续操作。
|
||
4. 在大约 8000 ms 左右,Editor 突然开始明显卡死。
|
||
5. 卡死十多秒后,体积云对象才真正显示出来,随后窗口恢复。
|
||
|
||
对比样本:
|
||
|
||
- `mvs/VolumeRenderer` 使用的是同一份 `cloud.nvdb`。
|
||
- 运行 `mvs/VolumeRenderer/run.bat` 时,大约 3 秒左右即可加载并显示。
|
||
|
||
这说明:
|
||
|
||
- 慢点不在“这份 `.nvdb` 根本无法解析”。
|
||
- 慢点也不在“当前机器根本无法承载这份体积数据”。
|
||
- 真正的问题出在 Editor 主线里的额外同步收口路径。
|
||
|
||
---
|
||
|
||
## 3. 当前已经确认的事实
|
||
|
||
### 3.1 当前 `Library` 对体积资源做的不是“运行时加速缓存”
|
||
|
||
当前源文件:
|
||
|
||
- `project/Assets/cloud.nvdb`
|
||
- 文件大小:`590,241,000` bytes
|
||
|
||
当前 artifact:
|
||
|
||
- `project/Library/Artifacts/.../main.xcvol`
|
||
- 文件大小:`590,240,896` bytes
|
||
|
||
这说明当前 `.xcvol` 基本就是:
|
||
|
||
- 一个较小的 header
|
||
- 加上一份几乎原样的 NanoVDB payload
|
||
|
||
也就是说,当前 `Library` 在体积资源这条链路上做的是:
|
||
|
||
- 导入身份缓存
|
||
- metadata 缓存
|
||
- source -> artifact 统一入口
|
||
|
||
但它还没有做到:
|
||
|
||
- 为运行时准备更轻的 cooked payload
|
||
- 为 GPU 上传准备更直接的 runtime-ready 数据
|
||
- 为首帧显示准备真正低成本的预热结果
|
||
|
||
结论:
|
||
|
||
- 当前 `Library` 对 `.nvdb` 是“导入缓存”,不是“运行时性能缓存”。
|
||
|
||
### 3.2 当前 VolumeField CPU 侧存在多次大拷贝
|
||
|
||
当前体积资源进入运行时时,大致会经历:
|
||
|
||
1. 从 `.xcvol` 读取整个 payload 到临时缓冲。
|
||
2. `VolumeField::Create(...)` 再把 payload 拷贝进 `VolumeField::m_payload`。
|
||
3. `RenderResourceCache::UploadVolumeField(...)` 再构造一个新的 `uploadData`。
|
||
4. 再把这份数据写入 RHI buffer。
|
||
|
||
对 590MB 级别的体积数据来说,这不是“小开销”,而是主路径上的重负担。
|
||
|
||
### 3.3 当前 GPU 上传不是在后台完成,而是在第一次真正绘制时同步触发
|
||
|
||
当前体积云真正进入渲染时,会在 `BuiltinVolumetricPass::DrawVisibleVolume(...)` 中走:
|
||
|
||
- `RenderResourceCache::GetOrCreateVolumeField(...)`
|
||
- `UploadVolumeField(...)`
|
||
|
||
也就是说:
|
||
|
||
- 体积资源即使 CPU 侧已经异步读好了,
|
||
- GPU 侧 residency 的真正建立仍然是在第一次真正绘制该体积对象时才触发,
|
||
- 而且当前实现是同步发生在渲染路径里。
|
||
|
||
这正好解释了现在的现象:
|
||
|
||
- 前期 `Runtime streaming scene assets...` 时还能操作。
|
||
- 到第一次真正要画体积云时,主线程/渲染线程突然进入大开销同步路径。
|
||
|
||
### 3.4 当前体积 shader / PSO 首次创建也会叠加到这次阻塞里
|
||
|
||
当前体积材质使用的是:
|
||
|
||
- `builtin://shaders/volumetric`
|
||
|
||
该 builtin shader 最终会加载:
|
||
|
||
- `engine/assets/builtin/shaders/volumetric.shader`
|
||
|
||
这个 shader 直接包含:
|
||
|
||
- `PNanoVDB.hlsl`
|
||
|
||
而当前 D3D12 shader 编译路径明确使用:
|
||
|
||
- `D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION`
|
||
|
||
这意味着:
|
||
|
||
- 体积 pass 第一次真正创建 pipeline state 时,
|
||
- 很可能还会在主线程/渲染线程上同步现编一个包含 `PNanoVDB` 的大 shader 变体。
|
||
|
||
因此这次卡死不是单一开销,而是两类重活叠加:
|
||
|
||
1. 590MB 体积 payload 的运行时实现化
|
||
2. 体积 shader / PSO 的首次真实可绘制化
|
||
|
||
### 3.5 当前场景加载状态文本会误导真实阶段
|
||
|
||
当前 `Runtime streaming scene assets...` 的状态主要依赖:
|
||
|
||
- `ResourceManager::GetAsyncPendingCount()`
|
||
|
||
它只能说明:
|
||
|
||
- 异步 loader 队列还有没有待完成任务
|
||
|
||
它不能说明:
|
||
|
||
- GPU 上传是否已经完成
|
||
- 体积 shader 是否已经编好
|
||
- PSO 是否已经 ready
|
||
- 体积对象是否真的达到 render-ready
|
||
|
||
所以现在 UI 上看到的“streaming 快结束了”并不等于体积云真的快 ready 了。
|
||
|
||
---
|
||
|
||
## 4. 最根本的原因
|
||
|
||
把这次问题压缩成一句话:
|
||
|
||
**当前 `Library` 缓存住的是体积资源的“导入结果”,但没有缓存住体积云真正昂贵的“运行时实现化结果”;而这部分实现化又被推迟到了第一次真正绘制时同步完成。**
|
||
|
||
更展开一点,根因可以拆成四层:
|
||
|
||
### 4.1 第一层根因:缓存层次不对
|
||
|
||
现在缓存住的是:
|
||
|
||
- source asset 身份
|
||
- artifact 文件
|
||
- metadata
|
||
|
||
没有缓存住的是:
|
||
|
||
- 低拷贝可消费的 volume payload 视图
|
||
- GPU-ready residency
|
||
- shader / PSO 可直接绘制状态
|
||
|
||
### 4.2 第二层根因:时机不对
|
||
|
||
当前重活发生在:
|
||
|
||
- 不是项目打开时
|
||
- 不是场景结构恢复后后台预热时
|
||
- 而是在第一次真正绘制体积云时
|
||
|
||
这导致体感非常差,因为卡顿被集中释放在用户已经开始操作视口之后。
|
||
|
||
### 4.3 第三层根因:实现路径太重
|
||
|
||
当前 590MB 体积数据在 Editor 里会经历多次 CPU 侧复制和一次同步 GPU 上传。
|
||
对这种量级的数据,这本身就足以形成长时间阻塞。
|
||
|
||
### 4.4 第四层根因:渲染首帧还叠加了 shader / PSO 首编
|
||
|
||
体积 pass 的首次真正绘制不是“只差最后一次 draw call”,而是还可能同时触发:
|
||
|
||
- shader variant 首编
|
||
- pipeline layout 创建
|
||
- PSO 创建
|
||
- descriptor set 初始化
|
||
|
||
所以这次卡死是“重资源 + 重 shader”叠加,不是单点故障。
|
||
|
||
---
|
||
|
||
## 5. 为什么 `mvs/VolumeRenderer` 更快
|
||
|
||
`mvs/VolumeRenderer` 当前更快,不是因为它“缓存更高级”,而是因为它路径短得多。
|
||
|
||
它做的是:
|
||
|
||
- 直接读源 `.nvdb`
|
||
- 直接生成 GPU buffer
|
||
- 直接编少量 shader
|
||
- 直接渲染
|
||
|
||
它没有承担下面这些 Editor 主线额外职责:
|
||
|
||
- `AssetDatabase`
|
||
- `AssetRef -> path` 解析
|
||
- scene/component 反序列化恢复
|
||
- `VolumeField` 运行时抽象包装
|
||
- `RenderResourceCache` 的通用缓存层
|
||
- `BuiltinVolumetricPass` 的通用 descriptor / pass / pipeline 约束
|
||
- 视口首帧时的 editor 额外渲染流
|
||
|
||
所以 MVS 快,说明的是:
|
||
|
||
- 这份 `.nvdb` 并不是天然慢到 15 秒
|
||
|
||
而不是说明:
|
||
|
||
- 现在的 Editor 路线只需要继续堆更多导入缓存就能变快
|
||
|
||
---
|
||
|
||
## 6. 修复目标
|
||
|
||
本轮修复目标不是“让 `.xcvol` 体积更小”,也不是“强行把所有工作前置到项目启动”。
|
||
本轮的正式目标是:
|
||
|
||
### 6.1 交互目标
|
||
|
||
- 打开 `Main.xc` 后,不允许再出现“先可交互,再突然长时间整窗卡死”的体验。
|
||
- 一旦场景已经进入可交互状态,后续体积云就只能继续后台预热,不能把窗口重新拖回不可响应。
|
||
|
||
### 6.2 架构目标
|
||
|
||
- 把体积资源的“导入缓存”和“运行时实现化”明确分成两个阶段。
|
||
- 首次绘制路径不能再承载 590MB 级别的同步重活。
|
||
- 首次绘制路径只能消费已经 ready 的资源,或优雅跳过未 ready 的体积对象。
|
||
|
||
### 6.3 性能目标
|
||
|
||
在当前开发机和当前 `cloud.nvdb` 样本下:
|
||
|
||
- warm cache 场景再次打开时:
|
||
- 不允许出现超过 `200 ms` 的二次窗口无响应段。
|
||
- 体积云从场景打开到可见的时间目标收敛到 `3 s` 以内。
|
||
- 该目标以当前 `mvs/VolumeRenderer` 的约 `3 s` 作为对齐基线,正式目标是不慢于 MVS。
|
||
- cold import 首次打开时:
|
||
- 允许总体耗时更长,
|
||
- 但不允许在体积资源真正显示前出现长时间窗口卡死。
|
||
|
||
### 6.4 诊断目标
|
||
|
||
- 必须能明确区分:
|
||
- scene structure ready
|
||
- CPU payload ready
|
||
- GPU upload in progress
|
||
- shader / PSO prewarm in progress
|
||
- render-ready
|
||
|
||
---
|
||
|
||
## 7. 本轮明确不做的错误修法
|
||
|
||
以下方案不能作为本轮主方案:
|
||
|
||
### 7.1 不能只继续优化 `AssetDatabase::EnsureArtifact()`
|
||
|
||
原因:
|
||
|
||
- 这次 warm cache 场景下的主问题已经不是 source import 了。
|
||
- 再继续只抠导入判定和 reimport,不会解决“第一次真正绘制才卡死”。
|
||
|
||
### 7.2 不能只加更多“异步读文件”
|
||
|
||
原因:
|
||
|
||
- 当前前半段已经异步了。
|
||
- 真正卡死点在后半段 render-time realization。
|
||
|
||
### 7.3 不能一上来就先加第二套磁盘缓存目录
|
||
|
||
原因:
|
||
|
||
- 当前最大问题首先是同步时机和多次拷贝。
|
||
- 如果不先把“谁在什么时候做重活”改对,再加新 cache 文件夹只是继续堆复杂度。
|
||
|
||
### 7.4 不能只通过隐藏 UI 文本来掩盖问题
|
||
|
||
原因:
|
||
|
||
- 现在不是提示文案不对,而是真有一段重度同步阻塞。
|
||
|
||
---
|
||
|
||
## 8. 正式修复方向
|
||
|
||
本轮采用四条主线并行收口,但执行顺序必须严格分阶段。
|
||
|
||
### 8.1 主线 A:先把真实耗时切开,看清楚谁最重
|
||
|
||
虽然根因已经明确,但时间占比仍需正式打点。
|
||
本阶段必须先拿到真实分段耗时,不允许后续继续靠体感猜。
|
||
|
||
需要新增的时间切片:
|
||
|
||
1. `Scene Deserialize`
|
||
2. `AssetRef Resolve`
|
||
3. `VolumeFieldLoader.ReadArtifact`
|
||
4. `VolumeField.Create`
|
||
5. `Volume GPU Upload`
|
||
6. `Volumetric Shader Variant Compile`
|
||
7. `Volumetric PSO Create`
|
||
8. `First Volume Visible`
|
||
|
||
需要新增的日志与状态:
|
||
|
||
- `Main.xc` 打开时,针对 `cloud.nvdb` 输出完整链路耗时。
|
||
- 把“异步流式加载完成”和“体积 render-ready”拆开显示。
|
||
|
||
这一阶段的目的不是修性能,而是:
|
||
|
||
- 锁死真正的大头时间占比
|
||
- 避免后续错误优化无关路径
|
||
|
||
### 8.2 主线 B:去掉体积 payload 的多次大拷贝
|
||
|
||
这是本轮最核心的工程改动之一。
|
||
|
||
正式方向:
|
||
|
||
1. `VolumeField` 不能继续默认把 590MB payload 再拷进一份新的 `m_payload`。
|
||
2. `.xcvol` 读取后,应该改成:
|
||
- 文件映射
|
||
- 或共享只读 blob
|
||
- 或单所有权 payload 容器
|
||
3. `RenderResourceCache::UploadVolumeField(...)` 不能再额外构造一份等体积的 `uploadData` 再拷一次。
|
||
4. 上传路径必须直接消费 loader 产出的只读 payload 视图。
|
||
|
||
本阶段完成后,应达到:
|
||
|
||
- `.xcvol -> VolumeField` 不再发生无意义的大内存复制。
|
||
- `VolumeField -> RenderResourceCache` 也不再产生第二份 590MB 临时副本。
|
||
|
||
这一步做完,即使还没异步 GPU 上传,卡顿也会先明显下降。
|
||
|
||
### 8.3 主线 C:把 GPU 上传从首次绘制路径里拿出去
|
||
|
||
当前真正错误的不是“上传很重”,而是“上传发生在第一次真正 draw 的时候”。
|
||
|
||
正式方向:
|
||
|
||
1. 为 `VolumeField` 建立明确的 runtime residency 状态机:
|
||
- `Unloaded`
|
||
- `CpuReady`
|
||
- `GpuUploading`
|
||
- `GpuReady`
|
||
- `Failed`
|
||
2. CPU 侧 payload 一旦 ready,就立即进入独立的 GPU 预热队列。
|
||
3. `BuiltinVolumetricPass::DrawVisibleVolume(...)` 不允许再承担首次重量级上传。
|
||
4. draw path 的职责改为:
|
||
- 如果 `GpuReady`,正常绘制
|
||
- 如果未 ready,跳过或显示占位,不得同步收口
|
||
|
||
对 D3D12 的具体要求:
|
||
|
||
1. `BufferType::Storage` 不能继续把大体积 volume payload 当普通 upload-heap 常驻缓冲来处理。
|
||
2. 需要引入正式的:
|
||
- staging/upload buffer
|
||
- default heap storage buffer
|
||
- copy queue 或专用 upload path
|
||
3. volume buffer 上传完成后再切换到可读状态,而不是在 draw path 上临时补。
|
||
|
||
这一步是解决“8000 ms 后突然卡死”的主修复点。
|
||
|
||
### 8.4 主线 D:把体积 shader / PSO 首编从体积首帧里拿出去
|
||
|
||
当前体积云第一次真正绘制时,渲染链路里还会叠加:
|
||
|
||
- builtin volumetric shader variant 首次真正编译
|
||
- pipeline layout / descriptor layout 构建
|
||
- PSO 创建
|
||
|
||
正式方向:
|
||
|
||
1. `builtin://shaders/volumetric` 的实际运行时使用变体需要正式预热。
|
||
2. 预热时机不放在“第一次真正 draw”。
|
||
3. 预热应在以下时机之一完成:
|
||
- scene structure ready 之后的后台预热阶段
|
||
- volume material 绑定后立刻排队预热
|
||
4. `BuiltinVolumetricPass` 首次执行时只能命中已存在的 shader variant / PSO cache。
|
||
|
||
当前 D3D12 路线还要额外处理一个问题:
|
||
|
||
- 现在编译 flags 是 `D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION`
|
||
|
||
这本身会显著拉长编译时间。
|
||
本轮需要明确一条正式策略:
|
||
|
||
1. Debug 能力是否仍要保留。
|
||
2. 如果保留,默认 editor 交互路径不能在首帧同步承担这份成本。
|
||
3. 可以接受“后台慢编”,不能接受“首帧卡死慢编”。
|
||
|
||
### 8.5 主线 E:修正场景加载进度模型
|
||
|
||
当前 `Runtime streaming scene assets...` 的语义不完整。
|
||
这会直接误导调试,也会误导后续调度逻辑。
|
||
|
||
正式方向:
|
||
|
||
1. 把 scene load progress 拆成四段:
|
||
- `Structure Ready`
|
||
- `CPU Asset Streaming`
|
||
- `GPU Residency Prewarm`
|
||
- `Render Warmup`
|
||
2. `pendingAsyncLoads == 0` 时,只能说明 CPU loader 阶段接近结束。
|
||
3. 只有 volume GPU ready 且体积 pass 关键 shader/PSO ready 时,才能算真正 ready。
|
||
4. UI 需要把这几个阶段明确显示出来,避免再把后半段卡顿误判成“明明 streaming 都结束了却又卡”。
|
||
|
||
---
|
||
|
||
## 9. 执行阶段
|
||
|
||
### Phase 0:诊断与基线固化
|
||
|
||
目标:
|
||
|
||
- 用日志和时间切片把当前问题彻底量化。
|
||
|
||
任务:
|
||
|
||
1. 为 `Main.xc` 的 `cloud.nvdb` 打通完整性能时间线。
|
||
2. 输出 warm cache / cold cache 两组基线。
|
||
3. 输出 MVS 对比基线。
|
||
|
||
验收标准:
|
||
|
||
1. 能明确给出 CPU 读取、CPU 拷贝、GPU 上传、shader 编译、PSO 创建各自耗时。
|
||
2. 不再用“感觉像是这里慢”做判断。
|
||
|
||
### Phase 1:体积 payload 低拷贝重构
|
||
|
||
目标:
|
||
|
||
- 消除 `.xcvol -> VolumeField -> UploadVolumeField` 链路中的重复大拷贝。
|
||
|
||
任务:
|
||
|
||
1. 重构 `VolumeField` 的 payload 持有方式。
|
||
2. 重构 `.xcvol` loader 的结果表示。
|
||
3. 重构 `RenderResourceCache::UploadVolumeField(...)` 的输入形式。
|
||
|
||
验收标准:
|
||
|
||
1. warm cache 情况下,CPU 内存峰值明显下降。
|
||
2. `VolumeField` 载入完成时不再出现等量级的重复 payload 副本。
|
||
|
||
### Phase 2:异步 GPU 上传与渲染脱钩
|
||
|
||
目标:
|
||
|
||
- 第一次绘制体积对象时,不再承担首次 GPU upload。
|
||
|
||
任务:
|
||
|
||
1. 增加 volume GPU prewarm 队列。
|
||
2. 建立 GPU residency 状态机。
|
||
3. 改掉 draw path 的同步上传兜底。
|
||
|
||
验收标准:
|
||
|
||
1. 首次打开 `Main.xc` 后,不再在体积第一次出现在视野中时发生长时间整窗阻塞。
|
||
2. 体积对象未 ready 时允许暂时不显示,但不允许卡死窗口。
|
||
|
||
### Phase 3:体积 shader / PSO 预热
|
||
|
||
目标:
|
||
|
||
- 不再把 `volumetric.shader` 的首次真实可绘制准备放在体积首帧。
|
||
|
||
任务:
|
||
|
||
1. 为 active backend 预热体积 shader variant。
|
||
2. 为 volume material / pass 预热关键 PSO。
|
||
3. 把 shader/PSO 首编首建从 draw path 移走。
|
||
|
||
验收标准:
|
||
|
||
1. 首次显示体积云时,不再同时伴随大段 shader/PSO 同步创建时间。
|
||
2. 日志能证明首帧命中的是已准备好的可绘制状态。
|
||
|
||
### Phase 4:场景状态机与 UI 收口
|
||
|
||
目标:
|
||
|
||
- 让场景打开状态和真实资源准备阶段一致。
|
||
|
||
任务:
|
||
|
||
1. 拆分 load status 阶段。
|
||
2. 修正 `Runtime streaming scene assets...` 的语义。
|
||
3. 在 Project/Viewport/Console 中统一反映同一份阶段状态。
|
||
|
||
验收标准:
|
||
|
||
1. UI 文案与真实阶段一致。
|
||
2. 不再出现“streaming 结束了但实际上后面还有一次大卡死”的错误认知。
|
||
|
||
### Phase 5:回归、压力样本与收口
|
||
|
||
目标:
|
||
|
||
- 用真实样本和自动化验证确认这条路线已经稳定。
|
||
|
||
任务:
|
||
|
||
1. 用当前 `cloud.nvdb` 做 warm/cold 双场景回归。
|
||
2. 回归 `Main.xc` 打开、关闭、再次打开。
|
||
3. 检查体积云显示、Editor 响应性、日志阶段切片。
|
||
|
||
验收标准:
|
||
|
||
1. warm cache 打开 `Main.xc` 时无二次长阻塞。
|
||
2. cold cache 首次导入时也保持可交互。
|
||
3. 体积云显示时间显著收敛,接近 MVS 量级。
|
||
|
||
---
|
||
|
||
## 10.1 当前执行进展(2026-04-10)
|
||
|
||
当前代码实现已经进入正式重构阶段,已落地的第一批改动如下:
|
||
|
||
1. `VolumeField` 新增 owned payload 创建路径,`.xcvol` 读取结果不再在 `VolumeField::Create(...)` 内发生第二次整块复制。
|
||
2. `VolumeFieldLoader` 的 artifact 载入路径已改为“读入 payload -> 直接 move 进 `VolumeField`”,去掉 artifact load 阶段的重复大拷贝。
|
||
3. `RenderResourceCache::UploadVolumeField(...)` 不再构造整块 `uploadData` 临时副本,改为直接消费 `VolumeField` payload。
|
||
4. `RenderResourceCache` 已为 volume 引入最小可用的 residency 状态:
|
||
- `Uninitialized`
|
||
- `Uploading`
|
||
- `Ready`
|
||
- `Failed`
|
||
5. volume GPU 上传已改为分帧推进,当前实现按固定 chunk 预算逐帧写入,避免第一次真正绘制时一次性同步吞下整块 payload。
|
||
6. `BuiltinVolumetricPass` 已拆出资源预热步骤:
|
||
- 先推进 volume upload
|
||
- 再预建 volume pass layout / pipeline
|
||
- draw path 只消费 `Ready` 的 volume,未 ready 时直接跳过,不再兜底触发重量级上传
|
||
7. 当前已补最小日志:
|
||
- `Volume GPU upload started`
|
||
- `Volume GPU upload ready`
|
||
|
||
这批改动的意义是:
|
||
|
||
- 先把 warm cache 路径里最重的两类同步收口拆开:
|
||
- `.xcvol -> VolumeField` 的重复 CPU 大拷贝
|
||
- 首次 draw path 内的一次性整块 GPU 上传
|
||
- 先把“卡死 Editor”问题从根上打散成可推进、可观察、可继续优化的状态。
|
||
|
||
当前这批改动还没有完成的部分:
|
||
|
||
1. 还没有把 `.xcvol` 升级成真正 runtime-ready 的 cooked artifact,当前只是先把现有 artifact 的运行时消费链路做轻。
|
||
2. 还没有补齐完整的分段耗时打点。
|
||
3. 还没有把 volumetric shader / PSO 的首编完全前移到更早阶段。
|
||
|
||
---
|
||
|
||
## 11. 涉及模块范围
|
||
|
||
预计会涉及但不限于以下模块:
|
||
|
||
- `engine/include/XCEngine/Resources/Volume/VolumeField.h`
|
||
- `engine/src/Resources/Volume/VolumeField.cpp`
|
||
- `engine/src/Resources/Volume/VolumeFieldLoader.cpp`
|
||
- `engine/include/XCEngine/Rendering/Caches/RenderResourceCache.h`
|
||
- `engine/src/Rendering/Caches/RenderResourceCache.cpp`
|
||
- `engine/include/XCEngine/Rendering/Passes/BuiltinVolumetricPass.h`
|
||
- `engine/src/Rendering/Passes/BuiltinVolumetricPass.cpp`
|
||
- `engine/src/RHI/D3D12/D3D12Device.cpp`
|
||
- `engine/src/RHI/D3D12/D3D12Buffer.cpp`
|
||
- `engine/src/Resources/BuiltinResources.cpp`
|
||
- `editor/src/Managers/SceneManager.cpp`
|
||
- `editor/src/Viewport/ViewportHostService.h`
|
||
|
||
如果 Phase 2 仍然不足,再考虑是否需要新增专门的 runtime-prewarm 辅助模块。
|
||
但在本轮中,不应先为了结构好看而过早引入新的大模块。
|
||
|
||
---
|
||
|
||
## 12. 风险与边界
|
||
|
||
### 11.1 风险一:只优化 CPU 拷贝,但仍保留首帧同步 GPU 上传
|
||
|
||
结果:
|
||
|
||
- 会变快,但不会从根上解决“突然卡死”。
|
||
|
||
### 11.2 风险二:只做 GPU 上传异步,但 shader / PSO 首编仍在首帧
|
||
|
||
结果:
|
||
|
||
- 体积数据路径变快,但体积首帧依然可能因为 shader 现编而卡死。
|
||
|
||
### 11.3 风险三:一上来先设计新的磁盘 runtime cache
|
||
|
||
结果:
|
||
|
||
- 复杂度先上去了,
|
||
- 但如果真正的大头是同步上传和首编,收益会被高估。
|
||
|
||
因此本轮策略必须是:
|
||
|
||
1. 先打点
|
||
2. 先移走同步重活
|
||
3. 再决定是否需要更重的磁盘级 runtime cache
|
||
|
||
---
|
||
|
||
## 13. 完成标志
|
||
|
||
当以下条件同时成立时,这份计划才算完成:
|
||
|
||
1. 打开 `project/Assets/Scenes/Main.xc` 时,Editor 不再出现二次长时间整窗卡死。
|
||
2. `cloud.nvdb` 的 warm cache 路径已经不再依赖首次绘制时的同步重资源收口。
|
||
3. 体积 shader / PSO 的首次准备不再挤在体积首帧。
|
||
4. 场景进度状态能正确区分 CPU streaming、GPU prewarm 和 render-ready。
|
||
5. 当前这条路径的耗时分布可以通过日志直接解释,不再需要靠猜。
|
||
|
||
---
|
||
|
||
## 14. 一句话结论
|
||
|
||
这次问题的根不在“`Library` 有没有命中”,而在“当前 `Library` 只缓存了导入结果,没有缓存运行时真正昂贵的实现化结果,而且这部分实现化被错误地放到了体积第一次真正绘制时同步完成”。
|
||
本轮修复必须围绕这个根因展开,而不是继续把注意力放回导入判定本身。
|