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 调优的测量前置。
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// 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
|
||||
Reference in New Issue
Block a user