FRHIDrawStatsCategory stopped providing stats in UE 5.6

Hello,

We’re using FRHIDrawStatsCategory in order to get the number of drawcalls on the different categories. But since 5.6 its NumCategory member is always zero.

By looking at the code I found the new macro RHI_NEW_GPU_PROFILER is used, which removes a lot of the previous GPU stats code and now nothing is feeding information into FRHIDrawStatsCategory.

Is there an alternative way with the new profiler code to get the list of drawcalls per category?

NOTE: As workaround I’m currently using GNumDrawCallsRHI to get the total number of drawcalls, but I’d like to know the categories as well so I can report where the drawcalls are coming from.

Thanks!

Hi Ross,

Sorry for the delay on this. You’re right, the draw call stats are broken in 5.6 onwards. It looks like maybe an oversight from when we enabled the new GPU profiler. I’ve created bug UE-350063 to track this.

It looks like we’re missing the calls to RHICmdList.SetDrawStatsCategory(), which used to happen inside the FScopedDrawStatCategory type, used by the stat macros.

I’m not entirely sure when we’ll be able to fix this, so you might want to try implementing it yourself if you really need that data, or possibly disable the new GPU profiler by defining RHI_NEW_GPU_PROFILER to zero in your .Target.cs file for your project to fall back to the old system.

Cheers,

Luke

Hi Ross,

I forgot to mention in my previous reply that the updated “profilegpu” command in UE 5.6 does include a column for draw calls and dispatches, which is broken down per RHI breadcrumb, and per GPU pipeline. This might give you the info you need, since it’ll show you how many draws and dispatches are happening in each RDG pass, although it is a one-frame snapshot, rather than a live stat display. Run “profilegpu” in the in-editor or in-game command console, and inspect the debug output. Note, the editor will still open the legacy profileGPU UI, but this dialog does not show the draw counts. That data is only shown in the text table output. You’ll see this text in the editor “Output Log” pane, or if you have Visual Studio attached, in the debugger output window. There are a collection of console variables at the top of GPUProfiler.cpp starting “r.ProfileGPU.” which can format and filter the table if there’s a specific RDG pass you want to focus on. If you find you aren’t using the legacy editor UI, it can be disabled via “r.ProfileGPU.ShowUI 0”.

I’d also caveat this by saying that, while the draw/dispatch count should be correct, the “num primitives / vertices” counts can only consider non-indirect draws etc, since the final vertex and primitive counts for indirect draws are only known on the GPU, and we don’t have a mechanism to read this back for stats purposes. Nanite geometry is an example of a system that uses indirect draws, which means the primitive counts won’t be accurate.

Unfortunately the GNumDrawCallsRHI has no context as to what is or is not “editor” draws, since the entire editor is itself drawn with the RHI. If you really do need a high level draw count stat, perhaps running the editor in “-game” mode so only the game viewport is spawn will help.

If there is some custom draw call stats you want to implement, you might be able to achieve this by modifying the new GPU profiler code in GPUProfiler.h and .cpp. The new profiler works on a stream of events that are posted by the platform RHI each frame. That event stream includes UE::RHI::GPUProfiler::FEvent::FStats events, which have NumDraws and NumDispatches fields that drive the corresponding columns in the “profileGPU” table. Whenever these events appear in the stream, their counts should be accumulated against whichever RHI breadcrumb is current at the top of the stack for that pipeline (as determined by the FBeginBreadcrumb and FEndBreadcrumb events). Maybe some custom code in the event processing loop in FGPUProfilerSink_StatSystem::ProcessQueue could look for specific breadcrumbs you care about, and accumulate the stat values for display in whatever way you need. Portions of this processing loop run in shipping builds to drive the “stat unit” GPU time, so be careful with perf if you’re adding code here.

Cheers,

Luke

Hi Ross.

Just a quick update on this… I’ve implemented the “stat DrawCount” stats with the new profiler in //UE5/Main, and it is currently pending a code review. I’ll let you know when this gets checked in. You might be able to merge the change, it was relatively self-contained.

Cheers, Luke

This is now submitted to //UE5/Main in CL 48083628.

Cheers,

Luke

