vampire-like/docs/TODO.md

5.6 KiB
Raw Blame History

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”这条渲染路径。

组件规划

  1. 新增批量渲染组件骨架

    • 建议文件:
      • Assets/GameMain/Scripts/CustomComponent/InstanceRendering/InstanceRendererComponent.cs
      • Assets/GameMain/Scripts/CustomComponent/InstanceRendering/EnemyInstanceRegistry.cs
      • Assets/GameMain/Scripts/CustomComponent/InstanceRendering/EnemyInstanceBatch.cs
    • 建议职责:
      • InstanceRendererComponent:帧级调度、统一提交 draw call。
      • EnemyInstanceRegistry:维护 EntityId -> RenderBinding,缓存 mesh/material/archetype。
      • EnemyInstanceBatch:缓存每组矩阵、颜色、闪白参数和批次拆分结果。
  2. 接入 GameEntry

    • 文件:Assets/GameMain/Scripts/Base/GameEntry.Custom.cs
    • 处理:
      • 增加 GameEntry.InstanceRenderer
      • SimulationWorld 一样在启动时自动获取或挂载组件

第一阶段:低风险版 DrawMeshInstanced

  1. 建立渲染注册表

    • 注册时机:敌人 show 时缓存渲染资源引用hide 时释放注册。
    • 注册内容:
      • EntityId
      • Mesh
      • Material
      • 阴影/Layer/子类型信息
      • 受击闪白、稀有度色等实例化属性默认值
    • 约束:
      • 注册表只缓存渲染资源和 archetype不缓存位置真相数据。
  2. SimulationWorld 读取实例数据

    • 读取来源:_enemies 或对外只读查询接口。
    • 每帧收集:
      • Matrix4x4
      • 朝向/缩放
      • flashAmount
      • baseColor
      • 其他实例化参数
    • 约束:
      • 由 sim 输出生成当前帧渲染数据,不从敌人 Transform 反推逻辑状态。
  3. 按批次分组并提交

    • 分组键至少包含:
      • Mesh
      • Material
      • ShadowCastingMode
      • ReceiveShadows
      • Layer
      • 需要的渲染状态位
    • 提交方式:
      • 每批最多 1023 实例
      • 使用 Graphics.DrawMeshInstanced
      • 通过 MaterialPropertyBlock 下发实例颜色和闪白参数
  4. 接入现有 Instanced Shader

    • 文件:Assets/GameMain/Materials/Shaders/SimpleInstancedFlash.shader
    • 处理:
      • 复用现有 _BaseColor / _FlashColor / _FlashAmount 实例化属性
      • 确认敌人材质启用 instancing
      • 不在第一阶段同时改复杂动画或多 Pass 材质
  5. 替换敌人独立 Renderer 路径

    • 第一阶段做法:
      • 保留敌人实体和挂点
      • 禁用敌人身上的 MeshRenderer/SkinnedMeshRenderer
      • 改由 InstanceRendererComponent 统一绘制
    • 约束:
      • 碰撞、伤害、掉落、状态切换仍完全走现有逻辑链路

第二阶段:高上限版 DrawMeshInstancedIndirect

  1. 升级批次数据结构

    • Matrix4x4[] 和实例属性数组迁移到 ComputeBuffer / GraphicsBuffer
    • 建立按 archetype 持久复用的 buffer避免每帧重建
  2. 升级提交方式

    • 使用 Graphics.DrawMeshInstancedIndirect
    • 为实例数量、剔除结果和参数下发建立独立 buffer
  3. 逐步加入可选优化

    • 视锥剔除
    • 距离分层
    • LOD
    • 大规模敌人下的批次复用和 buffer 容量管理
  4. 进入该阶段的前提

    • DrawMeshInstanced 版本已验证视觉正确
    • 5k 规模下 draw call 或主线程提交成本仍是瓶颈
    • 否则不要过早把复杂度拉到 Indirect

风险控制

  • 不要把 GPU batch 容器放进 SimulationWorld 主状态里。
  • 不要让碰撞、索引或目标选择依赖 InstanceRendererComponent
  • 不要一上来就移除 TransformSync,先保住现有 HPBar/特效/挂点行为。
  • 不要在第一阶段同时处理敌人、投射物、掉落物三类 instancing。

验证口径

  1. 功能回归

    • 敌人朝向、受击闪白、死亡隐藏、精英/普通配色与当前一致
    • Battle -> LevelUp -> Shop -> Battle 循环不丢渲染实例
  2. Profiling 指标

    • 0.5k / 1k / 1.5k / 2k / 5k 敌人
    • Draw Calls
    • SetPass Calls
    • Main Thread
    • Render Thread / GPU 时间
  3. 阶段验收

    • 第一阶段验收:
      • 先证明 DrawMeshInstanced 跑通且视觉一致
      • Draw Calls 相比当前路径明显下降
    • 第二阶段验收:
      • 5k 敌人规模下主线程提交和渲染耗时继续下降
      • buffer 生命周期稳定,无泄漏、无错绘、无实例残留