feat: 透明窗口 + 实例预算扩容(修复斜光阴影静默截断)
透明窗口:SceneSettings 加 ClearColor/DrawSkybox,随 FrameSceneData 每帧下发到 SkyboxPass(替换硬编码清屏色);GLFW 透明帧缓冲已开, tonemap 预乘 alpha 直通已有,链路全通。TAA alpha 时域混合为已知边界。 实例预算:kMaxInstances 16384 → 65536(四视图共享预算,100 实体 斜光时需求 16.4k 超限,cascade 2 实例被静默丢弃 = 用户看到的 "拖灯光方向部分背包阴影消失")。截断状态每帧落遥测 + ImGui 红字 (原 warn-once 对动态穿越阈值的场景等于没有告警)。
This commit is contained in:
+28
-16
@@ -22,13 +22,13 @@
|
|||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
|
||||||
using namespace Engine;
|
using namespace Engine;
|
||||||
using namespace Engine::ECS;
|
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/backpack/backpack.obj");
|
||||||
// const auto model = GetAssets().LoadModel("assets/models/pocket_watch_4k.gltf/pocket_watch_4k.gltf");
|
// 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 i = 0; i < countSize; i ++)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < countSize; j ++)
|
for (int j = 0; j < countSize; j ++)
|
||||||
@@ -316,23 +316,31 @@ namespace
|
|||||||
{
|
{
|
||||||
// 动态环境切换(Assets 层加载 → Renderer 换绑;缓存去重)
|
// 动态环境切换(Assets 层加载 → Renderer 换绑;缓存去重)
|
||||||
ImGui::Text("Environment (IBL)");
|
ImGui::Text("Environment (IBL)");
|
||||||
int envIdx = m_EnvIndex;
|
bool& DrawSkybox = GetScene().GetSettings().DrawSkybox;
|
||||||
if (ImGui::Combo("env", &envIdx, kEnvNames, kEnvCount))
|
ImGui::Checkbox("DrawSkybox", &DrawSkybox);
|
||||||
|
if (DrawSkybox)
|
||||||
{
|
{
|
||||||
// 异步:解码后台线程,完成后 OnUpdate 里换绑
|
int envIdx = m_EnvIndex;
|
||||||
m_EnvIndex = envIdx;
|
if (ImGui::Combo("env", &envIdx, kEnvNames, kEnvCount))
|
||||||
RequestEnvironment(envIdx);
|
{
|
||||||
}
|
// 异步:解码后台线程,完成后 OnUpdate 里换绑
|
||||||
ImGui::Text(m_EnvLoadBusy ? "(decoding...)" :
|
m_EnvIndex = envIdx;
|
||||||
"(switch = compute pre-integrate, ms-level)");
|
RequestEnvironment(envIdx);
|
||||||
|
}
|
||||||
|
ImGui::Text(m_EnvLoadBusy ? "(decoding...)" :
|
||||||
|
"(switch = compute pre-integrate, ms-level)");
|
||||||
|
|
||||||
// 调试视图:单看某个 pass 的中间输出(类 UE pass debug)
|
// 调试视图:单看某个 pass 的中间输出(类 UE pass debug)
|
||||||
const auto& viewNames = renderer.GetDebugViewNames();
|
const auto& viewNames = renderer.GetDebugViewNames();
|
||||||
int view = renderer.GetDebugView();
|
int view = renderer.GetDebugView();
|
||||||
if (ImGui::Combo("Debug View", &view, viewNames.data(),
|
if (ImGui::Combo("Debug View", &view, viewNames.data(),
|
||||||
static_cast<int>(viewNames.size())))
|
static_cast<int>(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",
|
ImGui::Text("instances: %u main / %u shadow",
|
||||||
renderer.GetMainInstanceCount(),
|
renderer.GetMainInstanceCount(),
|
||||||
renderer.GetShadowInstanceCount());
|
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& names = renderer.GetPassNames();
|
||||||
const auto& times = renderer.GetPassGpuMs();
|
const auto& times = renderer.GetPassGpuMs();
|
||||||
for (size_t i = 0; i < names.size() && i < times.size(); ++i)
|
for (size_t i = 0; i < names.size() && i < times.size(); ++i)
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ namespace Engine::ECS {
|
|||||||
|
|
||||||
// ---- 场景级设置 ----
|
// ---- 场景级设置 ----
|
||||||
struct Settings {
|
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; }
|
Settings& GetSettings() { return m_Settings; }
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ namespace Engine {
|
|||||||
// 双视图剔除后的实例数(遥测)
|
// 双视图剔除后的实例数(遥测)
|
||||||
u32 GetMainInstanceCount() const;
|
u32 GetMainInstanceCount() const;
|
||||||
u32 GetShadowInstanceCount() const;
|
u32 GetShadowInstanceCount() const;
|
||||||
|
bool GetInstancesTruncated() const; // 实例预算溢出(遥测红字)
|
||||||
|
|
||||||
// 垂直同步开关(present mode 切换会重建 swapchain),
|
// 垂直同步开关(present mode 切换会重建 swapchain),
|
||||||
// 只应在帧 UI 构建段调用(Render 之前)
|
// 只应在帧 UI 构建段调用(Render 之前)
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ namespace Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constexpr u32 kMaxLights = 4;
|
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 kMaxCascades = 3; // CSM 级联数
|
||||||
constexpr u32 kCascadeRes = 1024; // 每级联阴影图边长
|
constexpr u32 kCascadeRes = 1024; // 每级联阴影图边长
|
||||||
|
|
||||||
@@ -69,6 +73,9 @@ namespace Engine {
|
|||||||
struct FrameSceneData {
|
struct FrameSceneData {
|
||||||
UniformBufferObject ubo{};
|
UniformBufferObject ubo{};
|
||||||
|
|
||||||
|
glm::vec4 ClearColor{0.1, 0.1, 0.1, 1.0};
|
||||||
|
bool DrawSkybox = true;
|
||||||
|
|
||||||
// 天空盒反投影矩阵:inverse(projNoJitter × mat4(mat3(view)))
|
// 天空盒反投影矩阵:inverse(projNoJitter × mat4(mat3(view)))
|
||||||
//(视图去平移只留旋转 → 反投影结果即世界方向;Skybox pass push)
|
//(视图去平移只留旋转 → 反投影结果即世界方向;Skybox pass push)
|
||||||
glm::mat4 skyInvVP{1.0f};
|
glm::mat4 skyInvVP{1.0f};
|
||||||
@@ -90,6 +97,10 @@ namespace Engine {
|
|||||||
bool bloomEnabled = true; // BloomPass 子 pass 空转 + tonemap 强度归零
|
bool bloomEnabled = true; // BloomPass 子 pass 空转 + tonemap 强度归零
|
||||||
int debugView = 0; // Passes::DebugView 枚举(tonemap 路由上屏)
|
int debugView = 0; // Passes::DebugView 枚举(tonemap 路由上屏)
|
||||||
|
|
||||||
|
// 实例预算溢出(kMaxInstances):本轮收集有实例被静默丢弃——
|
||||||
|
// 必须遥测可见,"只告警一次"的大场景截断等于没有告警
|
||||||
|
bool instancesTruncated = false;
|
||||||
|
|
||||||
f32 collectMs = 0.0f; // 收集+剔除 CPU 耗时(遥测)
|
f32 collectMs = 0.0f; // 收集+剔除 CPU 耗时(遥测)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -94,9 +94,10 @@ namespace Engine::Passes
|
|||||||
ZoneScopedN("Skybox");
|
ZoneScopedN("Skybox");
|
||||||
const vk::CommandBuffer cmd = ctx.cmd;
|
const vk::CommandBuffer cmd = ctx.cmd;
|
||||||
|
|
||||||
|
const glm::vec4& cc = m_Frame.ClearColor;
|
||||||
vk::ClearValue clear;
|
vk::ClearValue clear;
|
||||||
clear.color = vk::ClearColorValue(
|
clear.color = vk::ClearColorValue(
|
||||||
std::array<float, 4>{0.1f, 0.1f, 0.1f, 1.0f});
|
std::array<float, 4>{cc.r, cc.g, cc.b, cc.a});
|
||||||
|
|
||||||
vk::RenderingAttachmentInfo color;
|
vk::RenderingAttachmentInfo color;
|
||||||
color.setImageView(ctx.View(m_HDRColor))
|
color.setImageView(ctx.View(m_HDRColor))
|
||||||
@@ -112,13 +113,13 @@ namespace Engine::Passes
|
|||||||
|
|
||||||
cmd.beginRendering(renderingInfo);
|
cmd.beginRendering(renderingInfo);
|
||||||
|
|
||||||
if (m_Env) {
|
if (m_Env && m_Frame.DrawSkybox) {
|
||||||
// 无环境贴图:只清屏(Forward loadOp=Load 依赖这里先写)
|
// 无环境贴图:只清屏(Forward loadOp=Load 依赖这里先写)
|
||||||
std::array<vk::ShaderStageFlagBits, 2> stages = {
|
const std::array<vk::ShaderStageFlagBits, 2> stages = {
|
||||||
vk::ShaderStageFlagBits::eVertex,
|
vk::ShaderStageFlagBits::eVertex,
|
||||||
vk::ShaderStageFlagBits::eFragment
|
vk::ShaderStageFlagBits::eFragment
|
||||||
};
|
};
|
||||||
std::array<vk::ShaderEXT, 2> shaders = {m_Vert, m_Frag};
|
const std::array<vk::ShaderEXT, 2> shaders = {m_Vert, m_Frag};
|
||||||
cmd.bindShadersEXT(stages, shaders);
|
cmd.bindShadersEXT(stages, shaders);
|
||||||
|
|
||||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||||
@@ -149,12 +150,12 @@ namespace Engine::Passes
|
|||||||
cmd.setVertexInputEXT({}, {});
|
cmd.setVertexInputEXT({}, {});
|
||||||
|
|
||||||
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
cmd.setRasterizationSamplesEXT(vk::SampleCountFlagBits::e1);
|
||||||
vk::SampleMask sampleMask = 0xFFFFFFFF;
|
constexpr vk::SampleMask sampleMask = 0xFFFFFFFF;
|
||||||
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
cmd.setSampleMaskEXT(vk::SampleCountFlagBits::e1, sampleMask);
|
||||||
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
cmd.setAlphaToCoverageEnableEXT(VK_FALSE);
|
||||||
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||||
|
|
||||||
vk::Viewport viewport(0.0f, 0.0f,
|
const vk::Viewport viewport(0.0f, 0.0f,
|
||||||
static_cast<float>(ctx.extent.width),
|
static_cast<float>(ctx.extent.width),
|
||||||
static_cast<float>(ctx.extent.height),
|
static_cast<float>(ctx.extent.height),
|
||||||
0.0f, 1.0f);
|
0.0f, 1.0f);
|
||||||
@@ -170,8 +171,8 @@ namespace Engine::Passes
|
|||||||
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
|
.setDstAlphaBlendFactor(vk::BlendFactor::eZero)
|
||||||
.setAlphaBlendOp(vk::BlendOp::eAdd);
|
.setAlphaBlendOp(vk::BlendOp::eAdd);
|
||||||
// ArrayProxyNoTemporaries 拒绝临时量:具名局部变量传入
|
// ArrayProxyNoTemporaries 拒绝临时量:具名局部变量传入
|
||||||
vk::Bool32 blendEnable = VK_FALSE;
|
constexpr vk::Bool32 blendEnable = VK_FALSE;
|
||||||
vk::ColorComponentFlags colorWriteMask =
|
constexpr vk::ColorComponentFlags colorWriteMask =
|
||||||
vk::ColorComponentFlagBits::eR |
|
vk::ColorComponentFlagBits::eR |
|
||||||
vk::ColorComponentFlagBits::eG |
|
vk::ColorComponentFlagBits::eG |
|
||||||
vk::ColorComponentFlagBits::eB |
|
vk::ColorComponentFlagBits::eB |
|
||||||
|
|||||||
@@ -215,6 +215,11 @@ namespace Engine
|
|||||||
return m_SceneRenderer->GetMainInstanceCount();
|
return m_SceneRenderer->GetMainInstanceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Renderer::GetInstancesTruncated() const
|
||||||
|
{
|
||||||
|
return m_SceneRenderer->Frame().instancesTruncated;
|
||||||
|
}
|
||||||
|
|
||||||
u32 Renderer::GetShadowInstanceCount() const
|
u32 Renderer::GetShadowInstanceCount() const
|
||||||
{
|
{
|
||||||
return m_SceneRenderer->GetShadowInstanceCount();
|
return m_SceneRenderer->GetShadowInstanceCount();
|
||||||
|
|||||||
@@ -166,9 +166,11 @@ namespace Engine
|
|||||||
ubo.proj[2][1] += jy * 2.0f / static_cast<f32>(targetExtent.height);
|
ubo.proj[2][1] += jy * 2.0f / static_cast<f32>(targetExtent.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
ubo.ambientColor = scene.GetSettings().ambientColor;
|
ubo.ambientColor = scene.GetSettings().AmbientColor;
|
||||||
|
|
||||||
// ---- 运行时开关/调试视图落帧(各 pass 与 frag 只读契约字段)----
|
// ---- 运行时开关/调试视图落帧(各 pass 与 frag 只读契约字段)----
|
||||||
|
m_Frame.ClearColor = scene.GetSettings().ClearColor;
|
||||||
|
m_Frame.DrawSkybox = scene.GetSettings().DrawSkybox;
|
||||||
m_Frame.shadowsEnabled = m_ShadowsEnabled;
|
m_Frame.shadowsEnabled = m_ShadowsEnabled;
|
||||||
m_Frame.taaEnabled = m_TAAEnabled;
|
m_Frame.taaEnabled = m_TAAEnabled;
|
||||||
m_Frame.bloomEnabled = m_BloomEnabled;
|
m_Frame.bloomEnabled = m_BloomEnabled;
|
||||||
@@ -363,11 +365,13 @@ namespace Engine
|
|||||||
m_LastProjNoJitter = ubo.projNoJitter;
|
m_LastProjNoJitter = ubo.projNoJitter;
|
||||||
++m_FrameIndex;
|
++m_FrameIndex;
|
||||||
|
|
||||||
|
// 截断状态每帧落遥测(ImGui 可见)——"只告警一次"的日志对
|
||||||
|
// 大场景不够:预算是动态穿越的,用户必须实时看到截断发生
|
||||||
|
m_Frame.instancesTruncated = instanceTruncated;
|
||||||
if (instanceTruncated && !m_TruncationWarned) {
|
if (instanceTruncated && !m_TruncationWarned) {
|
||||||
// 只告警一次(每帧告警会刷屏)
|
|
||||||
m_TruncationWarned = true;
|
m_TruncationWarned = true;
|
||||||
ENGINE_WARN("GPUDriven: instance buffer full ({}), entities truncated",
|
ENGINE_WARN("GPUDriven: instance buffer full ({}), entities truncated "
|
||||||
kMaxInstances);
|
"(见 Scene Debug 面板红字)", kMaxInstances);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Frame.collectMs = std::chrono::duration<f32, std::milli>(
|
m_Frame.collectMs = std::chrono::duration<f32, std::milli>(
|
||||||
|
|||||||
Reference in New Issue
Block a user