Thanks Ross. Fwiw it will be UE 5.8 when that change is officially shipped. We can’t put it in a 5.7 hotfix as it doesn’t meet our hotfix rules.

Cheers,

Luke

Hi Luke, thanks for your reply.

I gave it a shot at fixing this and made it as far as “register” the categories, but not to actually fill the data to them. The registration of the drawcalls categories is done with DECLARE_GPU_DRAWCALL_STAT_NAMED and DECLARE_GPU_DRAWCALL_STAT_NAMED_EXTERN macros, which with New GPU Profiles uses the TGPUStatWithDrawcallCategory class. That class declares the draw category like this: “FRHIDrawStatsCategory DrawcallCategory;” but it doesn’t pass the name to it, so it’s not used. By changing in CPUProfiler.h inside TGPUStatWithDrawcallCategory class, the member from “FRHIDrawStatsCategory DrawcallCategory;” to “FRHIDrawStatsCategory DrawcallCategory = FRHIDrawStatsCategory(TNameProvider::GetStatName());” the DrawcallCategory will be correctly initialized with a name and added to the Global Manager (FRHIDrawStatsCategory::FManager).

That’s all good, now the list of drawcalls categories (NumCategory) is not zero, but then the macros that actually add stats to those drawcalls categories are not defined with the New GPU Profiler:

```

// Empty when using the new RHI GPU profiler. GPU stats are handled via RHI_BREADCRUMB_EVENT_STAT etc.

// @todo deprecate

#define SCOPED_GPU_STAT_VERBOSE(…)

#define SCOPED_GPU_STAT(…)

#define GPU_STATS_BEGINFRAME(…)

#define GPU_STATS_ENDFRAME(…)

#define GPU_STATS_SUSPENDFRAME(…)

```

And the RHI_BREADCRUMB_EVENT_STAT macros do not fill the info to the drawcalls categories. I stopped at this point as I didn’t see a simple way to fill the data. But I hope this helps to you or others. If you know what changes are necesary to RHI_BREADCRUMB_EVENT_STAT to make this work, please let me know.

> possibly disable the new GPU profiler by defining RHI_NEW_GPU_PROFILER to zero in your .Target.cs

We aim to take full advantage of the latest tools available to us, so disabling the New GPU Profiler is not a viable option.

There is one thing though that would be really useful for us, if you can tell me a way to achieve it. As I mentioned, we are currently using GNumDrawCallsRHI to get the total number of drawcalls, but unfortunately that number includes all the drawcalls to render the actual Editor (menus, buttons, etc). That’s what is more disrupting for us, because we want to know the number of drawcalls of the viewport, not the editor. If there was a change you can provide to not include Editor drawcalls in GNumDrawCallsRHI that’d be good enough for now.

Thanks!

> …although it is a one-frame snapshot, rather than a live stat display…

Unfortuntely one-frame snapshot is not what we’re after, we need an updated drawcall count everyframe to show on screen running at runtime with performance unaffected (as much as possible) by profiling hooks & processing.

Hopefully you guys can feed the FRHIDrawStatsCategory again soon in a future change.

> Unfortunately the GNumDrawCallsRHI has no context as to what is or is not “editor” draws, since the entire editor is itself drawn with the RHI. If you really do need a high level draw count stat, perhaps running the editor in “-game” mode so only the game viewport is spawn will help.

Thanks for the clarification, we’ll just have to work around that then.

> I’d also caveat this by saying that, while the draw/dispatch count should be correct, the “num primitives / vertices” counts can only consider non-indirect draws etc, since the final vertex and primitive counts for indirect draws are only known on the GPU, and we don’t have a mechanism to read this back for stats purposes. Nanite geometry is an example of a system that uses indirect draws, which means the primitive counts won’t be accurate.

That’s a very good point, thanks for bringing it up. We don’t use Nanite geometry, so our usage of indirect drawcalls is limited.

Thanks!

Thanks Luke, I’ll have a look soon.

Hey Luke, I had a look at the P4 changes you did and they look really good. At the moment we’d decided it’s too large of a change to grab indiviadually, so we’ll continue without drawcall categorisation until we naturally get that change from the normal course of updating engine versions (that being UE 5.7 or 5.8). I really appreciate the efford getting the stats codebase up to date with the new GPU profiler. Thanks!