Recommended Posts

Hello, so I wanted to get an opinion on the best ways to manage gameplay logic in a multithreaded manner.

After watching these two presentations :

http://www.gdcvault.com/play/1020886/Killzone-Shadow-Fall-Threading-the

http://www.gdcvault.com/play/1022164/Multithreading

It seems that the best approach is to create a dependency graph for object updating, and for any object that needs to access another object that isn't a dependency, a message should be constructed and called after the "multithreading" phase. I wanted to know if anyone had any experience with multithreading game objects in this manner, or if they have any other solutions.

 

Share this post


Link to post
Share on other sites
17 hours ago, ApochPiQ said:

Unless and until you have a proven difficulty with your actual game objects getting work done in 1 CPU tick, multithreading is not the right tool.

[...]

 

Seconded.

Most games I've worked on typically update game logic systems between 10-20Hz on a single thread without any impact to the gameplay experience. There simply isn't enough going on every frame for gameplay to consume that much CPU, especially considering that humans themselves are limited in how fast they can respond anyway. As long as the key player "interactive" systems are real-time responsive (rendering, sound, input, etc.), the game as a whole will feel responsive. Also consider that in any multiplayer game, network latency is going to mask a lot of it.

Certain gameplay-relevant systems that are more computationally expensive (AI, pathfinding, physics) can be multithreaded independently as previously mentioned, but again those are very specific, well-defined problem domains.

Share this post


Link to post
Share on other sites

Thank you both for your responses. I guess what you are both saying makes sense. I currently have a task system and have jobified my rendering and wanted to take a similar approach to gameplay logic. I might just be focusing too much on preoptimization and also wanted to start leveraging multicore in more parts of my engine.

Share this post


Link to post
Share on other sites
6 hours ago, AxeGuywithanAxe said:

for any object that needs to access another object that isn't a dependency

If you're accessing another object, that's a dependency :P

The method that I've typically seen used on console games (which need this because console CPU's are terrriiiiiblleeee):

Try to do a lot of one operation at the same time (batch processing).
e.g. instead of for each entity, DoSomethingRandomViaAVirtual()
use: for each A, DoAStuff; for each B DoBStuff()

When a particular batch operation is identified as being an actual performance issue, figure out how to move it over to the job system.
To begin with, this is as simple as:
jobHandle = DoAStuffOnJobs()
Wait( jobHandle )
DoBStuff();// this depends on AStuff

Then to improve it further, try to move dependant operations earlier in the frame so that the "Wait" operation above hopefully doesn't actually have to wait.
jobHandle = DoAStuffOnJobs()
DoCStuff();
Wait( jobHandle )//hopefully doesn't actually pause any threads because AStuff jobs naturally finished during CStuff
DoBStuff();// this depends on AStuff

Share this post


Link to post
Share on other sites
11 hours ago, Hodgman said:

If you're accessing another object, that's a dependency :P

[...]

 

Well the issue I was having was with intertangeled dependencies. For example, let's say you have entity a and entity b. Lets say that entity a has a weapon attached to it, and entity b is attached to entity a. Entity a has to be updated before the weapon , before entity b. That fundamentally means that you can't batch process entities because of this dependency, unless you constantly update the "entity" array to make sure all dependent entities are ahead of it. There are certain components that can be easily batch processed, i.e a navigationcomponent , or ray tracing though.

Share this post


Link to post
Share on other sites
10 hours ago, AxeGuywithanAxe said:

Well the issue I was having was with intertangeled dependencies. [...]

2

It means you've got to apply the single responsibility principle more thoroughly.

