5.6 KiB
5.6 KiB
CodeX TODO
GPU Instancing 改造路线
目标定义
SimulationWorld继续作为敌人position / rotation / state的唯一真相源。- 新增
InstanceRendererComponent作为 Presentation 侧批量渲染组件,不把批次构建和 GPU Buffer 管理塞回SimulationWorld。 - 先做
Graphics.DrawMeshInstanced低风险版,确认链路跑通后,再决定是否升级到DrawMeshInstancedIndirect。 - P3 首阶段只处理“敌人批量渲染”,不同时扩大到投射物、掉落物和所有特效。
架构边界
SimulationWorld- 只负责逻辑 Tick、生命周期同步、状态容器与碰撞结算。
- 不负责
Mesh / Material / MaterialPropertyBlock / ComputeBuffer。
InstanceRendererComponent- 只负责收集可渲染实例、按
Mesh + Material + Shadow + Layer + RenderState分组、构建 batch、发起 draw call。 - 只消费
SimulationWorld输出,不反写 sim 状态。
- 只负责收集可渲染实例、按
TransformSync- P3 第一阶段继续保留,用于 HPBar、受击特效、挂点和现有表现依赖。
- 不要求一开始就把所有敌人
Transform写回移除,否则风险过高。
- 敌人 GameObject
- 第一阶段继续保留,用于碰撞、生命周期、事件桥和表现挂点。
- 仅逐步移除“每敌人独立 MeshRenderer”这条渲染路径。
组件规划
-
新增批量渲染组件骨架
- 建议文件:
Assets/GameMain/Scripts/CustomComponent/InstanceRendering/InstanceRendererComponent.csAssets/GameMain/Scripts/CustomComponent/InstanceRendering/EnemyInstanceRegistry.csAssets/GameMain/Scripts/CustomComponent/InstanceRendering/EnemyInstanceBatch.cs
- 建议职责:
InstanceRendererComponent:帧级调度、统一提交 draw call。EnemyInstanceRegistry:维护EntityId -> RenderBinding,缓存 mesh/material/archetype。EnemyInstanceBatch:缓存每组矩阵、颜色、闪白参数和批次拆分结果。
- 建议文件:
-
接入
GameEntry- 文件:
Assets/GameMain/Scripts/Base/GameEntry.Custom.cs - 处理:
- 增加
GameEntry.InstanceRenderer - 与
SimulationWorld一样在启动时自动获取或挂载组件
- 增加
- 文件:
第一阶段:低风险版 DrawMeshInstanced
-
建立渲染注册表
- 注册时机:敌人 show 时缓存渲染资源引用,hide 时释放注册。
- 注册内容:
EntityIdMeshMaterial- 阴影/Layer/子类型信息
- 受击闪白、稀有度色等实例化属性默认值
- 约束:
- 注册表只缓存渲染资源和 archetype,不缓存位置真相数据。
-
从
SimulationWorld读取实例数据- 读取来源:
_enemies或对外只读查询接口。 - 每帧收集:
Matrix4x4- 朝向/缩放
flashAmountbaseColor- 其他实例化参数
- 约束:
- 由 sim 输出生成当前帧渲染数据,不从敌人
Transform反推逻辑状态。
- 由 sim 输出生成当前帧渲染数据,不从敌人
- 读取来源:
-
按批次分组并提交
- 分组键至少包含:
MeshMaterialShadowCastingModeReceiveShadowsLayer- 需要的渲染状态位
- 提交方式:
- 每批最多
1023实例 - 使用
Graphics.DrawMeshInstanced - 通过
MaterialPropertyBlock下发实例颜色和闪白参数
- 每批最多
- 分组键至少包含:
-
接入现有 Instanced Shader
- 文件:
Assets/GameMain/Materials/Shaders/SimpleInstancedFlash.shader - 处理:
- 复用现有
_BaseColor / _FlashColor / _FlashAmount实例化属性 - 确认敌人材质启用 instancing
- 不在第一阶段同时改复杂动画或多 Pass 材质
- 复用现有
- 文件:
-
替换敌人独立 Renderer 路径
- 第一阶段做法:
- 保留敌人实体和挂点
- 禁用敌人身上的
MeshRenderer/SkinnedMeshRenderer - 改由
InstanceRendererComponent统一绘制
- 约束:
- 碰撞、伤害、掉落、状态切换仍完全走现有逻辑链路
- 第一阶段做法:
第二阶段:高上限版 DrawMeshInstancedIndirect
-
升级批次数据结构
- 将
Matrix4x4[]和实例属性数组迁移到ComputeBuffer/GraphicsBuffer - 建立按 archetype 持久复用的 buffer,避免每帧重建
- 将
-
升级提交方式
- 使用
Graphics.DrawMeshInstancedIndirect - 为实例数量、剔除结果和参数下发建立独立 buffer
- 使用
-
逐步加入可选优化
- 视锥剔除
- 距离分层
- LOD
- 大规模敌人下的批次复用和 buffer 容量管理
-
进入该阶段的前提
DrawMeshInstanced版本已验证视觉正确5k规模下 draw call 或主线程提交成本仍是瓶颈- 否则不要过早把复杂度拉到
Indirect
风险控制
- 不要把 GPU batch 容器放进
SimulationWorld主状态里。 - 不要让碰撞、索引或目标选择依赖
InstanceRendererComponent。 - 不要一上来就移除
TransformSync,先保住现有 HPBar/特效/挂点行为。 - 不要在第一阶段同时处理敌人、投射物、掉落物三类 instancing。
验证口径
-
功能回归
- 敌人朝向、受击闪白、死亡隐藏、精英/普通配色与当前一致
Battle -> LevelUp -> Shop -> Battle循环不丢渲染实例
-
Profiling 指标
0.5k / 1k / 1.5k / 2k / 5k敌人Draw CallsSetPass CallsMain Thread- Render Thread / GPU 时间
-
阶段验收
- 第一阶段验收:
- 先证明
DrawMeshInstanced跑通且视觉一致 - Draw Calls 相比当前路径明显下降
- 先证明
- 第二阶段验收:
5k敌人规模下主线程提交和渲染耗时继续下降- buffer 生命周期稳定,无泄漏、无错绘、无实例残留
- 第一阶段验收: