Profiling Deep Dive — Insights, RDG & Hardware Profilers性能分析深度解析 — Insights、RDG 与硬件 Profiler
Profiling Workflow — Layered Approach性能分析工作流 — 分层方法
Effective profiling in UE requires a layered approach: start with high-level UE tools to identify which system is slow, then drill down into hardware-level profilers to understand why it's slow. The tools form a hierarchy from most accessible to most detailed:
UE 中有效的性能分析需要分层方法:从高级 UE 工具开始确定哪个系统慢,然后深入到硬件级 profiler 了解为什么慢。工具形成从最易访问到最详细的层次结构:
Unreal Insights — CPU Thread AnalysisUnreal Insights — CPU 线程分析
Unreal Insights is UE's primary CPU profiling tool. It captures trace data from the running application and displays CPU thread timelines with nanosecond precision. The key instrument for rendering analysis:
Unreal Insights 是 UE 的主要 CPU 性能分析工具。它从运行中的应用程序捕获追踪数据,并以纳秒精度显示 CPU 线程时间线。渲染分析的关键工具:
- Game Thread view: Find per-tick bottlenecks (physics, Blueprint, animation). Look for long
UWorld::Tickspans. - Game Thread 视图:查找每帧瓶颈(物理、蓝图、动画)。寻找长
UWorld::Tick跨度。 - Render Thread view: Find slow
FDeferredShadingSceneRenderer::Renderspans. LongInitViews= CPU visibility bottleneck. LongRenderDeferredLighting= many lights. - Render Thread 视图:查找慢的
FDeferredShadingSceneRenderer::Render跨度。长InitViews= CPU 可见性瓶颈。长RenderDeferredLighting= 大量灯光。 - RHI Thread view: If the RT finishes early but the RHI is slow, you have an RHI bottleneck (too many API calls, resource binding overhead).
- RHI Thread 视图:如果 RT 早早完成但 RHI 慢,则存在 RHI 瓶颈(过多 API 调用、资源绑定开销)。
- Frame interlock pattern: A healthy frame shows GT and RT running in near-perfect lockstep with ~1 frame of offset. If RT latency exceeds GT frame time, frames start stacking.
- 帧互锁模式:健康帧显示 GT 和 RT 以近乎完美的节拍运行,偏移约 1 帧。如果 RT 延迟超过 GT 帧时间,帧开始堆叠。
// To enable Unreal Insights capture from the application itself: // Launch with -trace=cpu,gpu,frame,log,bookmark,counters -tracehost=127.0.0.1 // Or from editor: Window → Unreal Insights → Live session // Add custom trace events to your code: TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("FMySystem::Update")); // or with channel filtering: TRACE_CPUPROFILER_EVENT_SCOPE_ON_CHANNEL_STR("MyFeature", MyFeatureChannel, true);
GPU Visualizer — Per-Pass GPU TimingGPU 可视化工具 — 每通道 GPU 计时
The GPU Visualizer (ProfileGPU command, or Ctrl+Shift+, in editor) shows the GPU time consumed by each render pass. This is UE's equivalent of a coarse GPU profiler — it uses hardware timer queries to measure pass durations. It's the fastest way to identify which render pass is most expensive.
GPU 可视化工具(ProfileGPU 命令,或编辑器中的 Ctrl+Shift+,)显示每个渲染通道消耗的 GPU 时间。这是 UE 的粗粒度 GPU profiler 等价物——使用硬件计时器查询来测量通道持续时间。这是识别哪个渲染通道最昂贵的最快方法。
// Useful console commands for GPU timing: ProfileGPU // capture one frame, show GPU pass tree stat GPU // continuous overlay showing per-category GPU time r.GPUStatsEnabled 1 // enable fine-grained GPU stats collection r.RDG.Stats 1 // print RDG pass stats (pass count, cull count, merge count)
RDG Insights — Graph-Level AnalysisRDG Insights — 图级别分析
RDG Insights (available in Unreal Insights) shows the RDG dependency graph visualization: which passes were culled, which were merged, and where async compute passes overlapped with graphics. Use it to validate that your optimization hypotheses are correct:
RDG Insights(在 Unreal Insights 中可用)显示 RDG 依赖图可视化:哪些通道被剔除,哪些被合并,以及异步计算通道在哪里与图形重叠。用它验证你的优化假设是否正确:
- If a pass shows as culled when it shouldn't be: you're missing a
NeverCullflag or the output resource isn't extracted. - 如果通道显示为已剔除但不应该:缺少
NeverCull标志,或输出资源未被提取。 - If two passes you expect to merge don't: one of them has
NeverMerge, writes to a UAV inside the render pass, or has different render targets. - 如果你期望合并的两个通道没有合并:其中一个有
NeverMerge,在渲染通道内写入 UAV,或具有不同的渲染目标。 - If an async compute pass isn't overlapping: check if the platform supports efficient async compute (
GSupportsEfficientAsyncCompute) and that the pass doesn't have a direct dependency on the previous graphics pass. - 如果异步计算通道没有重叠:检查平台是否支持高效异步计算(
GSupportsEfficientAsyncCompute),以及通道是否与前一个图形通道有直接依赖。
stat Commands — Quick In-Game Profilingstat 命令 — 游戏内快速性能分析
| Command命令 | What it shows显示内容 |
|---|---|
| stat FPS | Frame time + FPS overlay帧时间 + FPS 叠加 |
| stat Unit | GT / RT / GPU / Draw breakdown per frame每帧 GT / RT / GPU / Draw 分解 |
| stat GPU | Per-category GPU time (base pass, shadows, post-process, etc.)每类别 GPU 时间(基础通道、阴影、后处理等) |
| stat RHI | Draw primitives count, RHI memory, triangle countDraw Primitives 数量、RHI 内存、三角形数量 |
| stat SceneRendering | Detailed render thread timing: InitViews, BasePass, Lighting, etc.详细渲染线程计时:InitViews、BasePass、光照等 |
| stat Nanite | Nanite cluster/triangle counts, visibility query statsNanite Cluster/三角形数量、可见性查询统计 |
| stat VirtualShadowMaps | VSM page counts, cache hit rate, physical pool usageVSM 页面数量、缓存命中率、物理池使用量 |
| stat Lumen | Lumen surface cache update counts, trace budgetsLumen 表面缓存更新数量、追踪预算 |
RenderDoc — Draw Call InspectionRenderDoc — 绘制调用检查
RenderDoc is the go-to tool for GPU frame debugging. In UE, trigger a capture via:
RenderDoc 是 GPU 帧调试的首选工具。在 UE 中,通过以下方式触发捕获:
// Method 1: Console command (in any build) r.CaptureNextDeferredShadingRendererFrame 1 // capture next frame // Method 2: RenderDoc overlay (attach to process or launch with -renderdoc) // Method 3: Programmatic capture in code (non-shipping builds) #include "RenderCaptureInterface.h" RenderCaptureInterface::FScopedCapture Capture(true, GraphBuilder, TEXT("MyCapture"));
RenderDoc with UE shows the full RDG event hierarchy because UE calls IRHICommanList::PushEvent/PopEvent for every RDG_EVENT_NAME() scope. Use the event list to navigate to the exact draw call you're interested in, then inspect: input resources, output resources, shader bytecode, constant buffer values, and pipeline state.
带 UE 的 RenderDoc 显示完整的 RDG 事件层级,因为 UE 为每个 RDG_EVENT_NAME() 范围调用 IRHICommanList::PushEvent/PopEvent。使用事件列表导航到你感兴趣的精确绘制调用,然后检查:输入资源、输出资源、着色器字节码、常量缓冲值和管线状态。
PIX (Windows / Xbox) — Timeline + Shader AnalysisPIX(Windows / Xbox)— 时间线 + 着色器分析
PIX for Windows (and Xbox) offers several modes that go beyond RenderDoc: GPU Capture (similar to RenderDoc), Timing Capture (GPU timeline with hardware counters per pass), and Function-Level Profiling (D3D12 command queue visualization). For UE specifically, PIX's Timing Capture is the most useful for identifying GPU bubbles — gaps in the Graphics or Compute queues where the GPU is idle waiting for a fence or sync point.
Windows/Xbox 的 PIX 提供了超越 RenderDoc 的几种模式:GPU 捕获(类似 RenderDoc)、计时捕获(每通道带硬件计数器的 GPU 时间线)和函数级分析(D3D12 命令队列可视化)。特别是对于 UE,PIX 的计时捕获对于识别 GPU 气泡最有用——图形或计算队列中 GPU 空闲等待 fence 或同步点的间隙。
NSight (NVIDIA) — Shader-Level AnalysisNSight(NVIDIA)— 着色器级别分析
NVIDIA NSight Graphics and NSight Systems together provide the deepest analysis on NVIDIA GPUs. NSight Graphics captures GPU frames like RenderDoc but adds hardware performance counter data: L1/L2 cache hit rates, DRAM bandwidth utilization, warp occupancy, texture fetch efficiency, and more. NSight Systems shows the full CPU+GPU timeline with CUDA/DX12/Vulkan overlaid, ideal for identifying async compute overlap quality.
NVIDIA NSight Graphics 和 NSight Systems 共同提供了 NVIDIA GPU 上最深入的分析。NSight Graphics 像 RenderDoc 一样捕获 GPU 帧,但增加了硬件性能计数器数据:L1/L2 缓存命中率、DRAM 带宽利用率、Warp 占用率、纹理获取效率等。NSight Systems 显示完整的 CPU+GPU 时间线,叠加 CUDA/DX12/Vulkan,非常适合识别异步计算重叠质量。
Key NSight metrics for UE rendering:
UE 渲染的关键 NSight 指标:
| Metric指标 | Healthy range健康范围 | What it means if bad不健康时意味着 |
|---|---|---|
| SM Active Cycles | >80% | GPU underutilized — too few draw calls or small dispatchesGPU 利用率不足——绘制调用太少或分发太小 |
| DRAM Bandwidth | <80% peak | Memory bandwidth bottleneck — GBuffer too large, too many texture reads内存带宽瓶颈——GBuffer 太大,纹理读取太多 |
| Warp Stall (Texture) | <20% | Shader spends too much time waiting for texture fetches (cache miss)着色器等待纹理读取的时间太多(缓存未命中) |
| L2 Hit Rate | >70% | Low = poor texture cache reuse, possibly random access patterns低 = 纹理缓存复用差,可能是随机访问模式 |
Snapdragon Profiler (Mobile) — Tile-Based BandwidthSnapdragon Profiler(移动端)— 基于瓦片的带宽
On Qualcomm Adreno devices, Snapdragon Profiler is essential for understanding TBDR-specific performance. Key metrics unique to mobile:
在高通 Adreno 设备上,Snapdragon Profiler 对于理解 TBDR 特定性能至关重要。移动端特有的关键指标:
- Tile writes to DRAM: Each tile write is a bandwidth cost. UE's
NextSubpass()/ESubpassHintreduce these — verify they're being used in the frame capture. - 瓦片写入 DRAM:每次瓦片写入都有带宽成本。UE 的
NextSubpass()/ESubpassHint减少这些——在帧捕获中验证它们是否被使用。 - Overdraw: High overdraw means many pixels are shaded multiple times. Use
r.ShaderComplexityto visualize it in-engine first. - 过度绘制:高过度绘制意味着许多像素被多次着色。首先在引擎中使用
r.ShaderComplexity可视化它。 - BinningTime: The time the GPU spends in the binning phase (tiling geometry to tiles). If high, reduce triangle count or draw call count.
- BinningTime:GPU 在分 Bin 阶段(将几何体分配到瓦片)花费的时间。如果高,减少三角形数量或绘制调用数量。
Bandwidth & VRAM Analysis带宽与 VRAM 分析
Bandwidth problems manifest as GPU utilization that's high but GPU speed (ALU throughput) is low — the shader is spending most of its time waiting for memory. Key strategies:
带宽问题表现为 GPU 利用率高但 GPU 速度(ALU 吞吐量)低——着色器大部分时间在等待内存。关键策略:
- Reduce GBuffer resolution: Use temporal upscaling (TSR) to render at lower resolution. Each 50% resolution reduction = 75% GBuffer bandwidth reduction.
- 降低 GBuffer 分辨率:使用时间超分辨率(TSR)以较低分辨率渲染。每降低 50% 分辨率 = GBuffer 带宽减少 75%。
- Reduce VRAM usage: Use
r.Streaming.PoolSizeto control texture pool, enable texture streaming to prevent VRAM overflow. Monitor withstat RHI→ TextureMemory. - 减少 VRAM 使用:使用
r.Streaming.PoolSize控制纹理池,启用纹理流送防止 VRAM 溢出。通过stat RHI→ TextureMemory 监控。 - Transient resource aliasing: RDG's transient heap allocator automatically reuses VRAM between non-overlapping passes. Enable with
r.RDG.TransientAllocator=1and confirm it's active via RDG Insights. - 瞬态资源别名:RDG 的瞬态堆分配器自动在不重叠的通道之间重用 VRAM。使用
r.RDG.TransientAllocator=1启用,并通过 RDG Insights 确认其活跃。
Custom Profiling Instrumentation自定义性能分析插桩
// Add CPU trace events (visible in Unreal Insights): TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("FMyRenderer::DoExpensiveWork")); // Add GPU stat scopes (visible in ProfileGPU / Insights GPU trace): DECLARE_GPU_STAT(MyCustomPass); RDG_GPU_STAT_SCOPE(GraphBuilder, MyCustomPass); // in RDG pass setup // CSV profiling (useful for automated regression testing): CSV_SCOPED_TIMING_STAT(MyCsvCategory, MyExpensiveOperation); // Add named events visible in RenderDoc / PIX: RDG_EVENT_SCOPE(GraphBuilder, "MySystem::ComputePass (%d instances)", InstanceCount); // ↑ These appear as GPU event groups in RenderDoc's event list // Performance counters for continuous monitoring: SET_DWORD_STAT(STAT_MyCustomTriangleCount, TriCount); INC_DWORD_STAT(STAT_MyCustomDrawCalls);