feat: GPUDriven 实例化渲染——draw 数与实体数解耦

- UniformBuffer ring 支持 eStorageBuffer usage(GPUDriven 实例矩阵
  per-frame-slot,正确性保证与 UBO 一致:fence 等待后写入)
- DescriptorManager 加 UpdateStorageBuffer;池补 STORAGE_BUFFER 类型
- Set0 新增 binding1 = 实例矩阵 SSBO(vertex 读取,std430)
- Forward pass 重写:每帧从 ECS 收集实例矩阵(按 mesh 聚合 batch,
  两趟遍历保证 batch 区间连续),同 mesh 的 N 个实体合并为
  一次 drawIndexed(instanceCount=N),firstInstance 指向 batch 起点;
  vertex shader 经 gl_InstanceIndex 取模型矩阵,forward 不再用 push
- draw 数 = 唯一 mesh 数,与实体数量解耦(4096 实例上限,
  超出截断告警;聚合改哈希为后续 TODO)

验证:8 pass 全活,validation 0 error 0 warning,GPU Timings 面板
实测各 pass 毫秒,场景渲染与阴影正确
This commit is contained in:
2026-09-22 19:46:17 +08:00
parent 41e4cc85d5
commit 7fca721a24
10 changed files with 138 additions and 50 deletions
+10 -5
View File
@@ -16,9 +16,12 @@ layout(location = 1) out vec3 fragNormal;
layout(location = 2) out vec3 fragWorldPos;
layout(location = 3) out vec3 fragLightSpacePos;
layout(push_constant) uniform PushConstants {
mat4 model;
} pc;
// GPUDriven:模型矩阵从实例缓冲按实例索引取。
// 同一 mesh 的 N 个实体 = 1 次 drawIndexed(instanceCount=N),
// draw 侧用 firstInstance 指向该 batch 在实例数组中的起始下标。
layout(std430, set = 0, binding = 1) readonly buffer ObjectBuffer {
mat4 models[]; // 连续 mat4 数组(16B 对齐,std430/std140 布局一致)
} objects;
layout(set = 0, binding = 0) uniform UniformBufferObject {
mat4 view;
@@ -29,14 +32,16 @@ layout(set = 0, binding = 0) uniform UniformBufferObject {
} ubo;
void main() {
vec4 worldPos = pc.model * vec4(inPosition, 1.0);
mat4 model = objects.models[gl_InstanceIndex];
vec4 worldPos = model * vec4(inPosition, 1.0);
gl_Position = ubo.proj * ubo.view * worldPos;
fragWorldPos = worldPos.xyz;
// 光空间坐标(含 Vulkan [0,1] 深度重映射,与 shadow pass 写入端共用同一矩阵)
fragLightSpacePos = (ubo.lightSpace * worldPos).xyz;
mat3 normalMatrix = mat3(transpose(inverse(pc.model)));
mat3 normalMatrix = mat3(transpose(inverse(model)));
fragNormal = normalize(normalMatrix * inNormal);
fragUV = inUV;