diff --git a/App/main.cpp b/App/main.cpp index d79ab24..6e03e69 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -22,13 +22,13 @@ #include #include +#include #include #include #include - using namespace Engine; using namespace Engine::ECS; @@ -88,7 +88,7 @@ namespace const auto model = GetAssets().LoadModel("assets/models/backpack/backpack.obj"); // const auto model = GetAssets().LoadModel("assets/models/pocket_watch_4k.gltf/pocket_watch_4k.gltf"); - int countSize = 2; + int countSize = 10; for (int i = 0; i < countSize; i ++) { for (int j = 0; j < countSize; j ++) @@ -316,23 +316,31 @@ namespace { // 动态环境切换(Assets 层加载 → Renderer 换绑;缓存去重) ImGui::Text("Environment (IBL)"); - int envIdx = m_EnvIndex; - if (ImGui::Combo("env", &envIdx, kEnvNames, kEnvCount)) + bool& DrawSkybox = GetScene().GetSettings().DrawSkybox; + ImGui::Checkbox("DrawSkybox", &DrawSkybox); + if (DrawSkybox) { - // 异步:解码后台线程,完成后 OnUpdate 里换绑 - m_EnvIndex = envIdx; - RequestEnvironment(envIdx); - } - ImGui::Text(m_EnvLoadBusy ? "(decoding...)" : - "(switch = compute pre-integrate, ms-level)"); + int envIdx = m_EnvIndex; + if (ImGui::Combo("env", &envIdx, kEnvNames, kEnvCount)) + { + // 异步:解码后台线程,完成后 OnUpdate 里换绑 + m_EnvIndex = envIdx; + RequestEnvironment(envIdx); + } + ImGui::Text(m_EnvLoadBusy ? "(decoding...)" : + "(switch = compute pre-integrate, ms-level)"); - // 调试视图:单看某个 pass 的中间输出(类 UE pass debug) - const auto& viewNames = renderer.GetDebugViewNames(); - int view = renderer.GetDebugView(); - if (ImGui::Combo("Debug View", &view, viewNames.data(), - static_cast(viewNames.size()))) + // 调试视图:单看某个 pass 的中间输出(类 UE pass debug) + const auto& viewNames = renderer.GetDebugViewNames(); + int view = renderer.GetDebugView(); + if (ImGui::Combo("Debug View", &view, viewNames.data(), + static_cast(viewNames.size()))) + { + renderer.SetDebugView(view); + } + }else { - renderer.SetDebugView(view); + ImGui::ColorEdit4("ClearColor", glm::value_ptr(GetScene().GetSettings().ClearColor)); } } @@ -343,6 +351,10 @@ namespace ImGui::Text("instances: %u main / %u shadow", renderer.GetMainInstanceCount(), renderer.GetShadowInstanceCount()); + if (renderer.GetInstancesTruncated()) { + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), + "TRUNCATED: instance buffer full!"); + } const auto& names = renderer.GetPassNames(); const auto& times = renderer.GetPassGpuMs(); for (size_t i = 0; i < names.size() && i < times.size(); ++i) diff --git a/Engine/include/Engine/ECS/Scene.h b/Engine/include/Engine/ECS/Scene.h index c97fc95..7425b41 100644 --- a/Engine/include/Engine/ECS/Scene.h +++ b/Engine/include/Engine/ECS/Scene.h @@ -41,7 +41,9 @@ namespace Engine::ECS { // ---- 场景级设置 ---- struct Settings { - glm::vec4 ambientColor{0.07f, 0.07f, 0.09f, 0.0f}; + glm::vec4 ClearColor = {0.1, 0.1, 0.1, 1.0}; + glm::vec4 AmbientColor{0.07f, 0.07f, 0.09f, 0.0f}; + bool DrawSkybox = true; }; Settings& GetSettings() { return m_Settings; } diff --git a/Engine/include/Engine/Renderer/Renderer.h b/Engine/include/Engine/Renderer/Renderer.h index 9295279..0cf567b 100644 --- a/Engine/include/Engine/Renderer/Renderer.h +++ b/Engine/include/Engine/Renderer/Renderer.h @@ -109,6 +109,7 @@ namespace Engine { // 双视图剔除后的实例数(遥测) u32 GetMainInstanceCount() const; u32 GetShadowInstanceCount() const; + bool GetInstancesTruncated() const; // 实例预算溢出(遥测红字) // 垂直同步开关(present mode 切换会重建 swapchain), // 只应在帧 UI 构建段调用(Render 之前) diff --git a/Engine/include/Engine/Renderer/SceneTypes.h b/Engine/include/Engine/Renderer/SceneTypes.h index 09adfc3..03e6033 100644 --- a/Engine/include/Engine/Renderer/SceneTypes.h +++ b/Engine/include/Engine/Renderer/SceneTypes.h @@ -33,7 +33,11 @@ namespace Engine { }; constexpr u32 kMaxLights = 4; - constexpr u32 kMaxInstances = 16384; // 实例 SSBO 容量(矩阵数) + // 实例 SSBO 容量(矩阵数)。四视图(main + 3 级联)共享同一预算: + // 需求 = Σ各视图剔除后实例数,大场景斜光时阴影实例可膨胀到主视图 + // 的 2-3 倍。65536 = 4MB/槽位,覆盖 ~800 实体 × 79 mesh 全级联; + // 再往上就该上 LOD / GPU culling,而不是继续加预算 + constexpr u32 kMaxInstances = 65536; constexpr u32 kMaxCascades = 3; // CSM 级联数 constexpr u32 kCascadeRes = 1024; // 每级联阴影图边长 @@ -69,6 +73,9 @@ namespace Engine { struct FrameSceneData { UniformBufferObject ubo{}; + glm::vec4 ClearColor{0.1, 0.1, 0.1, 1.0}; + bool DrawSkybox = true; + // 天空盒反投影矩阵:inverse(projNoJitter × mat4(mat3(view))) //(视图去平移只留旋转 → 反投影结果即世界方向;Skybox pass push) glm::mat4 skyInvVP{1.0f}; @@ -90,6 +97,10 @@ namespace Engine { bool bloomEnabled = true; // BloomPass 子 pass 空转 + tonemap 强度归零 int debugView = 0; // Passes::DebugView 枚举(tonemap 路由上屏) + // 实例预算溢出(kMaxInstances):本轮收集有实例被静默丢弃—— + // 必须遥测可见,"只告警一次"的大场景截断等于没有告警 + bool instancesTruncated = false; + f32 collectMs = 0.0f; // 收集+剔除 CPU 耗时(遥测) }; diff --git a/Engine/src/Renderer/Passes/SkyboxPass.cpp b/Engine/src/Renderer/Passes/SkyboxPass.cpp index e32f82b..1c0ac87 100644 --- a/Engine/src/Renderer/Passes/SkyboxPass.cpp +++ b/Engine/src/Renderer/Passes/SkyboxPass.cpp @@ -94,9 +94,10 @@ namespace Engine::Passes ZoneScopedN("Skybox"); const vk::CommandBuffer cmd = ctx.cmd; + const glm::vec4& cc = m_Frame.ClearColor; vk::ClearValue clear; clear.color = vk::ClearColorValue( - std::array{0.1f, 0.1f, 0.1f, 1.0f}); + std::array{cc.r, cc.g, cc.b, cc.a}); vk::RenderingAttachmentInfo color; color.setImageView(ctx.View(m_HDRColor)) @@ -112,13 +113,13 @@ namespace Engine::Passes cmd.beginRendering(renderingInfo); - if (m_Env) { + if (m_Env && m_Frame.DrawSkybox) { // 无环境贴图:只清屏(Forward loadOp=Load 依赖这里先写) - std::array stages = { + const std::array stages = { vk::ShaderStageFlagBits::eVertex, vk::ShaderStageFlagBits::eFragment }; - std::array shaders = {m_Vert, m_Frag}; + const std::array shaders = {m_Vert, m_Frag}; cmd.bindShadersEXT(stages, shaders); cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, @@ -149,12 +150,12 @@ namespace Engine::Passes cmd.setVertexInputEXT({}, {}); cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1); - vk::SampleMask sampleMask = 0xFFFFFFFF; + constexpr vk::SampleMask sampleMask = 0xFFFFFFFF; cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask); cmd.setAlphaToCoverageEnableEXT(VK_FALSE); cmd.setAlphaToOneEnableEXT(VK_FALSE); - vk::Viewport viewport(0.0f, 0.0f, + const vk::Viewport viewport(0.0f, 0.0f, static_cast(ctx.extent.width), static_cast(ctx.extent.height), 0.0f, 1.0f); @@ -170,8 +171,8 @@ namespace Engine::Passes .setDstAlphaBlendFactor(vk::BlendFactor::eZero) .setAlphaBlendOp(vk::BlendOp::eAdd); // ArrayProxyNoTemporaries 拒绝临时量:具名局部变量传入 - vk::Bool32 blendEnable = VK_FALSE; - vk::ColorComponentFlags colorWriteMask = + constexpr vk::Bool32 blendEnable = VK_FALSE; + constexpr vk::ColorComponentFlags colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | diff --git a/Engine/src/Renderer/Renderer.cpp b/Engine/src/Renderer/Renderer.cpp index 7f9c877..78a3409 100644 --- a/Engine/src/Renderer/Renderer.cpp +++ b/Engine/src/Renderer/Renderer.cpp @@ -215,6 +215,11 @@ namespace Engine return m_SceneRenderer->GetMainInstanceCount(); } + bool Renderer::GetInstancesTruncated() const + { + return m_SceneRenderer->Frame().instancesTruncated; + } + u32 Renderer::GetShadowInstanceCount() const { return m_SceneRenderer->GetShadowInstanceCount(); diff --git a/Engine/src/Renderer/SceneRenderer.cpp b/Engine/src/Renderer/SceneRenderer.cpp index 0a2786d..1416f23 100644 --- a/Engine/src/Renderer/SceneRenderer.cpp +++ b/Engine/src/Renderer/SceneRenderer.cpp @@ -166,9 +166,11 @@ namespace Engine ubo.proj[2][1] += jy * 2.0f / static_cast(targetExtent.height); } - ubo.ambientColor = scene.GetSettings().ambientColor; + ubo.ambientColor = scene.GetSettings().AmbientColor; // ---- 运行时开关/调试视图落帧(各 pass 与 frag 只读契约字段)---- + m_Frame.ClearColor = scene.GetSettings().ClearColor; + m_Frame.DrawSkybox = scene.GetSettings().DrawSkybox; m_Frame.shadowsEnabled = m_ShadowsEnabled; m_Frame.taaEnabled = m_TAAEnabled; m_Frame.bloomEnabled = m_BloomEnabled; @@ -363,11 +365,13 @@ namespace Engine m_LastProjNoJitter = ubo.projNoJitter; ++m_FrameIndex; + // 截断状态每帧落遥测(ImGui 可见)——"只告警一次"的日志对 + // 大场景不够:预算是动态穿越的,用户必须实时看到截断发生 + m_Frame.instancesTruncated = instanceTruncated; if (instanceTruncated && !m_TruncationWarned) { - // 只告警一次(每帧告警会刷屏) m_TruncationWarned = true; - ENGINE_WARN("GPUDriven: instance buffer full ({}), entities truncated", - kMaxInstances); + ENGINE_WARN("GPUDriven: instance buffer full ({}), entities truncated " + "(见 Scene Debug 面板红字)", kMaxInstances); } m_Frame.collectMs = std::chrono::duration(