feat: IBL 预积分 GPU compute 化 + 环境资产入 Assets 层(动态切换)

CPU 逐像素卷积(venice 4k 阻塞 7.36s)→ 4 个 compute shader 一次性
提交:equirect→cube / irradiance / prefilter×5mip / BRDF LUT。
Cubemap 瘦身为纯资源持有类,生成职责移交 IblGenerator;
环境经 AssetManager 缓存(EnvRef),Renderer::SetEnvironment
动态换绑(RenderPass::OnEnvironmentChanged 广播)。
调试视图 +4:EnvCube / Irradiance / Prefilter / BRDF LUT。

性能前后对比(RTX 5060 Laptop,venice_dawn_1_4k.hdr):
- 改造前 CPU:7.36 s(主线程阻塞)
- 改造后 GPU:HDR decode 127-134ms + 资源 22-35ms + compute 17-19ms
  ≈ 165-190ms(约 40×);Neutral 兜底 ~6ms
- 运行中动态切换环境同量级(ms 级 GPU,decode 占大头)

冒烟:两构建目录 0 警告;运行期 0 validation(含两次环境切换 +
调试视图 9-12 轮播自动化验证)。
This commit is contained in:
2026-09-24 13:23:59 +08:00
parent 705250df76
commit 1d690067bd
30 changed files with 1162 additions and 824 deletions
+32 -18
View File
@@ -16,6 +16,7 @@
#include <Engine/Renderer/Passes/ShadowPass.h>
#include <Engine/Renderer/Passes/ForwardPass.h>
#include <Engine/Renderer/Cubemap.h>
#include <Engine/Renderer/IblGenerator.h>
#include <Engine/Renderer/Passes/TAAPass.h>
#include <Engine/Renderer/Passes/BloomPass.h>
#include <Engine/Renderer/Passes/TonemapPass.h>
@@ -40,7 +41,6 @@
#include <glm/glm.hpp>
#include <array>
#include <filesystem>
#include <limits>
namespace Engine
@@ -74,17 +74,12 @@ namespace Engine
m_FullscreenGpu = CreateScope<Passes::FullscreenGpu>(
*m_Device, *m_DescriptorManager, *m_ShaderManager);
// 3.5 环境贴图(IBL):HDR 等距柱状 → cube + 预积分。
// 加载失败/文件缺失一律中性常数兜底(描述符恒有效)
const std::string envPath = "assets/textures/venice_dawn_1_4k.hdr";
if (std::filesystem::exists(envPath)) {
m_Environment = Cubemap::CreateFromHdrEquirect(
*m_Device, *m_Allocator, envPath, 1024);
}
if (!m_Environment) {
m_Environment = Cubemap::CreateNeutral(
*m_Device, *m_Allocator, glm::vec3(0.1f));
}
// 3.5 环境预积分生成端(compute 链)+ 中性兜底四件套。
// 真实环境由 Application 经 AssetManager 加载后 SetEnvironment
// (资产路径归资产层,Renderer 不感知具体文件)
m_Ibl = CreateScope<IblGenerator>(
*m_Device, *m_Allocator, *m_DescriptorManager, *m_ShaderManager);
m_Environment = m_Ibl->Neutral(glm::vec3(0.1f));
// 4. Swapchain(只管呈现;渲染目标统一归 RenderGraph)
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, m_Window);
@@ -101,7 +96,7 @@ namespace Engine
// 7. 资产上传服务(渲染层对外提供的 IAssetUploader 实现)
m_AssetUploader = CreateScope<VulkanAssetUploader>(
*m_Device, *m_Allocator, *m_DescriptorManager,
m_SceneGpu->textureSetLayout);
m_SceneGpu->textureSetLayout, *m_Ibl);
// 8. RenderGraph:资源声明 + pass 注册 + 首次编译
m_PostParams = CreateScope<Passes::PostProcessParams>();
@@ -111,8 +106,12 @@ namespace Engine
for (auto& pass : m_Passes) {
pass->OnTargetsRebuilt(*m_RenderGraph);
}
// 环境辐照度绑定(view 全程稳定,绑一次)
// 环境辐照度绑定(view 在单个环境内全程稳定,绑一次)
m_SceneGpu->BindEnvironment(*m_Environment);
// 环境消费端 pass(Skybox 背景 / Tonemap 调试视图)同步初始绑定
for (auto& pass : m_Passes) {
pass->OnEnvironmentChanged(m_Environment.get());
}
// 9. GPU timestamp:每 pass 两枚(前后),按帧槽位分区
m_QueriesPerFrame = static_cast<u32>(m_RenderGraph->GetPassCount()) * 2;
@@ -185,13 +184,28 @@ namespace Engine
int Renderer::GetDebugView() const { return m_SceneRenderer->Frame().debugView; }
void Renderer::SetDebugView(int v) { m_SceneRenderer->SetDebugView(v); }
void Renderer::SetEnvironment(const Cubemap* env)
{
if (!env) env = m_Environment.get();
// 描述符 host 更新要求 set 不被 in-flight 帧使用(validation 严格
// 校验):waitIdle 两槽位全停飞后统一换绑。环境切换低频,等待
// 成本 ≈ 单帧 GPU 尾巴;生成端(compute 预积分)已在资产加载期完成
m_Device->GetDevice().waitIdle();
m_SceneGpu->BindEnvironment(*env);
for (auto& pass : m_Passes) {
pass->OnEnvironmentChanged(env);
}
}
const std::vector<const char*>& Renderer::GetDebugViewNames() const
{
static const std::vector<const char*> kNames = {
"Final", "TemporalDiff", "HDR", "TAAColor", "Bloom",
"Velocity", "ShadowMap0", "ShadowMap1", "ShadowMap2",
"EnvCube", "Irradiance", "Prefilter", "BrdfLut",
};
static_assert(int(Passes::kDebugViewCount) == 9,
static_assert(int(Passes::kDebugViewCount) == 13,
"DebugView 枚举与名表同步");
return kNames;
}
@@ -285,9 +299,9 @@ namespace Engine
*m_FullscreenGpu, *m_PostParams, frame, m_TAAColor, m_BloomA,
m_BloomB));
m_Passes.push_back(CreateScope<Passes::TonemapPass>(
*m_FullscreenGpu, *m_PostParams, frame, m_TAAColor, m_BloomA,
m_TemporalDiff, m_Backbuffer, m_HDRColor, m_Velocity,
m_ShadowMaps[0], m_ShadowMaps[1], m_ShadowMaps[2]));
*m_FullscreenGpu, *m_DescriptorManager, *m_PostParams, frame,
m_TAAColor, m_BloomA, m_TemporalDiff, m_Backbuffer, m_HDRColor,
m_Velocity, m_ShadowMaps[0], m_ShadowMaps[1], m_ShadowMaps[2]));
m_Passes.push_back(CreateScope<Passes::ImGuiPass>(
&m_ImGuiLayer, m_Backbuffer));
m_Passes.push_back(CreateScope<Passes::PresentPass>(m_Backbuffer));