"Attaching" one object to another is a problem for a transform-hierarchy. The transform hierarchy can "attach" different entities to each other without caring what those entitys are; it just operates on transform-nodes.
(Side note: This is where people say "Aha! That's ECS!". No, that's following OO properly, composing entities out of simpler objects, and obeying SRP.)

You can batch-process the entire transform hierarchy so that the location of every entity is known, and then batch process your weapons afterwards.

Having complex entities that do many different things is the opposite of the batch processing that I'm suggesting ;)

Share this post


Link to post
Share on other sites
10 hours ago, Hodgman said:

It means you've got to apply the single responsibility principle more thoroughly.

[...]

 

I don't necessarily understand how that would completely work then. How granular would your component system be? There can be dozens of different data dependencies that can exist between components and entities. Are you saying that you would not update on an entity basis , and instead update on a per component type basis? Let's say you have a skinned mesh component on one entity, and it performs animations, an entity attached to this entity would have to be updated after the skinned mesh component is updated. With your approach, how would you handle this? Would you update all of the components first and then batch process the entities based on their transform?

Share this post


Link to post
Share on other sites
1 hour ago, AxeGuywithanAxe said:

Are you saying that you would not update on an entity basis , and instead update on a per component type basis?

Well I'm using OO, not ECS so I don't have entities and components, just objects. Complicated objects ("entities") tend to be made up of simpler objects ("components").  I update objects in the order required by their dependencies. 

1 hour ago, AxeGuywithanAxe said:

Let's say you have a skinned mesh component on one entity, and it performs animations, an entity attached to this entity would have to be updated after the skinned mesh component is updated.

If the animation update produces data that's consumed by the transform/attachment update, which produces data that's consumed by the weapon update, etc... Then yes, update animations first, and then update transforms/attachments, and then update weapons.

It can actually be illuminating to try to look at your code from a procedural or functional perspective. Especially functional w/ immutable objects -- try to imagine how you'd structure your code if every object was immutable (read only after construction). This is a lot more like math. In math we can't say:
x = x+1
Because that's simply false (for real numbers)... So a mathematician would write something like:
\(x_{t+1} = x_{t}+1\)
And then the code would have to be something like:
thisFrame.x = prevFrame.x + 1

This is an interesting way to look at your code because in the immutable style, it's always painfully obvious as to which "version" of the x variable is being consumed by any step. In typical OO code with mutable objects, it's not always obvious which "version" of an object you're interacting with, and it can be very easy to accidentally consume an object at the wrong point in the frame (e.g. updating animations after updating attachments).

So, I would recommend structuring your code in such a way that the required update order is as obvious as possible.
e.g.
Instead of:
UpdateA();
UpdateB();//uses results from A internally
Try to write your high level update loop more like:
a = UpdateA();
UpdateB(a);
As in this style, it's actually impossible to call UpdateB without calling UpdateA first.

IMHO, this is one thing that most ECS frameworks completely suck at, and a lot of traditional OOP code as well...

Share this post


Link to post
Share on other sites
8 hours ago, Hodgman said:

Well I'm using OO, not ECS so I don't have entities and components, just objects. Complicated objects ("entities") tend to be made up of simpler objects ("components").  I update objects in the order required by their dependencies. 

[...]

1

Ah, so I think my method is similar to yours then. I don't use ECS , I use a main object (entity), that contains components , somewhat like ue4 . So I'm guessing that you update your entities and components at the same time then? Or do your components only contain data?

Share this post


Link to post
Share on other sites
5 hours ago, AxeGuywithanAxe said:

So I'm guessing that you update your entities and components at the same time then? Or do your components only contain data?

No I don't have entities/components, I have objects :D You might call small objects "components" and you might call bigger objects that are composed of those small ones "entities", but there's no distinction... and there's no "entity framework" (or "component framework").

When I have a complex composed object (that you might call an "Entity") that's made up of simple ones (that you might call "Components"), I generally do not update that entire group of composed objects in one go (a.k.a. updating the entity, by updating all of it's child objects at the same time). Generally I operate on batches of simple objects -- which is more similar to updating a "system" in an ECS design.

Generally the large composed objects (that you might call "entities") don't have very much update logic at all. They're basically just there to create a bunch of simpler objects and plug them into each other in interesting ways.

IMHO having generic "entities" with a virtual "Update" method is evil. I rant about it being "spaghetti flow" at 44mins in this vid, moving onto flow graphs at 49 mins. 38 mins is immutable objects being good:

 

 

Share this post


Link to post
Share on other sites
1 hour ago, Hodgman said:

No I don't have entities/components, I have objects :D You might call small objects "components" and you might call bigger objects that are composed of those small ones "entities", but there's no distinction... and there's no "entity framework" (or "component framework").

When I have a complex composed object (that you might call an "Entity") that's made up of simple ones (that you might call "Components"), I generally do not update that entire group of composed objects in one go (a.k.a. updating the entity, by updating all of it's child objects at the same time). Generally I operate on batches of simple objects -- which is more similar to updating a "system" in an ECS design.

Generally the large composed objects (that you might call "entities") don't have very much update logic at all. They're basically just there to create a bunch of simpler objects and plug them into each other in interesting ways.

IMHO having generic "entities" with a virtual "Update" method is evil. I rant about it being "spaghetti flow" at 44mins in this vid, moving onto flow graphs at 49 mins. 38 mins is immutable objects being good:

 

 

Ah, okay, my mistake! haha. So it seems that you build a dependency graph using your system then and then batch update the objects?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now