Parallel design for game engine

Started by
8 comments, last by All8Up 4 years, 2 months ago

I had an idea about applying the “map reduce” model to a realtime game code.

I have written alot of game code, both on my own homebrew game engines, and on Unity & Unreal.

My game loops have allways been designed as a collection of entities ( objects or componnents ) which call each-others' methods sequentially.

Simplified example: Hero raycasts gunshot to enemy → Hero's gun calculates that shot hit → Gun calls enemy's “do 20 damage” function.

I had always avoided parallel game logic because of all of the consistency problems that might arise.

Recently I had toyed with creating a new type of loop (loosely based on map reduce) which might be an interesting way to do parallel computing without locks:

1. An entity can only ever operate uppon itself

2. All state changes in other entities are handled as messages.

ex: Gun cannot call a method enemy.doDamage(20) directly. Instead, it queues a message to said enemy.

Then for each logic frame:

  1. Entities are split between the different CPU threads
  2. Each entity reads and operates on it's own queued messages for that frame.
    • Entities can only:
      • Change their own state
      • Queue messages for other entities
  3. One might repeat step 1 and 2 until all messages for the current frame have been processed.

This way, one can parallelise game logic without worrying about locks and atomicty.

Let's ignore for a minute the complexity of said memory allocation, and debugging stackless chains of logic code ;-) . I am a hobbyist, I am not interested in practicality, I just want to do something cool ;-) .

This engine design idea seems very basic to me, I am wondering if you are aware of a game or a game engine that have tried this before?

What do you think of the concept?

My Oculus Rift Game: RaiderV

My Android VR games: Time-Rider& Dozer Driver

My browser game: Vitrage - A game of stained glass

My android games : Enemies of the Crown & Killer Bees

Advertisement

You might get trouble from having asynchronous communication, possibly together with different speeds of message processing. It is known as race condition. This might happen at every point where two messages target the same entity, and the order of those messages is crucial.

Say some entity is waiting for a timer message to fire. At some moment, the timer posts a message that it has run out (ie it can fire). At almost the same time, but say a fraction later, a projectile hits the entity, killing it.

So the entity should fire and then die, but perhaps the tmeout message processing is a little slower, so the hitting message arrives first, and the entity dies without having fired.

You also get in trouble if you want to reduce redundant code and components so lets say you have an enemy and a player component, both need a transform that says where in the world they are located and hat they are facing. Instead of adding those properties to each component, you decide to have a transform component but now how do enemies and players update?

What about message concurency? Some component might need to know values of another component but in between processing the component changed for a message processed after component A has read the values of component B but before component A finished processing. This may lead to artifacts in best and crashes in worst case

I don't think that I am in danger of “artifacts” and race conditions, because any entity can only update themselves. And that is done atomically on a single thread: As stated in “Entities are split between the different CPU threads”. So there will be no problems of 2 method calls colliding.

There could be some transactionality problems, but that would happen anyway on any distributed system, Having messages instead of interwoven method calls would help clearly define transaction boundaries.

My Oculus Rift Game: RaiderV

My Android VR games: Time-Rider& Dozer Driver

My browser game: Vitrage - A game of stained glass

My android games : Enemies of the Crown & Killer Bees

SillyCow said:
Simplified example: Hero raycasts gunshot to enemy → Hero's gun calculates that shot hit → Gun calls enemy's “do 20 damage” function.

You need to consider not only write access, but also read access. What data structures is the raycast using, and what happens if they are being modified concurrently with the raycast?

A few of the points you described I actually just read about in this book, http://gameprogrammingpatterns.com/

I think in general something like that would work. If you at least synchronized frames, you could easily handle the read/write race conditions by using something like a double buffer method. I think using multiple threads though might bite you in the ass. I think the reason asynchronous programming is taking off like it is, is because its lighter weight, and when not compute limited, faster that multi-threading. (Bare in mind, I am no CS). If you exceed the number of available threads on the CPU, you're going to end up with worse performance due to the OS context-switching so often.

SillyCow said:
I don't think that I am in danger of “artifacts” and race conditions, because any entity can only update themselves

This is wrong unless you never ever take other entities into acount and this won't happen because a player and/or enemy can have moved at the time the bullet hits it. So how do you make sure that such “artifacts” like a player got hit by an enemy even if he was already out of reach won't appear?

Shaarigan said:

This is wrong unless you never ever take other entities into acount and this won't happen because a player and/or enemy can have moved at the time the bullet hits it. So how do you make sure that such “artifacts” like a player got hit by an enemy even if he was already out of reach won't appear?

That is mitigated by the fact that the bullet does not decide that it hit the player. Rather the bullet tells the player: I am colliding with you at X,Y,Z. It's up to the player to “feel” if it was hit.

My Oculus Rift Game: RaiderV

My Android VR games: Time-Rider& Dozer Driver

My browser game: Vitrage - A game of stained glass

My android games : Enemies of the Crown & Killer Bees

Generally speaking, map reduce is a tool towards a reasonable parallel execution pipeline and yes, you can write an entire engine using it and succeed. I know this because I've done a variation of it, I reworked things into something a bit more generalized and targeted towards games specifically. So, why not map-reduce as the only solution? Mostly because I wanted generic reusable systems and a GUI that could be used to deal with the extreme complexities involved. They end up the same, just start from a different location for the most part.

As to the bullet/impact bit. The way I deal with this in my engine which is parallelized from ground zero, is split the check and action. You run the code that moves the bullet, checks for collision and produces impact events in one part of the code. Then the other part has exclusive write access to physics/health and applies the events. The application portion is done single threaded but because I define dependencies between the functions and also the access rights they have to data, I can schedule other non-conflicting work at the same time and still fill up all the threads.

All said and done, I would suggest thinking in terms of map reduce but considering how to move to a data driven pipeline definition. I.e. you don't want the code to define the pipeline as it is too hard to keep all the bits of a game engine in mind, you could easily be talking about a chain of 100+ parts. Rather, I use a system where I declare each piece of code to be executed, what data it accesses, the rights to the access (read/write) and what other pieces of code are dependencies. From that, I generate the pipeline and collapse it, which is automated versus the manual version of figuring out where to map things, reduce things etc.

This topic is closed to new replies.

Advertisement