Files
HelloVulkan/Engine/src/Renderer/TracyGpuProfiler.cpp
T
atdunbg 3bfe28b027 feat: Tracy GPU 时间线——RG pass 级 GPU zone
RenderGraph 新增 IGpuProfiler 可选注入(pass 边界 Begin/End 钩子 +
帧尾 Collect,zone 覆盖 barrier+lambda+postBarriers),RG 本体不感知
任何 profiler 实现。Renderer 侧 TracyGpuProfiler 实现:TracyVkContext
(校准用临时 cb,池必须带 RESET_COMMAND_BUFFER_BIT——tracy 校准流程
对同一 cb 连续 begin 三次)+ TracyVkZoneTransient(pass 名是运行时
字符串)。TRACY_ENABLE 关闭全链路退化为空。

用法:Tracy GUI(0.14.1 配对)连接后按 pass 名看 GPU 段时间线,
与 ImGui 数表互补;LOD 调优的测量前置。
2026-09-24 19:45:17 +08:00

100 lines
3.2 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/24.
//
#include <Engine/Renderer/TracyGpuProfiler.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Core/Log.h>
#include <tracy/TracyVulkan.hpp>
#include <cstring>
namespace Engine {
// pass scope:构造 = BEGIN 时间戳,析构 = END(覆盖 pass lambda 录制期)
class TracyGpuProfiler::ScopeImpl final : public RG::RenderGraph::IGpuProfiler::IScope {
public:
#ifdef TRACY_ENABLE
ScopeImpl(TracyVkCtx ctx, const char* name, vk::CommandBuffer cmd)
: m_Zone(ctx, static_cast<uint32_t>(__LINE__), __FILE__,
sizeof(__FILE__) - 1, __FUNCTION__, sizeof(__FUNCTION__) - 1,
name, std::strlen(name),
static_cast<VkCommandBuffer>(cmd), true) {}
tracy::VkCtxScope m_Zone;
#else
ScopeImpl(TracyVkCtx, const char*, vk::CommandBuffer) {}
#endif
};
TracyGpuProfiler::TracyGpuProfiler(Device& device)
{
#ifdef TRACY_ENABLE
// 校准用临时 command buffer:tracy 校准流程对同一 cb 连续
// begin 三次(无中间 reset)——池必须带 RESET_COMMAND_BUFFER_BIT,
// 否则 VUID-vkBeginCommandBuffer-commandBuffer-00050
vk::CommandPoolCreateInfo poolInfo;
poolInfo.setQueueFamilyIndex(device.GetGraphicsFamily())
.setFlags(vk::CommandPoolCreateFlagBits::eTransient |
vk::CommandPoolCreateFlagBits::eResetCommandBuffer);
vk::CommandPool pool =
device.GetDevice().createCommandPool(poolInfo);
vk::CommandBuffer cb = device.GetDevice().allocateCommandBuffers(
vk::CommandBufferAllocateInfo{}
.setCommandPool(pool)
.setLevel(vk::CommandBufferLevel::ePrimary)
.setCommandBufferCount(1))[0];
m_Ctx = TracyVkContext(
static_cast<VkPhysicalDevice>(device.GetPhysicalDevice()),
static_cast<VkDevice>(device.GetDevice()),
static_cast<VkQueue>(device.GetGraphicsQueue()),
static_cast<VkCommandBuffer>(cb));
device.GetDevice().freeCommandBuffers(pool, cb);
device.GetDevice().destroyCommandPool(pool);
if (m_Ctx) {
ENGINE_INFO("Tracy GPU profiler attached (pass zones active)");
} else {
ENGINE_WARN("Tracy GPU profiler: context creation failed, disabled");
}
#endif
}
TracyGpuProfiler::~TracyGpuProfiler()
{
#ifdef TRACY_ENABLE
if (m_Ctx) {
TracyVkDestroy(static_cast<TracyVkCtx>(m_Ctx));
}
#endif
}
Scope<TracyGpuProfiler::IScope> TracyGpuProfiler::BeginPass(
const char* name, vk::CommandBuffer cmd)
{
#ifdef TRACY_ENABLE
if (!m_Ctx) return nullptr;
return Scope<IScope>(new ScopeImpl(
static_cast<TracyVkCtx>(m_Ctx), name, cmd));
#else
(void)name;
(void)cmd;
return nullptr;
#endif
}
void TracyGpuProfiler::EndFrame(vk::CommandBuffer cmd)
{
#ifdef TRACY_ENABLE
if (m_Ctx) {
TracyVkCollect(static_cast<TracyVkCtx>(m_Ctx),
static_cast<VkCommandBuffer>(cmd));
}
#else
(void)cmd;
#endif
}
} // namespace Engine