Download

What happens to engine tick in case rendering of the frame took long time?

It is commonly described in game profiling that the bottleneck of the frame rate could be the game logic, or the rendering process.

It also the case that rendering is run, for the most part, on a different thread(s).

I am struggling to put it all together time-wise; I’d like to know what exactly happens when the rendering GPU device or thread takes more time than what the engine tick takes, specifically the FEngineLoop::Tick; Does it idle/wait for the rendering frame to finish doing nothing else at all? or does it go to the next frame with the rendering system own calculations compensating for what happened last frame? Now, what if it is consistently always the case the rendering is too slow for the GameThread’s loop for a certain game, does the GameThread become almost always idling? if so, where exactly in its logic keeps looping for the rendering markers to finally change each frame?

In Unreal 4, there is exactly one Tick call per frame presentation. When the frame presentation takes a while, the DeltaTime available to the tick function (from the world) will be bigger, so whatever simulation happens in ticking, can compensate.

Also, it’s not always the CPU that’s taking longer; it can frequently be the GPU especially for bigger resolutions on lower-end hardware. This will show itself as a long ‘stall’ in Present, waiting for the GPU to finish.
This means that there is a “stall” where perhaps the entire game will be waiting for the GPU before it can proceed.

For more information about how to profile an Unreal game, try:

Yes, here exactly comes my question, if the engire game is waiting for the GPU, what exactly is the GameThread doing at that very moment? Is it sleeping, or looping indefinitely until the GPU finishes? and in what part of the source code it does so ( is it in FEngineLoop::Tick)?

Gotcha – you’re asking “when buffer swapping is waiting, what is the game thread blocking on?”

Good question! I know it blocks on something but not exactly what.
Overriding Tick(float) in a player controller, and putting a breakpoint, and tracing it back, FEngineLoop::Tick() ends up doing this:

			SCOPE_CYCLE_COUNTER(STAT_FrameSyncTime);
			// this could be perhaps moved down to get greater parallelism
			// Sync game and render thread. Either total sync or allowing one frame lag.
			static FFrameEndSync FrameEndSync;
			static auto CVarAllowOneFrameThreadLag = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.OneFrameThreadLag"));
			FrameEndSync.Sync( CVarAllowOneFrameThreadLag->GetValueOnGameThread() != 0 );
		}

which seems to be the closest I can find to something that would sync up with the GPU.
So, FFrameEndSync::Sync() might be the spot to look for? It also has interesting comments inside like “doing opportunistic loading while waiting for vsync.”

I haven’t proven that this is actually the blocker (in fact, there may be more than one blocker – RHITick might be another place of stalling, perhaps?
If you find a definitive answer, I’m interested, too!

Great reply. Thanks jwatte indeed, although I now have a ton of work to just understand what is happening :sweat_smile: