diff --git a/Engine/include/Engine/Renderer/Renderer.h b/Engine/include/Engine/Renderer/Renderer.h index 9499bab..8f3f92f 100644 --- a/Engine/include/Engine/Renderer/Renderer.h +++ b/Engine/include/Engine/Renderer/Renderer.h @@ -140,7 +140,7 @@ namespace Engine { // GPUDriven:当帧实例数据(Render 期收集,Forward pass 消费) std::vector m_InstanceBatches; std::vector m_InstanceModels; - static constexpr u32 kMaxInstances = 4096; + static constexpr u32 kMaxInstances = 16384; // 可选叠加层(不拥有;Application 持有) ImGuiLayer* m_ImGuiLayer = nullptr; diff --git a/Engine/src/Renderer/Renderer.cpp b/Engine/src/Renderer/Renderer.cpp index 5f1a5e4..548fb51 100644 --- a/Engine/src/Renderer/Renderer.cpp +++ b/Engine/src/Renderer/Renderer.cpp @@ -52,10 +52,7 @@ namespace Engine constexpr u32 kTextureSetIndex = 1; constexpr u32 kShadowSetIndex = 2; constexpr u32 kMaxLights = 4; - - // Shadow pass:push constant = { lightVP, model } constexpr u32 kShadowMapSize = 2048; - constexpr u32 kShadowPushSize = static_cast(2 * sizeof(glm::mat4)); // 全屏 pass 通用 push(16B):所有全屏 pass(Tonemap/Bloom 系) // 统一声明与推送,规避 VUID-08878 的"range 定义必须一致" @@ -111,9 +108,10 @@ namespace Engine m_PipelineLayout = m_DescriptorManager->CreatePipelineLayout( {m_UBOSetLayout, m_TextureSetLayout, m_ShadowSetLayout}, 0); - // Shadow pass 独立 layout:无 set,128B push constant + // Shadow pass layout:Set0 = UBO/ObjectBuffer(实例矩阵),push = lightVP m_ShadowPipelineLayout = m_DescriptorManager->CreatePipelineLayout( - std::vector{}, kShadowPushSize); + std::vector{m_UBOSetLayout}, + sizeof(glm::mat4)); // 7. ShaderManager(VK_EXT_shader_object) m_ShaderManager = CreateScope(*m_Device); @@ -135,10 +133,14 @@ namespace Engine // shadow shader:depth-only,无 set auto shadowVert = m_ShaderManager->CreateFromFile( shaderDir + "shadow.vert.spv", - vk::ShaderStageFlagBits::eVertex, {}, kShadowPushSize); + vk::ShaderStageFlagBits::eVertex, + std::vector{m_UBOSetLayout}, + sizeof(glm::mat4)); auto shadowFrag = m_ShaderManager->CreateFromFile( shaderDir + "shadow.frag.spv", - vk::ShaderStageFlagBits::eFragment, {}, kShadowPushSize); + vk::ShaderStageFlagBits::eFragment, + std::vector{m_UBOSetLayout}, + sizeof(glm::mat4)); m_ShadowVertShader = shadowVert.Handle; m_ShadowFragShader = shadowFrag.Handle; @@ -225,7 +227,6 @@ namespace Engine // 10. Per-frame UBO ring + GPUDriven 实例 SSBO ring // (同一 ring 类,SSBO 只是 usage 不同;正确性保证一致) - constexpr u32 kMaxInstances = 4096; m_UniformBuffer = CreateScope( *m_Device, *m_Allocator, sizeof(UniformBufferObject), kMaxFramesInFlight); m_ObjectBuffer = CreateScope( @@ -445,29 +446,22 @@ namespace Engine vk::Rect2D scissor({0, 0}, {kShadowMapSize, kShadowMapSize}); cmd.setScissorWithCount(scissor); - // ---- 绘制:push {lightVP, model},与 Forward 用同一份 world 矩阵 ---- - struct ShadowPush { - glm::mat4 lightVP; - glm::mat4 model; - }; + // ---- 绘制:GPUDriven——与 Forward 共用同一批 batch ---- + // lightVP 全 pass 只 push 一次;模型矩阵经实例索引从 + // ObjectBuffer 取(与 Forward 同一份数据) + cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, + m_ShadowPipelineLayout, 0, + m_UBODescriptorSets[ctx.frame.frameSlot], {}); - m_BoundScene->Each( - [&](Entity, TransformComponent& transform, - MeshRendererComponent& meshRenderer) { - Model* model = m_BoundAssets->GetModel(meshRenderer.model); - if (!model) return; + // stage 必须覆盖 layout range 声明的全部 stage(VUID-01796) + cmd.pushConstants(m_ShadowPipelineLayout, + vk::ShaderStageFlagBits::eVertex | + vk::ShaderStageFlagBits::eFragment, + 0, sizeof(glm::mat4), &m_LightSpace); - const ShadowPush push{m_LightSpace, transform.world}; - // stage 必须覆盖 layout range 声明的全部 stage(VUID-01796) - cmd.pushConstants(m_ShadowPipelineLayout, - vk::ShaderStageFlagBits::eVertex | - vk::ShaderStageFlagBits::eFragment, - 0, kShadowPushSize, &push); - - for (auto& mesh : model->GetMeshes()) { - mesh->Draw(cmd); - } - }); + for (const auto& batch : m_InstanceBatches) { + batch.mesh->Draw(cmd, batch.count, batch.first); + } cmd.endRendering(); }) @@ -848,6 +842,17 @@ namespace Engine ? static_cast(end - start) * m_TimestampPeriod / 1.0e6f : 0.0f; } + + // 控制台遥测:每 120 次读取(≈60 帧 @2 slot)输出一行 + static u32 telemetryCount = 0; + if (++telemetryCount % 120 == 1) { + std::string line = "GPU times:"; + for (u32 i = 0; i < passCount; ++i) { + line += " " + m_PassNames[i] + "=" + + std::to_string(m_PassGpuMs[i]).substr(0, 5) + "ms"; + } + ENGINE_INFO("{}", line); + } } void Renderer::Render(ECS::Scene& scene, Assets::AssetManager& assets) @@ -973,8 +978,13 @@ namespace Engine } } if (instanceTruncated) { - ENGINE_WARN("GPUDriven: instance buffer full ({}), entities truncated", - kMaxInstances); + // 只告警一次(每帧告警会刷屏) + static bool warned = false; + if (!warned) { + warned = true; + ENGINE_WARN("GPUDriven: instance buffer full ({}), entities truncated", + kMaxInstances); + } } if (!m_InstanceModels.empty()) { m_ObjectBuffer->Update(frameSlot, m_InstanceModels.data(), diff --git a/shaders/shadow.vert b/shaders/shadow.vert index 6becc0e..0a74eaa 100644 --- a/shaders/shadow.vert +++ b/shaders/shadow.vert @@ -1,17 +1,19 @@ #version 450 // Shadow pass vertex:光源视角写深度。 -// push constant = { lightVP, model },与 Forward pass 用同一份 -// lightVP(UBO.lightSpace)和同一份 model(TransformComponent.world), -// 保证阴影贴图与主视角几何严格一致。 +// GPUDriven:模型矩阵经 gl_InstanceIndex 从实例缓冲取(与 Forward +// 共用同一份数据),push 只带 lightVP(全 pass 一次)。 layout(location = 0) in vec3 inPosition; -layout(push_constant) uniform ShadowPush { +layout(std430, set = 0, binding = 1) readonly buffer ObjectBuffer { + mat4 models[]; +} objects; + +layout(push_constant) uniform PushConstants { mat4 lightVP; - mat4 model; } pc; void main() { - gl_Position = pc.lightVP * pc.model * vec4(inPosition, 1.0); + gl_Position = pc.lightVP * objects.models[gl_InstanceIndex] * vec4(inPosition, 1.0); }