VFX Graph: "Idle" VFX objects wasting a lot of GPU?

I’m dealing with a relatively minor annoyance in working with VFX Graphs in my scenes. Specifically, even when a VFX Graph object is sitting there “doing nothing”, it still appears to use a lot of GPU resources, due to the way the Culling work on VFX Graph objects.

First, I’ll mention I feel I generally need to set most of my VFX graphs Culling Flags to “Always Recompute Bounds and Simulate”. This seems to be the only reasonable choice for any VFX graph that has a clearly defined start/end, like a single explosion. If I choose one of the other Culling Flags, the result will be that an effect will be paused if I look away from it, and it will resume when I look at it again. Consider an explosion occurring 100 meters away from the player. It’s a big burst of fire that takes 2-3 seconds to complete. If the explosion begins, and I immediately turn my back on it, I can wait 10 seconds, then look at it again, and the effect will resume where it left off. That’s really bad behavior, and the only way I can avoid it is by setting Culling Flags to “Always Recompute Bounds and Simulate”.

The issue is, for that same explosion, the effect will generally occur in distinct “bursts”. Most of the time, a VFX object will be doing “nothing”. There will be no spawned particles, and effectively nothing to simulate. Here’s an example of this kind of graph:

However, it seems that this graph will use quite a bit of GPU resources if it’s not disabled. In my scene, with no Game/Scene views being displayed, I’ll see my GPU usage sit at around 0%. But if I enable this VFX Graph, the GPU usage will climb to 10% even though A) The game isn’t actually being rendered in any Unity windows at the moment, and B) The VFX Object itself has no particles in it. It’s not disabled, but it’s “idle”, in that it only spawns particles when an event is called, and that event hasn’t been called.

The workaround to this performance hit is to ensure all of my VFX objects are disabled if they’re not currently displaying anything visually, but that’s potentially really difficult to determine.

It seems to me that the “Always Recompute Bounds and Simulate” should be smart enough to realize it has no current particles, and that there’s nothing to compute at the moment. Or, that if a VFX Graph has its Culling Flags set to only simulate when visible, it shouldn’t pause the Time of the graph when it’s not visible, so that when I do look at it again, it catches up with where the simulation should be. But I guess I can see how that’s kind of tricky to determine if you’re not computing ever step of the simulation…

TLDR: What’s the correct approach to take with “burst” VFX Graph objects, so they’re not sitting there sucking up GPU resources the whole time they’re idle?

Hi!

This scenario you are describing is not the expected behavior, we can take a look if you can share an effect that has this problem.

Visual effects (the component) get updated every frame when they are enabled. But, internally, there is a mechanism for particle systems that sends the system to sleep when there are no alive particles. When they are sleeping we skip the simulation, and therefore we don’t dispatch any compute shaders.

The particle count lives on the GPU, so we are reading it back with some frequency (I think it is every 60 frames by default). When we read 0 particles and we didn’t issue an spawn event since the last readback, the particle system goes to sleep. Which means that it can take a bit, but it will end up sleeping.
Of course, it is possible that something broke there, we will take a look.

Interesting. I’ll also see about reproducing this in a test project, and if I find it’s not working as you describe, I’ll officially report it with a repro project. Thanks for the conformation of the expected behavior. (I’m on the latest Unity 6 LTS, btw)

I tracked down the cause of the behavior I was seeing, and reported it as a bug (IN-120566), though you’ll perhaps not consider it a bug, I’m not sure.

While I generally agree with what you said about the VFX Graph objects sleeping when idle, there’s one way to break that: Having a camera with a Render Texture assigned to it in the scene.

I have this set up in the repro project on the issue I submitted, but the simplest way to reproduce this for me:

  • Create a new scene, and create a single Simple Burst VFX asset, and put it into the scene. You can change the start event to a random value to ensure it will never generate particles.
  • Make sure the Culling Mode is “Always recompute bounds and simulate”.
  • Add a camera to the scene, create a RenderTexture in the project, and assign it to the camera.
  • I’m not 100% sure whether the VFX Graph object needs to be in the camera’s frustrum.
  • Now, you can close the Game/Scene view windows, or make sure they’re not visible, and this combination should result in a very high GPU load as long as Unity is focused. (If another tab is active in front of Unity, the GPU usage won’t occur. This is on a Windows laptop. To view my GPU usage, I open Task Manager and make sure the window is always on top, so I can still see the GPU activity while Unity is focused.)

This GPU load stops if you disable the VFX graph object, disable the camera, or unassign the render texture from it. The key is the render texture. A normal camera won’t cause this behavior.

