perf: BuildFrame 收集段优化——实体级粗剔除 + 数组化 batch 查找 + 合并矩阵写
定位数据(100 背包 + SunTemple,RTX 5060):GPU 全帧仅 ~3ms (Forward 1.91 + Shadow 0.82),CPU BuildFrame 收集段 5.94ms 才是 帧率瓶颈。三段细分测量:pre 0.2 / each+cull 5.0 / flatten 0.4。 优化(collect 5.94 → 4.6ms,-23%): 1. Mesh 全局连续槽位(GetSlot);ViewScratch batch 查找 hash → 数组寻址(slot × kMeshLodCount 槽) 2. Model 级联合包围球(各 mesh bounds 并集):实体在四视图全部 不可见 → 跳过整个 mesh 循环(多 mesh 模型的粗剔除) 3. worlds/prevWorlds 合并为 stride-2 InstanceMatrices 单次写 (每实例每视图 2 次随机堆访问 → 1 次,cache miss 减半) 遥测:Renderer::GetCollectMs 暴露收集段耗时。剩余 4.6ms 的进一步 压缩(并行收集 / GPU-driven culling)单独立项。
This commit is contained in:
@@ -47,6 +47,8 @@ namespace Engine
|
||||
{
|
||||
return m_Lods[lod < kMeshLodCount ? lod : kMeshLodCount - 1].indexCount;
|
||||
}
|
||||
// 全局连续槽位(SceneRenderer 数组化 batch 查找用,避免逐实例 hash)
|
||||
u32 GetSlot() const { return m_Slot; }
|
||||
|
||||
// 局部空间包围球(构造时从顶点算出),视锥剔除用
|
||||
struct Bounds {
|
||||
@@ -66,6 +68,7 @@ namespace Engine
|
||||
Scope<Buffer> m_IndexBuffer;
|
||||
std::array<LodRange, kMeshLodCount> m_Lods{};
|
||||
Material* m_Material;
|
||||
u32 m_Slot = 0;
|
||||
Bounds m_Bounds;
|
||||
|
||||
u64 m_VertexBytes = 0;
|
||||
|
||||
@@ -81,6 +81,9 @@ namespace Engine
|
||||
Model& operator=(const Model&) = delete;
|
||||
|
||||
const std::vector<Scope<Mesh>>& GetMeshes() const { return m_Meshes; }
|
||||
// 全模型联合包围球(各 mesh bounds 的并集,局部空间):
|
||||
// 实体级粗剔除用——整个模型在视锥外直接跳过全部 mesh 循环
|
||||
const Mesh::Bounds& GetBounds() const { return m_Bounds; }
|
||||
|
||||
private:
|
||||
Model(Device& device, MemoryAllocator& allocator,
|
||||
@@ -103,6 +106,9 @@ namespace Engine
|
||||
bool isGltf);
|
||||
void InitDefaultTextures();
|
||||
|
||||
// 聚合各 mesh bounds 为全模型联合包围球(实体级粗剔除)
|
||||
void RebuildUnionBounds();
|
||||
|
||||
Texture* GetOrLoadTexture(const std::string& path, bool srgb);
|
||||
|
||||
// 材质缓存入口:同贴图组合+因子共享同一 Material(一个描述符集),
|
||||
@@ -124,6 +130,7 @@ namespace Engine
|
||||
std::array<Scope<Texture>, Material::kSlotCount> m_DefaultTextures;
|
||||
|
||||
std::vector<Scope<Mesh>> m_Meshes;
|
||||
Mesh::Bounds m_Bounds; // mesh 建完后聚合(实体级粗剔除)
|
||||
std::string m_ModelPath;
|
||||
bool m_IsGltf = false; // glTF 家族:MR 合图语义 + 不翻转 UV
|
||||
};
|
||||
|
||||
@@ -111,6 +111,7 @@ namespace Engine {
|
||||
u32 GetMainInstanceCount() const;
|
||||
u32 GetShadowInstanceCount() const;
|
||||
bool GetInstancesTruncated() const; // 实例预算溢出(遥测红字)
|
||||
f32 GetCollectMs() const; // CPU 收集+剔除段耗时
|
||||
|
||||
// 垂直同步开关(present mode 切换会重建 swapchain),
|
||||
// 只应在帧 UI 构建段调用(Render 之前)
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
#define HELLOVULKAN_SCENERENDERER_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/Renderer/Mesh.h>
|
||||
#include <Engine/Renderer/SceneTypes.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Engine {
|
||||
@@ -67,23 +69,26 @@ namespace Engine {
|
||||
|
||||
// ---- 剔除中间量(每帧 clear 复用,拼接进 m_Frame 后保留容量)----
|
||||
struct ViewScratch {
|
||||
// cur + prev 合并写:两次随机堆访问 → 一次(cache miss 减半)
|
||||
struct InstanceMatrices {
|
||||
glm::mat4 cur;
|
||||
glm::mat4 prev;
|
||||
};
|
||||
CullResult result;
|
||||
// 每 batch 自己的实例列表(按 mesh 分组)。GPU 端 batch 语义 =
|
||||
// firstInstance + 连续区间,实体序收集不满足(多实体共享 mesh
|
||||
// 时实例分散各处),扁平化成连续数组推迟到 appendView
|
||||
std::vector<std::vector<glm::mat4>> batchWorlds;
|
||||
std::vector<std::vector<glm::mat4>> batchPrevWorlds;
|
||||
// key = (Mesh*, LOD):同 mesh 不同 LOD 索引区间不同,
|
||||
// 必须分开成 batch(低 8 位 LOD,高位指针,指针 8 字节对齐安全)
|
||||
std::unordered_map<u64, u32> lookup;
|
||||
std::vector<std::vector<InstanceMatrices>> batchMats;
|
||||
// batch 查找:数组寻址(下标 = Mesh::GetSlot(),值 = 每 LOD 的
|
||||
// batchIdx+1,0 = 未用)——逐实例 hash 在大场景是毫秒级开销
|
||||
std::vector<std::array<u32, kMeshLodCount>> lookup;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
result.batches.clear();
|
||||
result.instanceCount = 0;
|
||||
batchWorlds.clear();
|
||||
batchPrevWorlds.clear();
|
||||
lookup.clear();
|
||||
batchMats.clear();
|
||||
for (auto& e : lookup) e.fill(0); // 容量保留,置零复用
|
||||
}
|
||||
};
|
||||
ViewScratch m_MainScratch;
|
||||
|
||||
Reference in New Issue
Block a user