I am running an algorithm on the GPU but i also execute it on the CPU at certain sample points for physics.
I run the CPU algorithm via Time.time and the GPU uses _Time.y.
Are they in sync? Or can they be out of sync? Is there a way to keep time in sync between the two?
I don’t mind a slight amount of difference in sync, because my physics only runs at like 10 FPS and doesn’t need to be totally perfect. But it is not clear how out of sync they can be to know if i can rely on it.
If i can’t i need some way to sync time between the time more accurately. Whats the best approach ?
Or at least it should be. I’ve run across a few cases where it doesn’t in the past on certain platforms, but I suspect this was a bug.
I’ve seen some people say they use Shader.GetGlobalVector(_Time); successfully to avoid any kind of problems with being out of sync, though I have not personally tried it.
Also using a custom time value is underrated as it means you can pause your game world and menu separately if you want to have shader animated elements in the UI.
When you say using a custom time value, do you mean just passing a time value to a property of the shader every frame ? Is that a performant option? I was led to believe passing data to/from the GPU is a performance problem and when possible, best to avoid it…
Passing data from the CPU to the GPU is pretty fast. Think about it this way. A single particle system with 1000 particles is sending at least a megabyte a second of data to the GPU. Every dynamically batched object is doing that much or more as well. Even just having a single moving object is several times that much data. An extra float isn’t a big deal.
Getting data back from the GPU to the CPU is a big bottleneck as that’s simply not how the system was designed.
Ah i see. Thanks for explaining. If only the system was designed for to and from data transfers, the potential for compute shaders would be immense for many run time algorithms for the CPU side.
You would have to be talking to GPU/hardware developers at that point
But you can totally do this if you don’t mind a small bit of latency before you get the results…
These kinds of things are possible (and done) on consoles since they used shared memory. You can have the GPU do something and read the results on the CPU almost instantly. Not possible on desktop since the GPU has its own RAM. You transfer a bunch of data to the GPU and the GPU works away on that data, but to get data back you need to stall the GPU to make sure the data doesn’t change while it’s being transferred back from from the GPU to the CPU RAM. Technically the way modern rendering systems are designed the actual rendering happens a frame delayed from what the CPU is doing. So often times to get data back from the GPU means waiting two frames. This isn’t always true, as the GPU can often be idle between frames, so it’s possible to have it do some compute work and get back the data in a ms or two, but it’s hard to rely on that on desktop.
Depend on when you call GetGlobal for a value set by Unity’s internal rendering code you may get the current frame, the previous frame, or junk. Some values might be set early in the frame, some just before rendering starts, and some might never actually get set in a way user-land c# can access. Generally you should never expect GetGlobal to return anything of use for any value but ones you’ve set yourself.
I’ve tried using Shader.GetGlobalVector("_Time") twice since my previous post. In one project I think I was always getting the previous frame’s data, and in the other I only every saw zeros. I don’t know why they were different. I don’t know why other people got useful values. Could be different versions of Unity changed how things worked. Could be when I was checking it. Could be I’m remembering it wrong. I don’t ever actually use it, and both times was just to see what values I got. I do however sometimes rely on _Time.y and Time.timeSinceLevelLoad matching, and in the cases I’ve used it it has worked. But again it might just be the Unity versions or other factors in how the project was setup allowed for that. Anything I need to absolutely be perfect I always use a custom time, or drive material values from script directly.
Using a custom timer like the always reliable @bgolus suggested was the only thing that worked for me, since whichever time variable I used there was always some issue with it. Mainly, it seemed to do completely different things between Editor and Player buids…
Mmm… isn’t passing the time from CPU to control GPU animation or dynamic objects defeating the whole idea of modern GPU ? Back to the past, we had jerky days, thanks to CPU and GPU frequency mismatch accumulation and inconsistencies. All those arguments Fixed Update vs Updade, delta time vs average… So actually it is a big deal to leave timing within GPU which is perfectly synchronised with user display, unlike CPU.
As I had the same problem and found inconsistency with shader’s timing between ShaderGraph’s compiled shader vs C#'s Time.timeSinceLevelLoad I’ve looked deeply into the Unity’s source code and found that:
private void InitRenderGraphFrame(RenderGraph renderGraph, ref RenderingData renderingData)
{
using (var builder = renderGraph.AddRenderPass<PassData>("InitFrame", out var passData,
Profiling.setupFrameData)) //TODO rendergraph maybe add a new profiling scope?
{
passData.renderingData = renderingData;
passData.renderer = this;
builder.AllowPassCulling(false);
builder.SetRenderFunc((PassData data, RenderGraphContext rgContext) =>
{
CommandBuffer cmd = rgContext.cmd;
#if UNITY_EDITOR
float time = Application.isPlaying ? Time.time : Time.realtimeSinceStartup;
#else
float time = Time.time;
#endif
float deltaTime = Time.deltaTime;
float smoothDeltaTime = Time.smoothDeltaTime;
ClearRenderingState(cmd);
SetShaderTimeValues(cmd, time, deltaTime, smoothDeltaTime);
data.renderer.SetupLights(rgContext.renderContext, ref data.renderingData);
});
}
}
After I’ve used that block:
#if UNITY_EDITOR
float time = Application.isPlaying ? Time.time : Time.realtimeSinceStartup;
#else
float time = Time.time;
#endif
Is this the time passed to the shader side and works ?
It would be great if could have an official answer also on how to pass our own time through Shader.GetGlobalVector(“_Time”), without having to define new variables, so can be globally applicable.
E.g. i assume there is an injection point from Unity side, that can be overwritten by our own global set if timed after that declaration.