I’m not sure if you’d consider that behavior surprising, or expected. The VFX Graph object isn’t generating any particles, but it seems like the RenderTexture camera is causing it to compute something, using a ton of GPU, even though no particles are appearing.

I spent a little more time trying to decide whether this was unique to VFX Graph, or just the way Render Textures work. It seems it’s unique to VFX graph.

I set up a different example where instead of a VFX Graph object in the scene, I instead placed a renderer with an animated ShaderGraph material on it. The material changes every frame as a noise pattern moves across its surface. But this movement/animation doesn’t trigger the camera to capture a new frame into the render texture. If I select the render texture, I can see the texture isn’t updating. But if the camera is point at an inactive VFX Graph object, it does force the render texture to recapture every frame.

Similarly, if I set up a script to move an object around in front of the camera, and I have this execute in edit mode, this won’t cause the render texture to recapture frames, even though the movement is changing what the camera sees.

So, VFX Graph is doing something special here, to force camera to update. And I confirmed the VFX Graph object doesn’t even need to be in the camera’s view frustrum. This behavior occurs even if the camera is 100+ meters away, pointing away from the VFX Graph object.

This is quite interesting, thanks for reporting it.

We will take a look when the bug gets to us. There might be a good explanation why this is happening, but if there is an opportunity to fix it, we will.

Thank you!

Thanks.

One other potentially related behavior I’m seeing related to the Scene View’s option to turn on/off VFX:

I’m noticing that although turning that flag off hides the VFX, it doesn’t actually stop it from processing/rendering the VFX. In other words, if I turn off that flag, I stop visually seeing the VFX, but I can still see very high GPU usage unless I actually disable the Visual Effect game object.

This occurs for all Visual Effects, regardless of Culling Mode. It seems that the Scene View’s version of “visible” is exclusively whether the VFX Graph object is within the scene camera’s frustrum; Toggling off the “Visual Effects Graphs” option does not make the VFX Graph object non-visible for the purposes of computing its graph.

Ok, so I finally had some time to look at this.

What you are seeing is a behavior specific to when the editor gets repainted. In general, when in editor and outside play mode, frames are only repainted when the rendered image is considered to be outdated. If you are not moving any objects, or the camera, and you don’t hover the mouse over the image, etc., there is no need to repaint.

This is slightly different with VFX graph. Because the visual look of a VFX can change without any external input, VFX graph usually request the frame to be repainted if there are any active VFX. It is, in practice, very similar to enable “Always Refresh”.

The key here is what is considered to be an active VFX. Even when a system is sleeping, there is no way to know if it will wake up next frame. At the moment, if there are any enabled VFX, it will trigger a repaint.

There is one exception to this and it is related to the other behavior you pointed out with disabling* Visual Effect Graphs in the scene. If ONLY the scene view is active and VFX toggle is off it will not trigger a new frame.

However, that we repaint a new frame does not mean that we actually dispatch compute shaders for VFX. If a VFX is sleeping it will still not have any GPU cost. The GPU activity that you are seeing is probably not related to VFX, it is just the camera being rendered. You can use the Frame Debugger to check what is being rendered.

I think there could be room for improvement, even if this is an editor only optimization. Currently we flag the repaint before culling. It might be possible to check after every culling operation and see if any VFX are within frustum of any camera to trigger a repaint. But even this has some corner-cases: what happens if bounding boxes are being updated dynamically, etc.?

We will prioritize it when we receive your report (I still didn’t see it), but I’m afraid it is very unlikely that we will spend time on this. It would be nice to optimize it further, but it is editor only and the fix is not trivial.

*Please notice that this flag only controls the rendering of VFX, not the simulation. It does not affect the compute shader dispatches.

tl;dr:

The behavior you are seeing, while odd, it is kind of expected and similar to enabling “Always Refresh”.
We really appreciate that you reported this and we will take it into consideration, but it is unlikely to get optimized.

Thanks for the detailed response. Sounds like a tricky thing to get perfect in all situations. And in fairness, it seems pretty reasonable that if I’ve set the system to “always” do something, it’s going to always do that thing.

For now, I’ve just taken to keeping these VisualEffect components disabled until runtime, when I turn them on only when needed. I haven’t spent the time determining whether this whole situation is just a Scene View thing (and therefore has no impact on a build), or if this is good practice in a build as well. But it’s pretty simple, and can’t hurt, I suppose.

Thanks again for the explanation.

I think enabling the VFX only when needed like you did is best, especially if they are set to always simulate. Even when sleeping, we have to go through all the enabled VFX every frame to check if they have to wake up. It is done in parallel when possible, but still.

The only difference between editor and build here is that the editor is not refreshed every frame if not needed, and we already saw that it can’t optimize when there are active VFX.

Have a great day!