Why is my shader using CPU when only working on HLSL side ?

Hi guys !

I have written a water shader that makes a lot of calls to a Simplex noise function which I found there : GitHub - ashima/webgl-noise: Procedural Noise Shader Routines compatible with WebGL

I have noticed that, if I call this function twice more often in my vertex shader, it doubles the CPU time and thus divides the framerate by 2, while the GPU time remains approximately the same.

Why is that so ? Shouldn’t the GPU be doing the extra work ? I’ve read the code of that noise function and it seems to be purely HLSL code without any access to the Unity C#, so I’m a bit confused by these results.

Any idea ? Thanks ! :slight_smile:

You’re right that shader usage shouldn’t affect the CPU.

You might want to use the Timeline view on the profiler to see what’s actually taking the time. It could easily be the CPU just waiting for the GPU to finish the previous frame–in which case it’s really the GPU taking twice as long.

Unity somewhat confusingly does not show the GPU render time in the statistics window. The “render thread” is also CPU time, just specifically how long it takes for the CPU to tell the GPU what to render. Changing the shader will indeed not show any change there. As @Baroque mentioned, the CPU time suddenly taking twice as long is a good indication that the GPU is the problem.

If you run the profiler and the top entry on the CPU is GFX.WaitForPresent that means the GPU is taking so long to render that Unity is stalling the CPU to wait for the GPU to finish.

Well the attached picture shows that time line, but I’m not sure about how to interpret what I see.

According to the times I read in the “Stats” overlay of the game view, the CPU seems to be the one limiting my FPS.
Also in the results you see I purposely disabled V sync in order to see what max FPS I could reach.

Why would the CPU wait if that wait is reducing FPS ? Doesn’t make sense to me.

The CPU isn’t reducing the FPS, the GPU is. The CPU is waiting because the GPU hasn’t finished rendering so it can’t tell it to start render something else yet.

Disabling V sync just waits for each frame to finish displaying before sending the next frame, and usually the frame takes less time to render than it does to display.

Lets say you have a 60hz monitor. This means every 16.67 ms the monitor refreshes the display. It also means it usually takes 16.67 ms to update the display, it’s not instantaneous, but rather starts at the top of the display and updates each line of pixels one after another over the next ~16 ms to get to the bottom. Yes, even modern LCD monitors do this. If the image that’s being shown changes while it’s updating the display, that causes screen tearing as the top of the screen and the bottom of the screen may be showing different images.

Vsync works by having the CPU & GPU wait for the next display refresh before sending a new frame to the GPU to render. The expectation is the GPU takes less than 16.67 ms to render a frame so each frame finishes rendering before the next display refresh, and it’ll swap the image being displayed to the newly finished one just before the new refresh starts.

In both of these cases often the CPU is taking more time to do its work than the GPU, so by the time the CPU is ready to start giving instructions to the GPU it’s already finished rendering the last frame, or enough of the last frame that it can start taking new instructions.

What you’re seeing is the GPU (or specifically the CPU side drivers for the GPU) telling the game “woah woah woah, I’m still busy right now, wait a little bit before you give me more to do.” That Gfx.WaitForPresent is Unity waiting for the GPU to say “Okay, I’m ready for new instructions now.” This is going to happen if the GPU is taking longer to render something than it takes for the CPU to finish calculating the last game state.

1 Like

Thanks for your answer. But why is unity showing me approximately the same 4 ms render time regardless of whether I’m doing more work on not in my shader ?
I also noticed that the FPS displayed by the “Stats” overlay of the game view seems to be overestimated by comparing it to two FPS counters from the asset store.

Not really. The profiler is only showing what the CPU is doing. It is not showing it takes “4 ms” for the GPU to render, it’s showing it takes “4 ms” for the CPU tell the GPU what to render. By default the Unity profiler doesn’t do any profiling of the GPU. If you notice there’s a “CPU: ##.##ms GPU:0.00ms” in that screengrab you posted above. That doesn’t mean the GPU is taking 0.00ms to render, it means Unity isn’t profiling the GPU and the numbers you’re seeing are just the CPU times.