Files
HelloVulkan/Engine/src/Renderer/Renderer.cpp
T
atdunbg 708d714459 fix: 阴影采样器 CompareOp 反转(诊断残留 eGreater)+ 移除截图调试设施
根因:shadow sampler 停留在诊断期的 eGreater——比较语义全反,
全场 shade 被压到 0.3 变暗,唯独真影区 lit=1 反而全亮,
即"影子比环境亮、像光源"的现象。修回 eLess 后影子正常。

顺带清理:第 60 帧截图管线(HDRColor/ShadowMap0 → BMP)、
CPU 投影诊断日志、GetCaptureTarget 及 TRANSFER_SRC extraUsage。
2026-09-24 12:42:24 +08:00

442 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by atdunbg on 2026/9/20.
//
// Renderer:编排者。pass 实现在 Passes::*,场景理解在 SceneRenderer,
// 本文件只做装配、资源声明与每帧编排——新增渲染效果不需要改这里
// (除非要加新的 RG 资源或注册新 pass)。
//
#include <Engine/Renderer/Renderer.h>
#include <Engine/Renderer/SceneRenderer.h>
#include <Engine/Renderer/VulkanAssetUploader.h>
#include <Engine/Renderer/Passes/SceneGpu.h>
#include <Engine/Renderer/Passes/FullscreenGpu.h>
#include <Engine/Renderer/Passes/RenderPass.h>
#include <Engine/Renderer/Passes/SkyboxPass.h>
#include <Engine/Renderer/Passes/ShadowPass.h>
#include <Engine/Renderer/Passes/ForwardPass.h>
#include <Engine/Renderer/Cubemap.h>
#include <Engine/Renderer/Passes/TAAPass.h>
#include <Engine/Renderer/Passes/BloomPass.h>
#include <Engine/Renderer/Passes/TonemapPass.h>
#include <Engine/Renderer/Passes/TemporalDiffPass.h>
#include <Engine/Renderer/Passes/ImGuiPass.h>
#include <Engine/Renderer/Passes/PresentPass.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Vulkan/MemoryAllocator.h>
#include <Engine/Vulkan/DescriptorManager.h>
#include <Engine/Vulkan/ShaderManager.h>
#include <Engine/Vulkan/Swapchain.h>
#include <Engine/Vulkan/CommandManager.h>
#include <Engine/Vulkan/SyncManager.h>
#include <Engine/Vulkan/UniformBuffer.h>
#include <Engine/Core/Window.h>
#include <Engine/Core/Log.h>
#include <Engine/ECS/Scene.h>
#include <Engine/Assets/AssetManager.h>
#include <tracy/Tracy.hpp>
#include <glm/glm.hpp>
#include <array>
#include <filesystem>
#include <limits>
namespace Engine
{
Renderer::Renderer(Window& window)
: m_Window(window)
{
// 1. Device / Allocator / Descriptor / Shader
m_Device = CreateScope<Device>(window);
m_Allocator = CreateScope<MemoryAllocator>(*m_Device);
m_DescriptorManager = CreateScope<DescriptorManager>(*m_Device);
m_ShaderManager = CreateScope<ShaderManager>(*m_Device);
// 2. Per-frame ring:UBO + GPUDriven 实例 SSBO(当前/上一帧,
// 同一 ring 类,SSBO 只是 usage 不同;正确性保证一致)
m_UniformBuffer = CreateScope<UniformBuffer>(
*m_Device, *m_Allocator, sizeof(UniformBufferObject), kMaxFramesInFlight);
m_ObjectBuffer = CreateScope<UniformBuffer>(
*m_Device, *m_Allocator,
sizeof(glm::mat4) * kMaxInstances, kMaxFramesInFlight,
vk::BufferUsageFlagBits::eStorageBuffer);
m_PrevObjectBuffer = CreateScope<UniformBuffer>(
*m_Device, *m_Allocator,
sizeof(glm::mat4) * kMaxInstances, kMaxFramesInFlight,
vk::BufferUsageFlagBits::eStorageBuffer);
// 3. pass 装配体:布局/shader/采样器/per-slot 描述符
m_SceneGpu = CreateScope<Passes::SceneGpu>(
*m_Device, *m_DescriptorManager, *m_ShaderManager,
*m_UniformBuffer, *m_ObjectBuffer, *m_PrevObjectBuffer);
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));
}
// 4. Swapchain(只管呈现;渲染目标统一归 RenderGraph)
m_Swapchain = CreateScope<Swapchain>(*m_Device, *m_Allocator, m_Window);
m_Extent = m_Swapchain->GetExtent();
// 5. Sync / Command(per-frame)
m_SyncManager = CreateScope<SyncManager>(
*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
m_CommandManager = CreateScope<CommandManager>(*m_Device, kMaxFramesInFlight);
// 6. 场景翻译层(pass 经引用读取 FrameSceneData,必须先于 BuildGraph)
m_SceneRenderer = CreateScope<SceneRenderer>();
// 7. 资产上传服务(渲染层对外提供的 IAssetUploader 实现)
m_AssetUploader = CreateScope<VulkanAssetUploader>(
*m_Device, *m_Allocator, *m_DescriptorManager,
m_SceneGpu->textureSetLayout);
// 8. RenderGraph:资源声明 + pass 注册 + 首次编译
m_PostParams = CreateScope<Passes::PostProcessParams>();
BuildGraph();
// 首次编译后物理镜像就位:各 pass 绑定自己的输入描述符
for (auto& pass : m_Passes) {
pass->OnTargetsRebuilt(*m_RenderGraph);
}
// 环境辐照度绑定(view 全程稳定,绑一次)
m_SceneGpu->BindEnvironment(*m_Environment);
// 9. GPU timestamp:每 pass 两枚(前后),按帧槽位分区
m_QueriesPerFrame = static_cast<u32>(m_RenderGraph->GetPassCount()) * 2;
m_PassNames = m_RenderGraph->GetPassNames();
m_PassGpuMs.assign(m_RenderGraph->GetPassCount(), 0.0f);
vk::QueryPoolCreateInfo qpInfo;
qpInfo.setQueryType(vk::QueryType::eTimestamp)
.setQueryCount(m_QueriesPerFrame * kMaxFramesInFlight);
m_QueryPool = m_Device->GetDevice().createQueryPool(qpInfo);
m_RenderGraph->SetQueryPool(m_QueryPool, m_QueriesPerFrame);
m_TimestampPeriod = static_cast<f32>(
m_Device->GetPhysicalDevice().getProperties().limits.timestampPeriod);
ENGINE_INFO("Renderer initialized");
}
Renderer::~Renderer()
{
// 必须等 GPU 空闲再析构成员(pass/RG/装配体都会销毁 GPU 对象)
m_Device->GetDevice().waitIdle();
if (m_QueryPool) {
m_Device->GetDevice().destroyQueryPool(m_QueryPool);
}
}
vk::DescriptorSetLayout Renderer::GetTextureSetLayout() const
{
return m_SceneGpu->textureSetLayout;
}
vk::Format Renderer::GetSwapchainFormat() const
{
return m_Swapchain->GetFormat();
}
u32 Renderer::GetSwapchainImageCount() const
{
return m_Swapchain->GetImageCount();
}
Assets::IAssetUploader& Renderer::GetAssetUploader() const
{
return *m_AssetUploader;
}
f32 Renderer::GetExposure() const { return m_PostParams->exposure; }
void Renderer::SetExposure(f32 e) { m_PostParams->exposure = e; }
bool Renderer::GetUseACES() const { return m_PostParams->useACES; }
void Renderer::SetUseACES(bool b) { m_PostParams->useACES = b; }
f32 Renderer::GetBloomThreshold() const { return m_PostParams->bloomThreshold; }
void Renderer::SetBloomThreshold(f32 v) { m_PostParams->bloomThreshold = v; }
f32 Renderer::GetBloomStrength() const { return m_PostParams->bloomStrength; }
void Renderer::SetBloomStrength(f32 v) { m_PostParams->bloomStrength = v; }
bool Renderer::GetShowTemporalDiff() const { return m_PostParams->showTemporalDiff; }
void Renderer::SetShowTemporalDiff(bool b) { m_PostParams->showTemporalDiff = b; }
u32 Renderer::GetMainInstanceCount() const
{
return m_SceneRenderer->GetMainInstanceCount();
}
u32 Renderer::GetShadowInstanceCount() const
{
return m_SceneRenderer->GetShadowInstanceCount();
}
void Renderer::BuildGraph()
{
m_RenderGraph = CreateScope<RG::RenderGraph>(
*m_Device, *m_Allocator, kMaxFramesInFlight);
// ---- 资源声明(命名资源;pass 经句柄引用,数据流由 uses 决定)----
// 导入 swapchain 颜色附件
m_Backbuffer = m_RenderGraph->ImportSwapchain(
"Backbuffer", m_Swapchain->GetFormat());
// 深度图:目标尺寸,格式由 Device 能力决定
const vk::Format depthFormat = m_Device->FindSupportedFormat(
{vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint,
vk::Format::eD24UnormS8Uint},
vk::ImageTiling::eOptimal,
vk::FormatFeatureFlagBits::eDepthStencilAttachment);
m_Depth = m_RenderGraph->CreateTexture(
"MainDepth", RG::TextureDesc{depthFormat});
// CSM 级联阴影图:每级联独立 1024² 正交深度图(D32 采样),
// 范围由 SceneRenderer::ComputeCascades 按相机视锥切片拟合
for (u32 c = 0; c < kMaxCascades; ++c) {
m_ShadowMaps[c] = m_RenderGraph->CreateTexture(
"ShadowMap" + std::to_string(c),
RG::TextureDesc{vk::Format::eD32Sfloat, kCascadeRes, kCascadeRes});
}
// HDR 颜色图:Forward 的输出(线性光照值 + 抖动),TAA 显式完成
// 时域累积后写入 TAAColor(对齐 UE/Unity 管线)
m_HDRColor = m_RenderGraph->CreateTexture(
"HDRColor",
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat});
// velocity:clip 空间运动向量(TAA 反推历史像素位置)
m_Velocity = m_RenderGraph->CreateTexture(
"Velocity",
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat});
// TAA 累积输出:既是 resolve 结果也是下一帧的历史(自引用)
m_TAAColor = m_RenderGraph->CreateTexture(
"TAAColor",
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat});
// Bloom ping-pong:半分辨率(sizeScale 相对目标尺寸,resize 自动跟随)
m_BloomA = m_RenderGraph->CreateTexture(
"BloomA",
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat, 0, 0, 0.5f});
m_BloomB = m_RenderGraph->CreateTexture(
"BloomB",
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat, 0, 0, 0.5f});
// 帧历史差异(调试视图):|上一帧 − 当前帧| × 增益。
// 帧历史机制(ReadHistory)的验证消费端,也是 TAA 的参考形态
m_TemporalDiff = m_RenderGraph->CreateTexture(
"TemporalDiff",
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat, 0, 0, 0.5f});
// ---- pass 注册:执行序由拓扑排序决定,与注册顺序无关 ----
// 要加新 pass:new 一个 RenderPass 子类,在这里 push 一行。
FrameSceneData& frame = m_SceneRenderer->Frame();
m_Passes.push_back(CreateScope<Passes::ShadowPass>(
*m_SceneGpu, frame, m_ShadowMaps));
// Skybox = HDRColor 的 first writer(清屏 + 画背景),先于 Forward 注册
m_Passes.push_back(CreateScope<Passes::SkyboxPass>(
*m_Device, *m_ShaderManager, *m_DescriptorManager, frame,
m_HDRColor, m_Environment.get()));
m_Passes.push_back(CreateScope<Passes::ForwardPass>(
*m_SceneGpu, frame, m_HDRColor, m_Velocity, m_Depth, m_ShadowMaps));
m_Passes.push_back(CreateScope<Passes::TAAPass>(
*m_FullscreenGpu, /*historyAlpha*/ 0.9f, m_HDRColor, m_Velocity,
m_TAAColor));
m_Passes.push_back(CreateScope<Passes::TemporalDiffPass>(
*m_FullscreenGpu, /*gain*/ 8.0f, m_HDRColor, m_TemporalDiff));
m_Passes.push_back(CreateScope<Passes::BloomPass>(
*m_FullscreenGpu, *m_PostParams, m_TAAColor, m_BloomA, m_BloomB));
m_Passes.push_back(CreateScope<Passes::TonemapPass>(
*m_FullscreenGpu, *m_PostParams, m_TAAColor, m_BloomA,
m_TemporalDiff, m_Backbuffer));
m_Passes.push_back(CreateScope<Passes::ImGuiPass>(
&m_ImGuiLayer, m_Backbuffer));
m_Passes.push_back(CreateScope<Passes::PresentPass>(m_Backbuffer));
for (auto& pass : m_Passes) {
pass->Register(*m_RenderGraph);
}
m_RenderGraph->SetTargetExtent(m_Extent); // 触发首次 Compile
}
void Renderer::SetVSync(bool vsync)
{
if (m_Swapchain->GetVSync() == vsync) return;
m_Swapchain->SetVSync(vsync);
HandleResize(); // present mode 变化需要重建 swapchain
}
void Renderer::HandleResize()
{
m_Device->GetDevice().waitIdle();
m_Swapchain->Recreate(m_Window);
m_Extent = m_Swapchain->GetExtent();
// image 数量可能变化
m_SyncManager = CreateScope<SyncManager>(
*m_Device, kMaxFramesInFlight, m_Swapchain->GetImageCount());
// RG 重编译:物理镜像按新尺寸轮换,各 pass 重绑输入描述符
m_RenderGraph->SetTargetExtent(m_Extent);
for (auto& pass : m_Passes) {
pass->OnTargetsRebuilt(*m_RenderGraph);
}
}
void Renderer::ReadGpuTimes(u32 frameSlot)
{
if (!m_QueryPool || m_QueriesPerFrame == 0) return;
// 每槽位首帧:该区间的查询尚未被 reset/写入,跳过一次读取
if (!m_TimesPrimed[frameSlot]) {
m_TimesPrimed[frameSlot] = true;
return;
}
const u32 passCount = m_QueriesPerFrame / 2;
std::vector<u64> ticks(m_QueriesPerFrame, 0);
// fence 已等待本槽位上一轮完成,结果必然可用(阻塞式 + 64bit)
const vk::Result r = m_Device->GetDevice().getQueryPoolResults(
m_QueryPool,
frameSlot * m_QueriesPerFrame, m_QueriesPerFrame,
sizeof(u64) * m_QueriesPerFrame,
ticks.data(), sizeof(u64),
vk::QueryResultFlagBits::e64 | vk::QueryResultFlagBits::eWait);
if (r != vk::Result::eSuccess) return;
for (u32 i = 0; i < passCount; ++i) {
const u64 start = ticks[i * 2];
const u64 end = ticks[i * 2 + 1];
m_PassGpuMs[i] = (end >= start)
? static_cast<f32>(end - start) * m_TimestampPeriod / 1.0e6f
: 0.0f;
}
}
void Renderer::Render(ECS::Scene& scene, Assets::AssetManager& assets)
{
ZoneScoped; // CPU 侧:UBO/SSBO 上传 + 录制 + 提交(GPU 耗时见 RG timestamp)
if (m_Window.WasResized()) {
m_Window.ResetResizedFlag();
HandleResize();
}
const u32 frameSlot = m_CurrentFrame;
const auto& vkDevice = m_Device->GetDevice();
// 1. 场景 → 当帧渲染数据(相机/灯光/双视图剔除;纯 CPU,不碰 GPU)
if (!m_SceneRenderer->BuildFrame(scene, assets, m_Extent)) {
return; // 无 primary 相机:跳帧
}
const FrameSceneData& frame = m_SceneRenderer->Frame();
// 2. 等本槽位 fence → 写 UBO/SSBO → reset(写-读竞争关闭)
m_SyncManager->WaitFence(frameSlot);
ReadGpuTimes(frameSlot); // fence 已等:上一轮该槽位的查询已就绪
m_UniformBuffer->Update(frameSlot, &frame.ubo, sizeof(frame.ubo));
m_SyncManager->ResetFence(frameSlot);
if (!frame.instanceModels.empty()) {
m_ObjectBuffer->Update(frameSlot, frame.instanceModels.data(),
frame.instanceModels.size() * sizeof(glm::mat4));
// 上一帧姿态与当前帧平行对齐(velocity 顶点着色器按下标取)
m_PrevObjectBuffer->Update(frameSlot, frame.prevInstanceModels.data(),
frame.prevInstanceModels.size() *
sizeof(glm::mat4));
}
// 3. acquire swapchain image
u32 imageIndex = 0;
try {
const auto result = vkDevice.acquireNextImageKHR(
m_Swapchain->Get(),
std::numeric_limits<u64>::max(),
m_SyncManager->GetImageAvailable(frameSlot));
if (result.result == vk::Result::eErrorOutOfDateKHR) {
HandleResize();
return;
}
imageIndex = result.value;
}
catch (const vk::OutOfDateKHRError&) {
HandleResize();
return;
}
// 4. 录制:FrameContext + RenderGraph
FrameContext frameCtx;
frameCtx.frameSlot = frameSlot;
frameCtx.imageIndex = imageIndex;
frameCtx.cmd = m_CommandManager->BeginFrame(frameSlot);
m_RenderGraph->SetImportedImage(
m_Backbuffer,
m_Swapchain->GetImages()[imageIndex],
m_Swapchain->GetImageViews()[imageIndex]);
m_RenderGraph->Execute(frameCtx);
m_CommandManager->EndFrame(frameSlot);
// 5. 提交
std::array<vk::PipelineStageFlags, 1> waitStages = {
vk::PipelineStageFlagBits::eColorAttachmentOutput
};
std::array<vk::Semaphore, 1> waitSemaphores = {
m_SyncManager->GetImageAvailable(frameSlot)
};
std::array<vk::Semaphore, 1> signalSemaphores = {
m_SyncManager->GetRenderFinished(imageIndex)
};
vk::SubmitInfo submitInfo;
submitInfo.setCommandBuffers(frameCtx.cmd)
.setWaitSemaphores(waitSemaphores)
.setWaitDstStageMask(waitStages)
.setSignalSemaphores(signalSemaphores);
m_Device->GetGraphicsQueue().submit(
submitInfo, m_SyncManager->GetFrameFence(frameSlot));
// 6. 呈现
std::array<vk::SwapchainKHR, 1> swapchains = {m_Swapchain->Get()};
std::array<vk::Semaphore, 1> presentWait = {
m_SyncManager->GetRenderFinished(imageIndex)
};
vk::PresentInfoKHR presentInfo;
presentInfo.setImageIndices(imageIndex)
.setSwapchains(swapchains)
.setWaitSemaphores(presentWait);
try {
const auto result = m_Device->GetPresentQueue().presentKHR(presentInfo);
if (result == vk::Result::eSuboptimalKHR) {
HandleResize();
}
}
catch (const vk::OutOfDateKHRError&) {
HandleResize();
}
m_CurrentFrame = (m_CurrentFrame + 1) % kMaxFramesInFlight;
}
} // namespace Engine