Skip to content

Make the Tracy layer optional, even when compiled with feature = "tracing-tracy"#24109

Open
nfagerlund wants to merge 3 commits intobevyengine:mainfrom
nfagerlund:nf/may26-optional-tracy-client
Open

Make the Tracy layer optional, even when compiled with feature = "tracing-tracy"#24109
nfagerlund wants to merge 3 commits intobevyengine:mainfrom
nfagerlund:nf/may26-optional-tracy-client

Conversation

@nfagerlund
Copy link
Copy Markdown
Contributor

@nfagerlund nfagerlund commented May 3, 2026

Objective

Tracy integration is currently controlled at compile time with the tracing-tracy feature, so you don't have to build it if you don't use it. Rad. But once it's compiled in, you have to always connect the profiler or collector when your app is running, or else the Tracy client'll blow out your RAM with its tracing event buffer. This is annoying enough to have been worth a warn-level log in the LogPlugin build method.

If it's annoying enough to warn, perhaps it's annoying enough for an opt-out switch. I'd love to have the option to disable the client by default, enable the profiler whenever I want to check something, and NOT have to recompile bevy_log and everything downstream of it in order to switch modes.

Solution

Luckily, it turns out Option<impl Layer> implements Layer, so this is easy.

I left the default as "on", to match existing behavior.

Testing

  • Did you test these changes? If so, how?

Yeah — I originally made this patch to a fork of 0.18.1, so I could use it in the game I'm working on, and I tested that. I confirmed that it doesn't send the regular profiling data to Tracy if I don't pass --profile to the app (I used that cli flag to set the enable_tracy field). I also confirmed that memory usage is under much better control! It quickly grows to multiple GB if you enable Tracy and don't start the profiler, but if you refrain from enabling it, it stays down in the low hundreds of megs. (It grows a tiny bit over time, but it seems likely that's normal ECS allocation stuff happening.)

However, it does still seem to wake up the Tracy profiler (if it's listening) and get it to start logging global CPU, and I'm not sure what's up with that! Other apps that don't have a Tracy client compiled in don't do that. But I can't find where in the engine or dependencies it would actually initialize a Tracy client if we never construct a subscriber layer. 🤔

  • Are there any parts that need more testing?

The GPU context thingie is different between main and 0.18.1 -- the Option return type was added sometime since then. So I haven't tested the current approach.

However, before I fixed that (slightly differently) in my 0.18 fork, it actually still didn't explode... which means the Client::running().unwrap() must have found a running client, bringing us back to the question above of who the heck is initializing that. Oh -- or else it means that GPU diagnostics path never ran; it looked to me like it should always included in the default plugins when the tracing-tracy feature is live, but I could easily have misinterpreted it.

  • How can other people (reviewers) test your changes? Is there anything specific they need to know?

Just gotta rig up a way to toggle that boolean field at runtime. I went with let enable_tracy = std::env::args().any(|arg| &arg == "--profile");.

  • If relevant, what platforms did you test these changes on, and are there any important ones you can't test?

I've only tested on mac so far, gonna test on my windows potato later. (The potato was the motivating use case.) works good on the windows potato.

nfagerlund added 2 commits May 3, 2026 14:05
Tracy integration is currently controlled at compile time with the
`tracing-tracy` feature, so you don't have to build it if you don't use it. Rad.
But once it's compiled in, you have to _always_ connect the profiler or
collector when your app is running, or else the Tracy client'll blow out your
RAM with its tracing event buffer. This is annoying enough to have been worth a
`warn`-level log in the LogPlugin build method.

If it's annoying enough to warn, perhaps it's annoying enough for an opt-out
switch. I'd love to have the option to disable the client by default, enable the
profiler whenever I want to check something, and NOT have to recompile bevy_log
and everything downstream of it in order to switch modes.

Luckily, it turns out `Option<impl Layer>` implements `Layer`, so this is easy.
This function already returns an Option and has two paths that might return None
(macos for some reason, and the .ok() on a fallible constructor), so it should
use ? instead of crashing the app.
@alice-i-cecile alice-i-cecile added C-Usability A targeted quality-of-life change that makes Bevy easier to use A-Diagnostics Logging, crash handling, error reporting and performance analysis D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 3, 2026
@cart cart closed this May 5, 2026
@cart cart reopened this May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Diagnostics Logging, crash handling, error reporting and performance analysis C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants