Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

for those unfamiliar, it's because the game's main loop looks like

  while (!exitRequested) {
    player.updateState();
    someCharacter.updateState();
    someOtherCharacter.updateState();
  }

you could in theory make this kind of updates in parallel but then the entire game becomes a non-deterministic chaos and trying to deal with synchronising threads in a context like this is such a nightmare and I'm sure intractable performance-wise. did anyone even try this, ever?

bottom line, real-time or turn-based, a piece of code needs to execute before or after another, not at the same time.

the order in which things take their "turn" each frame becomes very important the more complex the game btw, so even the order in which things execute serially cannot be entirely arbitrary. usually for things that depend on other things to update their state in order to accurately update their own state. which is a lot of things in every game. for example, you wanna update the text on the GUI that says how much gold the player has. you'll update the text after everything that could have influenced the gold this frame has updated (i.e. at the end of the frame). player input state (keyboard input e.g.) is updated at the beginning of the frame before you make the player's character do anything based on input.

particular stuff can be parallelized or turned into coroutines that "update a little bit each loop" so as to not kill performance. like pathfinding, a character needs to go from point A to point B, he doesn't really need to find the whole path now. a partial path while a separate thread calculates the entire path can do. or just make him think a bit while the pathfinding thread finds a path, the advantage is characters thinking about their actions is also realistic :P




> did anyone even try this, ever?

I think it depends on the granularity.

Coarse-grained parallelism is already common: some things like AI (like your example), Physics, Resource Management, Shader Compilation, Audio and even Netcode are already commonly run in separate threads.

However you won't see the updateInputs(), all the updateState() and sometimes even render() running in parallel for two reasons: first because it's often cheaper and easier/simpler to run in the main thread than dispatching async jobs anyway. Second because each operation often depends on the previous ones, and you can't wait until the next frame to take it into account: you often want instant feedback.

However these things can in theory be run in parallel without becoming chaos. ECS is often very parallelizable: you can run multiple Systems in different threads, as long as the output of one System is not a dependency of another running at the same time. You could also process multiple components of a system in multiple threads, but that would negate ECS main advantage: being cache-friendly by virtue of running in a single thread.


I think Bevy in Rust demonstrates this well. Your systems are structured in a dependency graph and, I think, automatically figure out what things can be done in parallel.


Naughty Dog developers did a talk at GDC 2015 where they explain how they parallelized their game engine.

https://www.gdcvault.com/play/1022186/Parallelizing-the-Naug...

>for example, you wanna update the text on the GUI that says how much gold the player has. you'll update the text after everything that could have influenced the gold this frame has updated (i.e. at the end of the frame).

Modern game engines are pipelined; you render the previous frame logic. In the talk aforementioned, they show a three stages deep pipeline looking like this

    [FRAME]       [FRAME+1]       [FRAME+2]
    ----------------------------------------------
    [LOGIC]       [LOGIC+1]       [LOGIC+2]
                  [RENDER LOGIC]  [RENDER LOGIC+1]
                                  [GPU RENDERING]
each stage is independent and doesn't require syncing. they call that "frame centric design".


This system introduces yet more lag, which is increasingly awful kinesthetically. It makes modern games feel... sticky, sluggish, unresponsive, imprecise. We've gone from instant feedback to ridiculous degrees of input latency.

You press a button. It takes 1 frame for the signal from your USB device to get polled through the OS into your engine. Then it takes 1 frame for the input to affect the logic. Then it takes 1 frame for that change in logic to get "prepared" for rendering. Then it takes 1 frame for the GPU to draw the result. And depending on your video buffering settings and monitor response time, you're still adding a frame until you see the result.

If you're running 60 frames per second, that's an abysmal 83 milliseconds lag on every player input. And that's before network latency.


Last of Us Remastered had +100 milliseconds of input delay. GTA5 was above 150ms. Modern games feel sluggish because of the animations getting more and more realistic. With old games, the input would break the player current animation instantly, today games don't allow that anymore; everything has to be blend together. The input may have to be buffered and analyzed for few "logic" frames before having any effect on the "render logic" frames.


And then there's the output latency of LCD TVs. Seems less of a problem than it used to be, but some older TVs could easily add 50-100ms of latency (particularly if not switched to Game Mode, but even a game mode didn't guarantee great results)

But these days it's hard enough to convince people that 'a cinematic 30fps' really really sucks compared to 60 (or better), and there's an even smaller number of gamers/devs who seem to notice or care about latency issues.


Most people barely notice or mind, which is unfortunate. Elden Ring has so much input lag but most dont even notice.


Ooooh this is like that

  for {
     a[i] = something;
     b[i] = something;
     c[i] = a[i] + b[i]; // can only do a and b at the same time because c depends on them
  }


  // handle a[0],b[0]
  for {
    a[i] = something; 
    b[i] = something;
    c[i-1] = a[i-1] + b[i-1]; // can do all 3 at the same time because no deps.
  }

  // handle c[last]

optimization I saw in a talk about how CPUs can do many instructions at once if they don't depend on each other.

I was unaware of how something like this could play into game engines at the loop level, thanks for the link I'll watch it asap.


I learned about system order the hard way.

A system would try to apply damage to a ship that was already destroyed.

It taught me that you often have to flag things and then do a cleanup phase at the end. So destroyed = true but don’t outright delete the entity until the end of the “turn.”




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: