- vendored tracy 子模块;TRACY_ENABLE 全局生效,宏关闭时零开销 - 插桩:Application::Run FrameMark(帧边界)、Renderer::Render、 SceneRenderer::BuildFrame(ZoneScopedN)+ GPUDriven Collect (ZoneNamedN——同作用域第二个 zone 不能用 ZoneScopedN,变量名 固定会重声明)、Scene::UpdateTransforms - 覆盖 CPU 侧关键路径:帧时间线 / 场景收集 / transform 重算 / 录制提交 - 用法:游戏运行时打开 Tracy GUI(release 下载)即自动连接(8086 端口)
421 lines
17 KiB
C++
421 lines
17 KiB
C++
//
|
||
// 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/ShadowPass.h>
|
||
#include <Engine/Renderer/Passes/ForwardPass.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 <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);
|
||
|
||
// 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);
|
||
}
|
||
|
||
// 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));
|
||
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
|