For testing Cute ECS I implemented a simple multi agent system, so I am not only testing my ECS, I can stress it and calibrate to the max performance.
A multi agent system defines simple agents that solve specific problems, then you run all of them at the same time, creating a lot of interesting reactions. My multi agent system will just try to simulate the relation between hunters, prey and vegetation, so it will create a simulated ecosystem.
Description of the multi agent system:
We have three type of agents:
- Grass. Green circles, they will grow until they touch another grass.
- Gazelle. White circles, they will eat the closest grass around them, trying to avoid the hunters.
- Lion. Yellow Triangles, they will try to eat gazelles, they have a recharge energy, so they can spring during a short time to catch a gazelle.
Each agent will have some identity values (DNA?) that will differentiate to other agents:
- Grass, each one will have different growing speeds.
- Gazelle, different speeds (linear or angular), vision range and some of them will prefer bigger Grass than closer Grass.
- Lions, different speeds, targeting angles and recharge time.
The size of the agent represent its life, when they move around the get smaller because they consume life and when they eat they get bigger. Once they reach a top size, they will duplicate their identity values and divide in new three agents. So successful agents will propagate their identity values.
During the execution of the system we can change some of the parameters of the simulation, so we can see what happens when we spawn a lot more lions or when we stop to spawn grass.
Implementation details:
- It will use the ECS and the job system.
- ECS supports accessing, creating and deleting from different threads.
- The job system in Cute has a lock-free stealing queue for each worker.
- Each agent will have common components, like EntitySize, PositionComponent and SpeedComponent.
- The world is a 2D box and it will be divided in 24x24 zones.
- The frame is divided in three steps:
- Agents update: Where the agents will update their behaviours.
- Agents movement: Where the agents will apply the new speed to the positions and move zones.
- Agents rendering: Where a frustum culling will happen, an instance buffer for each type of agent will be created and the render commands will get send to the renderer.
- Each step will generate a lot of jobs that will then get distributed to all the workers threads, each job will not share the same cache lines and they will get sync to the correct fence.
- Each update will use the ECS as much as possible, for example when a lion is looking for gazelles it can calculate the zones against its query, then it can iterate looking for gazelles only in that zones.
- Gazelles and lions collide between them and they try to keep some distance.
- We calculate all iterations in all the frames, so each lion looks around for all the gazelles in each frame, each gazelle selects what grass is its target each frame. We could reduce the frequency of a lot of the logic, so we could split the cost between different frames, but I wanted to stress the ECS as much as possible.
- There are still some iterations that need some threading synchronisation, for example what happens if more than one gazelle is eating a grass. For these situations we use atomics.
Result:
This simulation has more than 10k agents running at more of 150fps in my modest CPU, AMD Ryzen 2400G.
Microprofiler (https://github.com/jonasmr/microprofile) is integrated in Cute for profiling, these are few common frames.
No comments:
Post a Comment