486 lines
16 KiB
Markdown
486 lines
16 KiB
Markdown
|
|
# NanoVDB 体积云加载阻塞与 Runtime 上传修复计划
|
|||
|
|
|
|||
|
|
文档日期:2026-04-10
|
|||
|
|
|
|||
|
|
适用范围:当前 `XCEngine` 的 `Resources / Asset / Rendering / RHI / Editor` 主线,目标问题为 editor 打开主场景后 `cloud.nvdb` 体积云加载时间长、首帧解锁后再次长时间卡死,而 `mvs/VolumeRenderer` 使用同一份 `cloud.nvdb` 仅约 3 秒即可完成加载与显示。
|
|||
|
|
|
|||
|
|
文档目标:把当前 editor 中 `NanoVDB` 体积云的加载链路,从“CPU 异步读完后在首个可见渲染帧同步创建并写入大体积 GPU 资源,导致主线程长时间阻塞”的错误运行模式,重构为接近 `mvs/VolumeRenderer` 的正确模式,即“CPU 异步读取 + GPU 本地 buffer 上传 + 上传完成前不阻塞编辑器交互 + 运行时不再把大体积 payload 留在 draw path 上处理”。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 问题结论
|
|||
|
|
|
|||
|
|
当前问题不是单点 bug,而是三段成本叠加:
|
|||
|
|
|
|||
|
|
1. `AssetDatabase` 对 `.nvdb` 的 `main.xcvol` artifact 只是“metadata + 原始 NanoVDB payload”包装,几乎没有降低运行时 payload 成本。
|
|||
|
|
2. `VolumeRendererComponent` 的 deferred scene load 只把 CPU 资源异步读到 `VolumeField`,没有提前完成 GPU 上传。
|
|||
|
|
3. `BuiltinVolumetricPass` 首次真正消费 `VolumeField` 时,`RenderResourceCache::UploadVolumeField()` 在渲染线程同步创建 `StorageBuffer` 并调用 `SetData()` 写入整个 payload,直接把首个可见帧变成一次大块同步上传。
|
|||
|
|
|
|||
|
|
最关键的问题在第 3 条。
|
|||
|
|
|
|||
|
|
当前 D3D12 后端里:
|
|||
|
|
|
|||
|
|
- `BufferType::Storage` 默认走 `UPLOAD heap`
|
|||
|
|
- `SetData()` 走 `Map + memcpy`
|
|||
|
|
- 于是体积云最终 shader 访问的不是 GPU 本地 `DEFAULT heap` buffer,而是 CPU 可写的 upload buffer
|
|||
|
|
|
|||
|
|
这与 `mvs/VolumeRenderer` 的链路根本不同。`mvs` 是:
|
|||
|
|
|
|||
|
|
1. 创建最终 `DEFAULT heap` buffer
|
|||
|
|
2. 创建临时 `UPLOAD heap` staging buffer
|
|||
|
|
3. CPU 只写 staging
|
|||
|
|
4. 通过 `CopyBufferRegion` 拷到默认堆
|
|||
|
|
5. 之后 shader 从 GPU 本地 buffer 读取
|
|||
|
|
|
|||
|
|
因此,第一阶段收益最大的修复,不是继续优化 artifact 文件,而是把 editor/runtime 的 volume buffer 上传路径改成和 `mvs` 同构。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 现状与根因拆解
|
|||
|
|
|
|||
|
|
## 2.1 当前 editor 链路
|
|||
|
|
|
|||
|
|
### 场景打开阶段
|
|||
|
|
|
|||
|
|
1. `SceneManager::LoadScene()` 在 deferred scene load 作用域中恢复场景结构。
|
|||
|
|
2. `VolumeRendererComponent` 仅恢复 `volumeRef -> path`,不立即同步 load。
|
|||
|
|
3. 直到渲染抽取阶段调用 `GetVolumeField()` 时,才触发 `LoadAsync()`。
|
|||
|
|
4. editor 状态栏中的 `Runtime streaming scene assets...` 只反映 CPU 侧异步资源请求计数。
|
|||
|
|
|
|||
|
|
### CPU 资源完成阶段
|
|||
|
|
|
|||
|
|
1. `LoadAsync()` 完成后,`VolumeField` 已在 CPU 内存中可用。
|
|||
|
|
2. 此时 editor 可以结束“streaming”状态,但 GPU 尚未完成 volume payload 驻留。
|
|||
|
|
3. 首个真正绘制体积云的 pass 进入 `BuiltinVolumetricPass::DrawVisibleVolume()`。
|
|||
|
|
4. `RenderResourceCache::GetOrCreateVolumeField()` 发现无缓存,触发 `UploadVolumeField()`。
|
|||
|
|
5. `UploadVolumeField()` 在渲染线程里同步分配 buffer / SRV,并写入整个 payload。
|
|||
|
|
|
|||
|
|
### 结果
|
|||
|
|
|
|||
|
|
1. 用户前面数秒可以交互,因为 CPU 异步读取没有阻塞主线程。
|
|||
|
|
2. 当 CPU 资源可用且首次 draw 发生时,主线程突然承担完整 GPU 上传。
|
|||
|
|
3. 由于 payload 约 `590 MB`,首个可见渲染帧被长时间卡死。
|
|||
|
|
|
|||
|
|
## 2.2 当前 artifact 链路的问题边界
|
|||
|
|
|
|||
|
|
`cloud.nvdb` 已经命中 `Library/Artifacts/.../main.xcvol`,因此问题不是“每次重导入”。
|
|||
|
|
|
|||
|
|
但当前 `xcvol` 也没有真正消除运行时成本:
|
|||
|
|
|
|||
|
|
1. 写 artifact 时直接写出 `VolumeField` payload。
|
|||
|
|
2. 读 artifact 时重新读入整个 payload 到 CPU 数组。
|
|||
|
|
3. 运行时依然需要再把整块 payload 上传到 GPU。
|
|||
|
|
|
|||
|
|
换言之,当前 artifact 的价值主要是:
|
|||
|
|
|
|||
|
|
- 导入结果稳定
|
|||
|
|
- metadata 结构化
|
|||
|
|
- 允许项目资产走统一 `AssetRef` / `Library` 流程
|
|||
|
|
|
|||
|
|
它还没有做到:
|
|||
|
|
|
|||
|
|
- 运行时零拷贝或近零拷贝装载
|
|||
|
|
- GPU 驻留态预烘焙
|
|||
|
|
- 直接针对 volume draw path 的运行时加速
|
|||
|
|
|
|||
|
|
## 2.3 与 `mvs/VolumeRenderer` 的本质差异
|
|||
|
|
|
|||
|
|
不是“都在传同一个 buffer,所以理论上应该一样快”,而是当前两者的最终资源模型不同:
|
|||
|
|
|
|||
|
|
### `mvs/VolumeRenderer`
|
|||
|
|
|
|||
|
|
- 最终资源:`DEFAULT heap` GPU 本地 buffer
|
|||
|
|
- 中转资源:临时 `UPLOAD heap`
|
|||
|
|
- 上传模式:copy queue / direct queue 提交拷贝后等待完成
|
|||
|
|
- draw path:只消费已上传完成的 GPU buffer
|
|||
|
|
|
|||
|
|
### 当前 editor
|
|||
|
|
|
|||
|
|
- 最终资源:`UPLOAD heap` buffer
|
|||
|
|
- 中转资源:无专门 staging
|
|||
|
|
- 上传模式:draw path 内同步 `Map + memcpy`
|
|||
|
|
- draw path:首次消费时同时承担资源上传职责
|
|||
|
|
|
|||
|
|
这意味着:
|
|||
|
|
|
|||
|
|
1. editor 首帧 draw path 的职责过重
|
|||
|
|
2. volume payload 的最终落点错误
|
|||
|
|
3. 即使 CPU 读取时间相近,GPU 上传和后续 shader 读取性能仍会明显落后于 `mvs`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 修复目标
|
|||
|
|
|
|||
|
|
本次修复分为三个层级目标。
|
|||
|
|
|
|||
|
|
## 3.1 一级目标:先把“8 秒后突然卡死十几秒”彻底打掉
|
|||
|
|
|
|||
|
|
要求:
|
|||
|
|
|
|||
|
|
1. 打开主场景后,editor 在 volume payload 首次可见前后都不出现长时间主线程冻结。
|
|||
|
|
2. `BuiltinVolumetricPass` 不再承担大体积同步上传职责。
|
|||
|
|
3. `StorageBuffer` 不再默认把 volume payload 留在 `UPLOAD heap` 作为最终运行时资源。
|
|||
|
|
|
|||
|
|
## 3.2 二级目标:让 editor 的 volume GPU 上传路径和 `mvs` 同构
|
|||
|
|
|
|||
|
|
要求:
|
|||
|
|
|
|||
|
|
1. D3D12 下 volume payload 最终驻留在 `DEFAULT heap`。
|
|||
|
|
2. CPU 只写 staging / upload 资源。
|
|||
|
|
3. GPU 通过 copy 提交完成真正拷贝。
|
|||
|
|
4. shader 后续只读取 GPU 本地资源。
|
|||
|
|
|
|||
|
|
## 3.3 三级目标:继续把总加载时间向 `mvs` 靠近
|
|||
|
|
|
|||
|
|
要求:
|
|||
|
|
|
|||
|
|
1. 逐步削减 `xcvol -> CPU payload` 的运行时装载成本。
|
|||
|
|
2. 未来允许 volume artifact 直接流向 GPU upload 路径,而不是“先完整常驻 CPU,再完整复制到 GPU”。
|
|||
|
|
3. 在保持引擎正式资源体系一致性的前提下,把总时间尽量压向 `mvs` 的约 3 秒基线。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 非目标
|
|||
|
|
|
|||
|
|
本轮不做以下内容,避免修复方向失焦:
|
|||
|
|
|
|||
|
|
1. 不重写 NanoVDB ray marching 算法本身。
|
|||
|
|
2. 不把正式主线路径退化回 `mvs/VolumeRenderer` 的孤立 sample 结构。
|
|||
|
|
3. 不先做 volume 压缩格式、体素裁剪重编码、分块稀疏 streaming。
|
|||
|
|
4. 不先重构完整 SRP / render graph。
|
|||
|
|
5. 不以“删除 Library 重建”作为修复方案。
|
|||
|
|
6. 不为体积云单独发明 editor 私有旁路渲染器。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 正式修复方向
|
|||
|
|
|
|||
|
|
## 5.1 方向一:补齐“不可变 GPU 本地 buffer + 初始数据上传”能力
|
|||
|
|
|
|||
|
|
这是本轮最高优先级。
|
|||
|
|
|
|||
|
|
### 当前缺口
|
|||
|
|
|
|||
|
|
当前 `RHIDevice::CreateBuffer(const BufferDesc&)` 只描述“创建一个 buffer”,但没有能力表达:
|
|||
|
|
|
|||
|
|
- 最终资源要落在 GPU 本地内存
|
|||
|
|
- 初始数据通过 staging copy 进入
|
|||
|
|
- 创建后立即进入某个最终状态
|
|||
|
|
|
|||
|
|
因此 `RenderResourceCache::UploadVolumeField()` 只能:
|
|||
|
|
|
|||
|
|
1. `CreateBuffer()`
|
|||
|
|
2. `CreateShaderResourceView()`
|
|||
|
|
3. `SetData()`
|
|||
|
|
|
|||
|
|
对 D3D12 volume 来说,这条路是错的。
|
|||
|
|
|
|||
|
|
### 目标能力
|
|||
|
|
|
|||
|
|
新增正式 buffer 创建能力,语义类似:
|
|||
|
|
|
|||
|
|
- `CreateBuffer(const BufferDesc& desc, const void* initialData, size_t initialDataSize, ResourceStates finalState)`
|
|||
|
|
|
|||
|
|
或
|
|||
|
|
|
|||
|
|
- `CreateInitializedBuffer(...)`
|
|||
|
|
|
|||
|
|
要求:
|
|||
|
|
|
|||
|
|
1. D3D12 volume storage buffer 走 `DEFAULT heap`
|
|||
|
|
2. 使用 upload staging 完成拷贝
|
|||
|
|
3. 拷贝后转为 `GenericRead` 或对应 shader 可读状态
|
|||
|
|
4. 返回对象仍然是统一 `RHIBuffer`
|
|||
|
|
|
|||
|
|
### 设计原则
|
|||
|
|
|
|||
|
|
1. 不要全局修改现有 `CreateBuffer(BufferType::Storage)` 的默认语义
|
|||
|
|
2. 仅给需要“设备本地 + 初始数据上传”的资源新增专用路径
|
|||
|
|
3. 保持旧代码依赖 `SetData()` 的场景继续可用
|
|||
|
|
|
|||
|
|
这一步是最大收益点,因为它同时解决:
|
|||
|
|
|
|||
|
|
1. 首帧主线程同步大 memcpy
|
|||
|
|
2. volume 最终资源落在 upload heap 的错误模型
|
|||
|
|
|
|||
|
|
## 5.2 方向二:把 volume GPU 上传从 draw path 前移
|
|||
|
|
|
|||
|
|
仅修正 D3D12 buffer 落点,还不够。
|
|||
|
|
|
|||
|
|
如果 volume 仍在 `BuiltinVolumetricPass::DrawVisibleVolume()` 首次执行时触发上传,那么:
|
|||
|
|
|
|||
|
|
1. 即使上传路径更正确
|
|||
|
|
2. 首个可见帧依旧要等待 GPU upload 完成
|
|||
|
|
3. editor 仍会出现明显顿挫
|
|||
|
|
|
|||
|
|
因此还需要把职责改成:
|
|||
|
|
|
|||
|
|
### 正确职责分层
|
|||
|
|
|
|||
|
|
#### CPU 异步资源层
|
|||
|
|
|
|||
|
|
- `VolumeRendererComponent` 异步拿到 `VolumeField`
|
|||
|
|
|
|||
|
|
#### GPU 上传调度层
|
|||
|
|
|
|||
|
|
- 检测到 `VolumeField` CPU 资源完成后,提交 GPU upload 请求
|
|||
|
|
- volume cache 进入 `Uploading` 状态
|
|||
|
|
|
|||
|
|
#### 渲染消费层
|
|||
|
|
|
|||
|
|
- `BuiltinVolumetricPass` 只消费 `GpuReady` 的 volume
|
|||
|
|
- 对尚未就绪的 volume 不绘制,不再临时上传
|
|||
|
|
|
|||
|
|
### 目标状态机
|
|||
|
|
|
|||
|
|
建议为 volume runtime cache 明确引入:
|
|||
|
|
|
|||
|
|
1. `Uninitialized`
|
|||
|
|
2. `CpuReady`
|
|||
|
|
3. `Uploading`
|
|||
|
|
4. `GpuReady`
|
|||
|
|
5. `Failed`
|
|||
|
|
|
|||
|
|
首帧 draw path 不再负责从 `Uninitialized/CpuReady` 直接推进到 `GpuReady`。
|
|||
|
|
|
|||
|
|
## 5.3 方向三:给 volume 增加正式上传队列或帧外预热入口
|
|||
|
|
|
|||
|
|
本轮至少需要一个最小正式机制,用于承接 GPU 上传工作。
|
|||
|
|
|
|||
|
|
### 最小可落地形式
|
|||
|
|
|
|||
|
|
1. 在渲染系统或 `RenderResourceCache` 外围增加 volume upload service
|
|||
|
|
2. 在主循环中轮询已完成的 CPU async load
|
|||
|
|
3. 将 volume GPU upload 提交给渲染设备层
|
|||
|
|
4. 上传完成后切换到 `GpuReady`
|
|||
|
|
|
|||
|
|
### 推荐正式方向
|
|||
|
|
|
|||
|
|
统一成“渲染资源上传服务”,后续 mesh / large texture 也可以逐步收口到这里。
|
|||
|
|
|
|||
|
|
本次 volume 修复可以先做 volume-only 版本,但接口命名不要把未来扩展堵死。
|
|||
|
|
|
|||
|
|
## 5.4 方向四:削减 `xcvol` 的运行时 CPU 装载成本
|
|||
|
|
|
|||
|
|
这是第二优先级的大项。
|
|||
|
|
|
|||
|
|
当前 artifact 仍然要求:
|
|||
|
|
|
|||
|
|
1. 打开文件
|
|||
|
|
2. 读 header
|
|||
|
|
3. 分配 payload 数组
|
|||
|
|
4. 把整个 payload 读入 CPU
|
|||
|
|
5. 再把整个 payload 上传到 GPU
|
|||
|
|
|
|||
|
|
要接近 `mvs` 的总时间,后面必须继续推进:
|
|||
|
|
|
|||
|
|
### 正式方向
|
|||
|
|
|
|||
|
|
1. volume artifact header 与 payload 更明确分段
|
|||
|
|
2. 支持 volume payload memory-mapping 或流式读入 upload buffer
|
|||
|
|
3. 非必要时不长期保留 `590 MB` CPU payload 常驻
|
|||
|
|
|
|||
|
|
### 本轮边界
|
|||
|
|
|
|||
|
|
本轮不要求一步做到 memory-mapped 零拷贝,但文档和接口设计必须为此留口。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 分阶段实施计划
|
|||
|
|
|
|||
|
|
## Phase 0:指标固化与基线采样
|
|||
|
|
|
|||
|
|
### 目标
|
|||
|
|
|
|||
|
|
在改代码前先量化基线,避免后续只凭体感判断。
|
|||
|
|
|
|||
|
|
### 任务
|
|||
|
|
|
|||
|
|
1. 记录当前 editor 打开 `Main.xc` 的三个时间点:
|
|||
|
|
- 场景结构恢复完成
|
|||
|
|
- `Runtime streaming scene assets...` 结束
|
|||
|
|
- volume 真正可见
|
|||
|
|
2. 记录当前 `mvs/VolumeRenderer` 从启动到 volume 可见耗时。
|
|||
|
|
3. 为 volume load / upload 补 focused trace,区分:
|
|||
|
|
- CPU artifact load
|
|||
|
|
- GPU upload begin
|
|||
|
|
- GPU upload complete
|
|||
|
|
4. 记录 D3D12 volume buffer 创建类型与 heap 类型。
|
|||
|
|
|
|||
|
|
### 交付
|
|||
|
|
|
|||
|
|
1. 一组可复现实测数字
|
|||
|
|
2. 一组可复现日志样本
|
|||
|
|
|
|||
|
|
## Phase 1:RHI 补齐 initialized GPU-local buffer 能力
|
|||
|
|
|
|||
|
|
### 目标
|
|||
|
|
|
|||
|
|
让 D3D12 volume storage buffer 走 `DEFAULT heap + staging copy`。
|
|||
|
|
|
|||
|
|
### 任务
|
|||
|
|
|
|||
|
|
1. 在 `RHIDevice` 层新增 initialized buffer 创建接口。
|
|||
|
|
2. D3D12 实现复用现有纹理上传思路,构建:
|
|||
|
|
- upload queue
|
|||
|
|
- upload allocator
|
|||
|
|
- upload command list
|
|||
|
|
- fence / idle wait
|
|||
|
|
3. volume 使用新接口,不再 `CreateBuffer + SetData()`。
|
|||
|
|
4. 保证 SRV 创建逻辑不变,shader 读取接口不变。
|
|||
|
|
|
|||
|
|
### 验收标准
|
|||
|
|
|
|||
|
|
1. D3D12 下 volume payload 最终 buffer 不再是 upload heap。
|
|||
|
|
2. `RenderResourceCache::UploadVolumeField()` 不再通过 `SetData()` 写入大 payload。
|
|||
|
|
3. 场景首次可见时的卡顿时长明显下降。
|
|||
|
|
|
|||
|
|
## Phase 2:把 GPU 上传从 draw path 中移除
|
|||
|
|
|
|||
|
|
### 目标
|
|||
|
|
|
|||
|
|
首个可见帧只消费 ready 资源,不执行大资源上传。
|
|||
|
|
|
|||
|
|
### 任务
|
|||
|
|
|
|||
|
|
1. 在 volume runtime cache 层引入上传状态。
|
|||
|
|
2. 在 CPU async load 完成后提交 GPU upload 请求。
|
|||
|
|
3. `BuiltinVolumetricPass` 遇到 `Uploading` 状态时跳过该 volume。
|
|||
|
|
4. editor 状态条可选择扩展为同时显示:
|
|||
|
|
- CPU streaming count
|
|||
|
|
- GPU upload pending count
|
|||
|
|
|
|||
|
|
### 验收标准
|
|||
|
|
|
|||
|
|
1. 打开场景后不再出现“streaming 结束后突然长时间卡死”。
|
|||
|
|
2. editor 在 volume GPU 上传期间依旧保持交互。
|
|||
|
|
3. volume 在 upload 完成后自然出现。
|
|||
|
|
|
|||
|
|
## Phase 3:降低 `xcvol` 运行时 CPU 成本
|
|||
|
|
|
|||
|
|
### 目标
|
|||
|
|
|
|||
|
|
继续逼近 `mvs` 的总时间,而不只是解决阻塞。
|
|||
|
|
|
|||
|
|
### 任务
|
|||
|
|
|
|||
|
|
1. 评估 `VolumeFieldLoader` 是否允许 payload 延迟所有权或映射式读取。
|
|||
|
|
2. 将 `xcvol` 的 metadata 和 payload 读取职责分层。
|
|||
|
|
3. 评估“直接读入 upload staging”路径,减少中间副本。
|
|||
|
|
|
|||
|
|
### 验收标准
|
|||
|
|
|
|||
|
|
1. 打开主场景后的总 volume 可见时间继续下降。
|
|||
|
|
2. CPU 峰值内存和中间复制次数下降。
|
|||
|
|
|
|||
|
|
## Phase 4:正式化验证与回归保护
|
|||
|
|
|
|||
|
|
### 目标
|
|||
|
|
|
|||
|
|
确保修复不会在后续 volume / mesh / texture 路径上回归。
|
|||
|
|
|
|||
|
|
### 任务
|
|||
|
|
|
|||
|
|
1. 增加 volume upload 相关单测或最小集成验证。
|
|||
|
|
2. 补 D3D12 路径日志断言或 profiling 钩子。
|
|||
|
|
3. 验证 Scene View / Game View / 运行时体积渲染路径一致。
|
|||
|
|
|
|||
|
|
### 验收标准
|
|||
|
|
|
|||
|
|
1. 主场景 volume 打开稳定
|
|||
|
|
2. `mvs` 与 editor 行为差距有明确量化解释
|
|||
|
|
3. 后续可以继续迭代到更强的 runtime streaming 架构
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 涉及模块
|
|||
|
|
|
|||
|
|
本轮实现预计涉及以下模块:
|
|||
|
|
|
|||
|
|
### RHI
|
|||
|
|
|
|||
|
|
- `engine/include/XCEngine/RHI/RHIDevice.h`
|
|||
|
|
- `engine/src/RHI/D3D12/D3D12Device.cpp`
|
|||
|
|
- 必要时:
|
|||
|
|
- `engine/include/XCEngine/RHI/RHITypes.h`
|
|||
|
|
- `engine/include/XCEngine/RHI/RHIEnums.h`
|
|||
|
|
- `engine/include/XCEngine/RHI/D3D12/D3D12Buffer.h`
|
|||
|
|
- `engine/src/RHI/D3D12/D3D12Buffer.cpp`
|
|||
|
|
|
|||
|
|
### Rendering
|
|||
|
|
|
|||
|
|
- `engine/src/Rendering/Caches/RenderResourceCache.cpp`
|
|||
|
|
- `engine/include/XCEngine/Rendering/Caches/RenderResourceCache.h`
|
|||
|
|
- `engine/src/Rendering/Passes/BuiltinVolumetricPass.cpp`
|
|||
|
|
- 必要时新增 volume upload service 或相关状态结构
|
|||
|
|
|
|||
|
|
### Resources / Components
|
|||
|
|
|
|||
|
|
- `engine/src/Components/VolumeRendererComponent.cpp`
|
|||
|
|
- `engine/include/XCEngine/Components/VolumeRendererComponent.h`
|
|||
|
|
- `engine/src/Resources/Volume/VolumeFieldLoader.cpp`
|
|||
|
|
- `engine/include/XCEngine/Resources/Volume/VolumeField.h`
|
|||
|
|
|
|||
|
|
### Editor / Telemetry
|
|||
|
|
|
|||
|
|
- `editor/src/Managers/SceneManager.cpp`
|
|||
|
|
- `editor/src/Viewport/ViewportHostService.h`
|
|||
|
|
|
|||
|
|
### Tests
|
|||
|
|
|
|||
|
|
- `tests/Resources/Volume/`
|
|||
|
|
- `tests/Components/test_volume_renderer_component.cpp`
|
|||
|
|
- 必要时新增 rendering / integration 回归验证
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. 风险与约束
|
|||
|
|
|
|||
|
|
## 8.1 不能粗暴全局改 `StorageBuffer = DEFAULT heap`
|
|||
|
|
|
|||
|
|
原因:
|
|||
|
|
|
|||
|
|
1. 现有其他调用方可能默认依赖 `SetData()`
|
|||
|
|
2. 全局改语义会引入隐藏回归
|
|||
|
|
3. 本轮应以“新增 initialized immutable path”为主
|
|||
|
|
|
|||
|
|
## 8.2 首先只确保 D3D12 主线正确
|
|||
|
|
|
|||
|
|
当前用户问题发生在 editor D3D12 主线,因此:
|
|||
|
|
|
|||
|
|
1. 本轮优先保证 D3D12 正确和快
|
|||
|
|
2. Vulkan / OpenGL 保持兼容,不要求一步做到同等级优化
|
|||
|
|
3. 但接口设计不要阻断后续跨后端统一
|
|||
|
|
|
|||
|
|
## 8.3 不能让 draw path 同时承担恢复和上传职责
|
|||
|
|
|
|||
|
|
这条是硬约束。
|
|||
|
|
|
|||
|
|
只要 draw path 里仍然存在“第一次看到资源就同步上传 590MB”这件事,问题就没有从根上解决。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. 完成标准
|
|||
|
|
|
|||
|
|
认为本轮修复完成,至少要同时满足以下条件:
|
|||
|
|
|
|||
|
|
1. editor 打开 `Main.xc` 时,`Runtime streaming scene assets...` 结束后不再出现十几秒主线程卡死。
|
|||
|
|
2. `cloud.nvdb` 的 volume payload 在 D3D12 下最终驻留于 GPU 本地 buffer,而不是 upload heap。
|
|||
|
|
3. `BuiltinVolumetricPass` 不再在首次 draw 时同步执行大体积上传。
|
|||
|
|
4. editor 的 volume 可见总时间相比当前主线显著下降。
|
|||
|
|
5. 修复后的行为可以用日志和代码路径明确解释,而不是只靠体感判断“快了”。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. 本轮执行顺序
|
|||
|
|
|
|||
|
|
严格按以下顺序推进:
|
|||
|
|
|
|||
|
|
1. 固化指标与日志
|
|||
|
|
2. 补 RHI initialized GPU-local buffer 能力
|
|||
|
|
3. volume 改走新上传路径
|
|||
|
|
4. 把 GPU upload 从 draw path 前移
|
|||
|
|
5. 再评估 `xcvol` 运行时 CPU 装载优化
|
|||
|
|
|
|||
|
|
不跳步骤,不同时展开多个大方向,先把最大收益点打掉。
|